ContextMenuStrip
control identification
.NET development
event handling
user interface design

Determine what control the ContextMenuStrip was used on

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When one ContextMenuStrip is shared across several WinForms controls, the handler needs to know where the menu came from before it can apply the right action. The normal answer is ContextMenuStrip.SourceControl, and if you need the exact row, node, or item under the pointer, you combine that with control-specific hit testing.

Use SourceControl for the control-level answer

WinForms records the control that opened the menu on the menu itself. That means both the Opening event and the item-click handlers can query the same property.

csharp
1private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
2{
3    var menu = (ContextMenuStrip)sender;
4    Control source = menu.SourceControl;
5
6    if (source == null)
7    {
8        e.Cancel = true;
9        return;
10    }
11
12    renameToolStripMenuItem.Visible = source is TextBox;
13    deleteToolStripMenuItem.Visible = source is ListView;
14}

This is the standard solution when the question is simply "which control opened the menu?" It lets one shared menu adapt its visible commands without duplicating the menu for every control.

Use the same property in command handlers

When a menu item is clicked, the sender is the menu item, not the original control. That is why trying to infer the source from the click handler's sender usually leads to the wrong object.

Instead, read the source back from the shared ContextMenuStrip instance:

csharp
1private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
2{
3    Control source = contextMenuStrip1.SourceControl;
4
5    if (source is ListView listView)
6    {
7        foreach (ListViewItem item in listView.SelectedItems)
8        {
9            listView.Items.Remove(item);
10        }
11    }
12    else if (source is TextBox textBox)
13    {
14        textBox.Clear();
15    }
16}

That keeps the logic centralized. One menu can serve several controls while still doing control-specific work safely.

Sometimes you need the item, not just the control

SourceControl tells you which control displayed the menu, but sometimes the real target is a child element inside that control. A TreeView command may depend on the node under the pointer. A ListView command may depend on the clicked row rather than the overall control.

In those cases, use SourceControl first and then apply the control's own hit-test API.

csharp
1private TreeNode? _clickedNode;
2
3private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
4{
5    _clickedNode = null;
6
7    if (contextMenuStrip1.SourceControl is TreeView tree)
8    {
9        Point localPoint = tree.PointToClient(Control.MousePosition);
10        _clickedNode = tree.GetNodeAt(localPoint);
11        e.Cancel = _clickedNode == null;
12    }
13}

That pattern is especially useful for TreeView, ListView, and DataGridView, where the menu action depends on a specific item rather than just the hosting control.

Handle keyboard invocation as well as right-clicks

Context menus are not always opened with a mouse. Users can invoke them from the keyboard, in which case Control.MousePosition may not identify the intended item. For keyboard-driven menus, the focused or selected item is often the real target.

A robust policy is:

  • use SourceControl to identify the control
  • use hit testing for mouse-driven invocation
  • use the control's selected or focused item for keyboard-driven invocation

That makes the behavior consistent for both mouse users and keyboard users.

Keep the shared-menu design centralized

Shared context menus are worth using when most commands are common across controls. The clean structure is:

  • 'Opening decides which commands should be visible or enabled'
  • click handlers read SourceControl and apply the action

That is usually easier to maintain than keeping several nearly identical ContextMenuStrip instances synchronized over time.

Common Pitfalls

  • Treating the menu-item click sender as if it were the control that opened the context menu.
  • Using SourceControl when the real problem is identifying a specific node, row, or item inside the control.
  • Assuming the menu is always mouse-driven and ignoring keyboard invocation.
  • Forgetting to cancel the menu when no valid target item exists.
  • Duplicating multiple near-identical context menus instead of sharing one and branching on SourceControl.

Summary

  • 'ContextMenuStrip.SourceControl is the standard way to identify which control opened a shared context menu.'
  • Read it in Opening and item-click handlers rather than relying on the event sender.
  • If the action depends on a row or node, combine SourceControl with control-specific hit testing.
  • Account for keyboard invocation as well as mouse invocation.
  • A shared menu stays maintainable when the source-detection logic is centralized.

Course illustration
Course illustration

All Rights Reserved.