Caius Jard
Caius Jard

Reputation: 74605

Find control that caused ContextMenuStrip menu to be shown

I've read a few articles on SO:

How to detrmine the control that cause ContextMenuStrip Getting the control of a context menu

and a couple others that suggested use of the SourceControl property.. but none work in this context:

I have a ContextMenuStrip that has a child ToolStripMenuItem - this code from the windows forms designer generated section:

        // _tileContextMenuStrip
        // 
        this._tileContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
        this.tileKindToolStripMenuItem,
        this.forceWidthScalingToolStripMenuItem,
        this.forceHeightScalingToolStripMenuItem});
        this._tileContextMenuStrip.Name = "_tileContextMenuStrip";
        this._tileContextMenuStrip.Size = new System.Drawing.Size(184, 70);
        // 
        // tileKindToolStripMenuItem
        // 
        this.tileKindToolStripMenuItem.Name = "tileKindToolStripMenuItem";
        this.tileKindToolStripMenuItem.Size = new System.Drawing.Size(183, 22);
        this.tileKindToolStripMenuItem.Text = "Tile Kind";

So the context menu strip and the menu item first in the list are fixed at design time. At runtime, the TSMI has child TSMIs added to it in a loop based on an enum:

        foreach(TileKind t in typeof(TileKind).GetEnumValues()) {

            ToolStripMenuItem tsmi = new ToolStripMenuItem(t.ToString("g"));
            tsmi.Tag = t;
            tsmi.Click += tsmi_Click; 

            tileKindToolStripMenuItem.DropDownItems.Add(tsmi);
        }

Later I have 20 checkboxes on my form and I set their .ContextMenuStrip to be the same thing:

foreach(Thing t in someDataSource){
  CheckBox c = new CheckBox();
  c.Text = t.SomeData;
  c.ContextMenuStrip = this._tileContextMenuStrip;
  myPanelBlah.Controls.Add(c);
}

Great, so now I have all my checkboxes and they all show the context menu when I right click them, but when I choose one the sub-menu items, I just can't find out the control that fired the context menu...

    //this the click handler for all the menu items dynamically added
    void tsmi_Click(object sender, EventArgs e)
    {
        ToolStripMenuItem tsmi = sender as ToolStripMenuItem;
        (tsmi.OwnerItem                   //the parent node in the menu tree hierarchy
            .Owner as ContextMenuStrip)   //it's a ContextMenuStrip so the cast succeeds
            .SourceControl                //it's always null :(
    }

I can reliably get ahold of the contextmenustrip either by routing up from the event handler sender, or even just by referencing the ContextMenuStrip itself as a form instance variable, but SourceControl is always null

Any ideas what to try next?

Upvotes: 1

Views: 340

Answers (1)

Hans Passant
Hans Passant

Reputation: 941457

I see the problem, quacks loudly like a bug. There's a workaround, you can subscribe the ContextMenuStrip's Opening event. At that point, well before you start navigating into the sub-items, the SourceControl property is still valid. So store it in a field of the class so you'll have it available in the Click event handler. Roughly:

private Control _tileCmsSource;

private void _tileContextMenuStrip_Opening(object sender, CancelEventArgs e) {
    _tileCmsSource = _tileContextMenuStrip.SourceControl;
}

void tsmi_Click(object sender, EventArgs e)
{
    ToolStripMenuItem tsmi = sender as ToolStripMenuItem;
    // Use _tileCmsSource here
    //...
}

Upvotes: 1

Related Questions