Public Class frmContextMenuExample
[COLOR="seagreen"]'these need copy/pasting too
'these key list must be in uppercase
'for every item on your menu put the keys in the relevant list
'i.e. if you checkbox is shortcutted by Ctrl+G put a G in the Ctrl key list
'if youre thinking of using numbers.. dont! things get messy when numpad
'numbers are used. stick to letters![/COLOR]
Private ctxMenuChkBoxInlineShorts As String = "CHE"
Private ctxMenuChkBoxCtrlKeyShorts As String = "QWE"
Private ctxMenuChkBoxAltKeyShorts As String = ""
Private ctxMenuChkBoxShiftKeyShorts As String = ""
Private ctxMenuChkBoxShortKeyPressed As Boolean = False
Private ctxMenuClosingEvtMustCancel As Boolean = False
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
RegisterMenuCheckbox(Check1ToolStripMenuItem, ContextMenuStrip1)
RegisterMenuCheckbox(Check2ToolStripMenuItem, ContextMenuStrip1)
RegisterMenuCheckbox(Check3ToolStripMenuItem, ContextMenuStrip1)
End Sub
Private Sub TextBoxToolStripMenuItem_KeyPress _
(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBoxToolStripMenuItem.KeyPress
[COLOR=seagreen]'here's how we make a menu go away and do something when we have a textbox and press return[/COLOR]
If e.KeyChar = Convert.ToChar(Keys.Return) Then
DirectCast(sender, ToolStripTextBox).PerformClick() 'close the context menu
MessageBox.Show("Running search")
End If
End Sub
Private Sub Form1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Click
[COLOR="seagreen"]'heres how we show a context menu at the point of the mouse[/COLOR]
If DirectCast(e, MouseEventArgs).Button = Windows.Forms.MouseButtons.Right Then
ContextMenuStrip1.Show(Me, DirectCast(e, MouseEventArgs).Location)
End If
End Sub
[COLOR="seagreen"] '!!!!!!!!everything after this line needs copy/paste into another form if you have a contextmenu to affect
'as described[/COLOR]
Private Sub chkBoxMenuItemPaint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs)
[COLOR="seagreen"] 'checkbox items to which this handler is attached should not close the menu.
'one of the ways we can tell if such an item is being interacted with is when
'it gets painted - the menu items typically changed colour when being interacted
'with and paint is fired as a result. at this time we can see if the item is
'selected - the highlight that occurs when you roll the mouse over or use the
'cursor keys to select it. there is a problem in that when we press a shortut key
'i.e. the menu item text is &MenuItem and we press M - the problem is that the item
'doesnt actually become selected but it experiences a paint. in this case, pressing
'a key clicks our item and the menu closes. when we find a key has been pressed we
'indicate this with a boolean so that when the paint occurs, whether the item is
'selected or whether the keypress happened. [/COLOR][COLOR="seagreen"]ctxMenuChkBoxKeyShortPressed will be
'switched to false by the itemclick event that will be happening soon[/COLOR]
ctxMenuClosingEvtMustCancel = DirectCast(sender, ToolStripMenuItem).Selected Or ctxMenuChkBoxShortKeyPressed
End Sub
Private Sub contextMenuStripClosing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.ToolStripDropDownClosingEventArgs)
[COLOR="seagreen"] 'how did the menu come to want to close? if it was an itemclick such as click
'or highlight+return or programmatic PerformClick() then we should check if it
'needs cancelling
'if we could detect here, which item it was that was clicked, our life would be
'simple, but this event occurs before the ItemClick and sender is the context menu
'itself so holds no clues :( [/COLOR]
If e.CloseReason = ToolStripDropDownCloseReason.ItemClicked Then
e.Cancel = ctxMenuClosingEvtMustCancel
End If
[COLOR="SeaGreen"]'make doubly sure we set to false here - if we dont do this then theoretically it can
'become set to true somehow, the user clicks outside the menu, the menu goes away but
'mustcancel remains true.. so the next time the menu is shown, it wont go away first time!
'its also a catch-all: if the menu tries to close and there's an error in our logic
'that we havent catered for, then the mustcancel will become false so the user can click
'again and the menu will definitely go[/COLOR]
ctxMenuClosingEvtMustCancel = False
End Sub
Private Sub chkBoxMenuItemClick(ByVal sender As System.Object, ByVal e As System.EventArgs)
[COLOR="seagreen"]'this toggles the state of the checkbox[/COLOR]
DirectCast(sender, ToolStripMenuItem).Checked = Not DirectCast(sender, ToolStripMenuItem).Checked
[COLOR="seagreen"]'if the statetoggle was arrived by by keypress then switch off the keypress
'this avoids the final problem i found whereby if the most recent key the user
'pressed was a shortkey, then mustcancel would remain true, so the menu would remain
'when the next item was clicked. it also good practice because it makes the menuitems
'that are click-but-dont-hide-menu responsible for cleaning up "their own mess"[/COLOR]
If ctxMenuChkBoxShortKeyPressed Then ctxMenuChkBoxShortKeyPressed = False
End Sub
[COLOR="seagreen"]'the previewkeydown event doesnt fire if we are typing into a textbox on the menu. this is useful[/COLOR]
Private Sub contextMenuStripPreviewKeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PreviewKeyDownEventArgs)
[COLOR="seagreen"] 'we code in at the top, which of our context menu items have shortcut key presses in
'underlined style, which are ctrl or shift or altkey activated. it has a bug in that
'if you make a menu item fire from TWO control keys (Ctrl+Alt+A for example) then it
'will assume set shortkeypressed to true needlessly. of course, you can adjust the logic
'yourself, or just stick to not combining control keys[/COLOR]
If ((Not (e.Alt Or e.Shift Or e.Control)) AndAlso ctxMenuChkBoxInlineShorts Like String.Format("*{0}*", Convert.ToChar(e.KeyCode))) OrElse _
(e.Alt AndAlso ctxMenuChkBoxAltKeyShorts Like String.Format("*{0}*", Convert.ToChar(e.KeyCode))) OrElse _
(e.Shift AndAlso ctxMenuChkBoxShiftKeyShorts Like String.Format("*{0}*", Convert.ToChar(e.KeyCode))) OrElse _
(e.Control AndAlso ctxMenuChkBoxCtrlKeyShorts Like String.Format("*{0}*", Convert.ToChar(e.KeyCode))) Then
[COLOR="seagreen"]'one of our watched keys was pressed[/COLOR]
ctxMenuChkBoxShortKeyPressed = True
End If
End Sub
Private Sub RegisterMenuCheckbox(ByVal tsmi As ToolStripMenuItem, ByVal onMenu As ContextMenuStrip)
'handle paint events
AddHandler tsmi.Paint, AddressOf chkBoxMenuItemPaint
AddHandler tsmi.Click, AddressOf chkBoxMenuItemClick
[COLOR="seagreen"]'we remove first to ensure only one menu handler for this event. if add is called multiple times
'then the subs run multiple times, which destroys the logic[/COLOR]
RemoveHandler onMenu.PreviewKeyDown, AddressOf contextMenuStripPreviewKeyDown
AddHandler onMenu.PreviewKeyDown, AddressOf contextMenuStripPreviewKeyDown
RemoveHandler onMenu.Closing, AddressOf contextMenuStripClosing
AddHandler onMenu.Closing, AddressOf contextMenuStripClosing
End Sub
End Class