Determine what control the ContextMenuStrip was used on
For a ContextMenu
:
The problem is that the sender
parameter points to the item on the context menu that was clicked, not the context menu itself.
It's a simple fix, though, because each MenuItem
exposes a GetContextMenu
method that will tell you which ContextMenu
contains that menu item.
Change your code to the following:
private void MenuViewDetails_Click(object sender, EventArgs e)
{
// Try to cast the sender to a MenuItem
MenuItem menuItem = sender as MenuItem;
if (menuItem != null)
{
// Retrieve the ContextMenu that contains this MenuItem
ContextMenu menu = menuItem.GetContextMenu();
// Get the control that is displaying this context menu
Control sourceControl = menu.SourceControl;
}
}
For a ContextMenuStrip
:
It does change things slightly if you use a ContextMenuStrip
instead of a ContextMenu
. The two controls are not related to one another, and an instance of one cannot be casted to an instance of the other.
As before, the item that was clicked is still returned in the sender
parameter, so you will have to determine the ContextMenuStrip
that owns this individual menu item. You do that with the Owner
property. Finally, you'll use the SourceControl
property to determine which control is displaying the context menu.
Modify your code like so:
private void MenuViewDetails_Click(object sender, EventArgs e)
{
// Try to cast the sender to a ToolStripItem
ToolStripItem menuItem = sender as ToolStripItem;
if (menuItem != null)
{
// Retrieve the ContextMenuStrip that owns this ToolStripItem
ContextMenuStrip owner = menuItem.Owner as ContextMenuStrip;
if (owner != null)
{
// Get the control that is displaying this context menu
Control sourceControl = owner.SourceControl;
}
}
}
How do you get the control that was clicked to open a ContextMenuStrip?
Private Sub mnuWebCopy_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuWebCopy.Click
Dim myItem As ToolStripMenuItem = CType(sender, ToolStripMenuItem)
Dim cms As ContextMenuStrip = CType(myItem.Owner, ContextMenuStrip)
MessageBox.Show(cms.SourceControl.Name)
End Sub
Determine control on which context menu was opened
Contextmenu has a SourceControl property which you should be able to use :)
VB.Net Get The Control That Is Used To Show The Contextmenu Strip
If you check this C# thread the accepted answer notes it is a bug. The workaround presented there uses a private variable to store the SourceControl
on the Opening
event of the ContextMenuStrip
. I've converted to VB.NET and used the Tag
of the ContextMenuStrip
instead of using the variable. You then refer to the Tag
property instead of the faulty SourceControl
property:
Imports System.ComponentModel
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.TextBox1.ContextMenuStrip = Me.ContextMenuStrip1
Me.TextBox2.ContextMenuStrip = Me.ContextMenuStrip1
End Sub
Private Sub ContextMenuStrip1_Opening(sender As Object, e As CancelEventArgs) Handles ContextMenuStrip1.Opening
Me.ContextMenuStrip1.Tag = CType(Me.ContextMenuStrip1.SourceControl, Control)
End Sub
Private Sub TestToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles TestToolStripMenuItem.Click
' first level of context menu strip
Dim Strip As ContextMenuStrip = CType(sender, ToolStripMenuItem).Owner
Dim Box As TextBox = Strip.Tag
MessageBox.Show(Box.Name)
End Sub
Private Sub ChildToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ChildToolStripMenuItem.Click
' second level of context menu strip
Dim Strip As ContextMenuStrip = CType(sender, ToolStripMenuItem).OwnerItem.Owner
Dim Box As TextBox = Strip.Tag
MessageBox.Show(Box.Name)
End Sub
End Class
How to determine which control opens ContextMenuStrip
sender is going to be the context menu strip
the source control property will retrieve the owner instance
Private Sub mycontextmenu_Opening(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles mycontextmenu.Opening
If mycontextmenu.SourceControl is MenuStrip1 Then
ExitToolStripMenuItem.Visible = False
Else
ExitToolStripMenuItem.Visible = True
End If
End Sub
Detect if a ContextMenuStrip is displayed or intercepting key events
Well after walking away from this issue for a while and working on something else, I discovered a moderately easy WinAPI approach to solving this problem that plays nicely with C#.
The magic here is the SetWinEventHook
function from WinAPI:
// WINAPI Declarations
const uint EVENT_SYSTEM_MENUSTART = 0x0004;
const uint EVENT_SYSTEM_MENUEND = 0x0005;
const uint EVENT_SYSTEM_MENUPOPUPSTART = 0x0006;
const uint EVENT_SYSTEM_MENUPOPUPEND = 0x0007;
const uint WINEVENT_OUTOFCONTEXT = 0x0000
delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll")]
static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll")]
static extern bool UnhookWinEvent(IntPtr hWinEventHook);
// Sample Usage - This version will subscribe to menu events on all processes and all threads:
void StartMonitoringMenus()
{
// The lifetime of these two objects must be the same - we must not let the delegate
// get GC'd before calling UnhookWinEvent at the risk of crashing other processes.
// This sample assumes class fields for these, but could also be static variables
_menuEventCallback = new WinEventDelegate(MenuEventCallback);
_menuEventHook = SetWinEventHook(
EVENT_SYSTEM_MENUSTART,
EVENT_SYSTEM_MENUPOPUPEND,
IntPtr.Zero,
_menuEventCallback,
0,
0,
WINEVENT_OUTOFCONTEXT);
}
void StopMonitoringMenus()
{
// Cleanup Logic
UnhookWinEvent(_menuEventHook);
_menuEventCallback = null;
}
void MenuEventCallback(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
// Do something here like clear focus on poorly behaving hosted native controls
}
The particular sequence of events which get raised depends on the type of menu being displayed.
For MenuStrip controls:
- MENUSTART
- MENUPOPUPSTART
- MENUPOPUPEND
- ... MENUPOPUPSTART - as the user moves from menu to menu without making a selection
- ... MENUPOPUPEND
- MENUEND
For ContextMenuStrips:
- MENUPOPUPSTART
- MENUPOPUPEND
Related Topics
Wpf Binding a Listbox to an Enum, Displaying the Description Attribute
Programmatically Set Browser Proxy Settings in C#
How to Capture a 0..1 to 0..1 Relationship in Entity Framework
How to Select Text from the Richtextbox and Then Color It
Admin Rights for a Single Method
Custom Header to Httpclient Request
How to Use JSON.Net for JSON Modelbinding in an MVC5 Project
How to Get and Set the Window Position of Another Application in C#
How to Disable a Tab Inside a Tabcontrol
Problem Parsing Currency Text to Decimal Type
Zoom and Translate an Image from the Mouse Location
How to Get Current Page Url in MVC 3