WinForms FAQ - Menus

Find answers for the most frequently asked questions
Expand All Collapse All

Normally, the context menu will be shown at the center of the control when the keyboard is used to invoke the context menu of a control (Shift+F10, for example). You can customize the location as follows.

  1. Override WndProc in the grid and check if the Message.Msg is
    WM_CONTEXTMENU (0x007b)
  2. If so, check if the Message.LParam is -1, which means this was due to a
    keyboard message as opposed to the user right-clicking the mouse.
  3. Now, call the associated ContextMenu’s Show method explicitly at the
    selected row position.
  4. Make sure NOT to call the base class after showing the menu explicitly.

protected override void WndProc(ref Message m)
{
	if(m.Msg == 0x007B /*WM_CONTEXTMENU*/)
	{
		// LParam == -1 means that this is due to a keyboard message
		if((int)m.LParam == -1)
		{
			this.contextMenu.Show();
			return;
		}
	}
}

[VB.Net]
Protected Overrides  Sub WndProc(ByRef m As Message)
	If m.Msg = 0x007B Then ’WM_CONTEXTMENU
		’ LParam == -1 means that this is due to a keyboard message
		If (CType(m.LParam,Integer)) = -1 Then
			Me.contextMenu.Show()
			Return
		End If
	End If
End Sub
Permalink

When you assign Ctrl1, Ctrl2 etc as shortcuts for menuitems they show up as Ctrl+D1, Ctrl+D2. This can be worked around by creating and adding the menuitem through code as demonstrated below:


[C#]
//Create the menuitem 
MenuItem mymenuItem =  new MenuItem();
//ShortCut
mymenuItem.Shortcut = System.Windows.Forms.Shortcut.Ctrl1;
//Add Event Handler for the menuitem
mymenuItem.Click +=new EventHandler(this.mymenuItem_Click);
//ShortCut Text to be displayed
mymenuItem.Text = 'My MenuItem' +'\t'+ 'Ctrl+1';
//hide shortcut
mymenuItem.ShowShortcut = false;
//Add it to the bottom of the first menu
this.mainMenu1.MenuItems[0].MenuItems.Add(mymenuItem);

[VB.NET]
’Create the menuitem 
Dim mymenuItem As MenuItem =  New MenuItem() 
’ShortCut
mymenuItem.Shortcut = System.Windows.Forms.Shortcut.Ctrl1
’Add Event Handler for the menuitem
mymenuItem.Click +=New EventHandler(Me.mymenuItem_Click)
’ShortCut Text to be displayed
mymenuItem.Text = 'My MenuItem' +'\t'+ 'Ctrl+1'
’hide shortcut
mymenuItem.ShowShortcut = False
’Add it to the bottom of the first menu
Me.mainMenu1.MenuItems(0).MenuItems.Add(mymenuItem)
Permalink

To customize the shortcut (which are not present in the System.Windows.Forms.Shortcut Enum), you need to override the ProcessCmdKey event of the form and handle the needed keys. The below example has a menu item with name “item” and the shortcut Ctrl + Alt + 0 has been set by handling the ProcessCmdKey event. The Click event of the menu item is simulated.


//Override this method in the form which have the menu. This will only trigger when the form is on focus.
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    if (keyData == (Keys.Control | Keys.Alt | Keys.D0)) // Ctrl + Alt + 0
    {
        if (this.Menu != null && this.Menu.MenuItems.Count > 0)
            this.Menu.MenuItems['item'].PerformClick(); // triggers click event.
    }
    return base.ProcessCmdKey(ref msg, keyData);
}

Instead of MenuItem, you can utilize ToolStripMenuItem which has plenty of features and all kind of shortcuts are available.

Permalink

Usually the underline appears only after you press the ALT Key, but you can enable it by changing the Operating System Settings. On Windows XP, Right Click Desktop to bring up the Display Properties Dialog and then choose Appearance tab and then the Effects Button and uncheck the checkbox ‘Hide Underlined letters for keyboard navigation until I press the ALT Key’.

Permalink

To automatically close the context menu after a set time interval, you can use a Timer and send a ESC key stroke after the desired time interval as shown:


[C#]
private void timer1_Tick(object sender, System.EventArgs e)
{
	SendKeys.Send('{ESC}');
	timer1.Stop();
}
private void contextMenu1_Popup(object sender, System.EventArgs e)
{
	//set interval to 5 seconds
	timer1.Interval = 5000;
	timer1.Start();
}

[VB.Net]

Private  Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs)
	SendKeys.Send('{ESC}')
	timer1.Stop()
End Sub
Private  Sub contextMenu1_Popup(ByVal sender As Object, ByVal e As System.EventArgs)
	’set interval to 5 seconds
	timer1.Interval = 5000
	timer1.Start()
End Sub
Permalink

First keep track of which control is showing the ContextMenu by listening to the menu’s Popup event and querying for the SourceControl.

Then when you are ready to cancel the popup, do as follows:


[C#]
		[DllImport('user32.dll', CharSet=CharSet.Auto)]
		extern internal static IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

		private void Timer_Tick(object sender, EventArgs e)
		{
			if(menuSourceControl != null)
				SendMessage(menuSourceControl.Handle, 0x001F/*WM_CANCELMODE*/, IntPtr.Zero, IntPtr.Zero);

		}

[VB.Net]
		 _ 
		extern internal static IntPtr SendMessage(IntPtr hWnd, Integer msg, IntPtr wParam, IntPtr lParam)
 
		Private  Sub Timer_Tick(ByVal sender As Object, ByVal e As EventArgs)
			If Not menuSourceControl Is Nothing Then
				SendMessage(menuSourceControl.Handle, 0x001F, IntPtr.Zero, IntPtr.Zero)
			End If
 
		End Sub

Permalink

Override WndProc in your Control and do the following. You should then listen to keyup and show the context menu yourself.


[C#]
protected override void WndProc(ref Message m)
{
	if(m.Msg == 0x7b /*WM_CONTEXTMENU*/ )
	{
		return;
	}
	if(m.Msg == 0x101 /*WM_KEYUP*/)
	{
		Keys keys = (Keys)m.WParam.ToInt32();
		// Prevent this key from being processed.
		if(keys == Keys.Apps)
			return;
	}
	base.WndProc(ref m);
}

[VB.Net]
Protected Overrides  Sub WndProc(ByRef m As Message)
	If m.Msg = 0x7b Then ’WM_CONTEXTMENU
		Return
	End If
	If m.Msg = 0x101 Then ’WM_KEYUP
		Dim keys As Keys = CType(m.WParam.ToInt32(), Keys)
		’ Prevent this key from being processed.
		If keys = Keys.Apps Then
			Return
		End If
	End If
	MyBase.WndProc( m)
End Sub
Permalink

The focused control’s ProcessCmdKey will be called first.

1) The Control class implementation of this method will check if there is a ContextMenu associated with the Control and if so let the context menu handle the shortcut.

2) If not handled, the ProcessCmdKey of the parent will be called recursively until the Form is reached.

3) When the Form is reached it’s MainMenu will be requested to handle the key.

You can override ProcessCmdKey of any Control and interrupt the normal processing. Note that you can also override the ProcessCmdKey method is the MainMenu and ContextMenu classes (this is not documented) to override default processing.

Permalink

You can listen to the Popup event, determine where the mouse was clicked and selectively make the menu items visible in the menu as follows:


		// In C#
		private void contextMenu1_Popup(object sender, System.EventArgs e)
		{
			// Get current mouse click position in the control (assuming pictureBox1 is the control):
			Point ptClick = this.pictureBox1.PointToClient(Control.MousePosition);
			// Get the rectangle where you want to show the context menu.
			Rectangle preferredClickRect = new Rectangle(0, 0, 50, 50);
			if(preferredClickRect.Contains(ptClick))
			{
				// Show all the menu items so that the menu will appear
				foreach(MenuItem item in this.contextMenu1.MenuItems)
					item.Visible = true;
			}
			else
			{
				// Hide all the menu items so that the menu will not appear
				foreach(MenuItem item in this.contextMenu1.MenuItems)
					item.Visible = false;
			}
		}

		’ In VB.Net
		Private  Sub contextMenu1_Popup(ByVal sender As Object, ByVal e As System.EventArgs)
			’ Get current mouse click position in the control (assuming pictureBox1 is the control):
			Dim ptClick As Point =  Me.pictureBox1.PointToClient(Control.MousePosition) 
			’ Get the rectangle where you want to show the context menu.
			Dim preferredClickRect As Rectangle =  New Rectangle(0,0,50,50) 
			If preferredClickRect.Contains(ptClick) Then
				’ Show all the menu items so that the menu will appear
				Dim item As MenuItem
				For Each item In Me.contextMenu1.MenuItems
					item.Visible = True
				Next
			Else 
				’ Hide all the menu items so that the menu will not appear
				Dim item As MenuItem
				For Each item In Me.contextMenu1.MenuItems
					item.Visible = False
				Next
			End If
		End Sub
Permalink

The frame will manage a context menu for you if you set the control’s ContextMenu property. Here is some code adding a context menu to a Label.

	//Add Menus in your form’s constructor...
	this.pictureContextMenu = new ContextMenu();
	this.pictureContextMenu.MenuItems.Add('&Color', 
			new EventHandler(Color_Clicked));
	this.pictureContextMenu.MenuItems.Add('&Font', 
			new EventHandler(Font_Clicked));
	label1.ContextMenu = this.pictureContextMenu;
	//
	// TODO: Add any constructor code after InitializeComponent call
	//
          }
	
          private void Font_Clicked(object sender, System.EventArgs e) 
          {     . . .     }

          private void Color_Clicked(object sender, System.EventArgs e) 
         {  ...       }
Permalink

Share with

Couldn't find the FAQs you're looking for?

Please submit your question and answer.