Dan
Dan

Reputation: 149

ContextMenuStrip Requires Two Right Clicks to Display

I like to create my contextmenu's programmatically. I generally do not add the items to the contextmenustrip until it is opening as the items that get displayed are dependent on other aspects of the design that are variable.

I have found that the contextmenustrips seem to require two right clicks to display. I've tried adding the menu items in different events (opening, opened, etc) and also manually setting the contextmenustrip's visibility to true to no avail.

I can't for the life of me figure out why two right clicks are necessary. If you create a blank winforms project and then replace all the code with this, it'll demonstrate the issue.


Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        Dim currContextMenuStrip As New ContextMenuStrip
        Me.ContextMenuStrip = currContextMenuStrip
        AddHandler currContextMenuStrip.Opening, AddressOf ContextMenuStrip1_Opening
    End Sub

    Private Sub ContextMenuStrip1_Opening(sender As Object, e As CancelEventArgs)
        Dim currContextMenuStrip As ContextMenuStrip = sender
        Dim menuTxt As String = "&Find"
        'only add the menu if it doesn't already exist
        If (From f In currContextMenuStrip.Items Where f.text = menuTxt).Count = 0 Then
            Dim newMenuItem As New ToolStripMenuItem
            newMenuItem.Text = menuTxt
            currContextMenuStrip.Items.Add(newMenuItem)
        End If
    End Sub
End Class

EDIT: Just figured out it seems to be connected to the fact that the contextmenustrip doesn't have any items on the first right click. If I add a dummy item, then hide it once other items are added, it works on the first right click. So confused!

This works:

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        Dim currContextMenuStrip As New ContextMenuStrip
        Me.ContextMenuStrip = currContextMenuStrip
        AddHandler currContextMenuStrip.Opening, AddressOf ContextMenuStrip1_Opening

        'add a dummy item
        Dim newMenuItem As New ToolStripMenuItem
        newMenuItem.Text = "dummy"
        currContextMenuStrip.Items.Add(newMenuItem)
    End Sub

    Private Sub ContextMenuStrip1_Opening(sender As Object, e As CancelEventArgs)
        Dim currContextMenuStrip As ContextMenuStrip = sender
        Dim menuTxt As String = "&Find"
        'only add the menu if it doesn't already exist
        If (From f In currContextMenuStrip.Items Where f.text = menuTxt).Count = 0 Then
            Dim newMenuItem As New ToolStripMenuItem
            newMenuItem.Text = menuTxt
            currContextMenuStrip.Items.Add(newMenuItem)
        End If

        'hide the dummy item
        Dim items As List(Of ToolStripMenuItem) = (From f As ToolStripMenuItem In currContextMenuStrip.Items Where f.Text = "dummy").ToList
        items.First.visible = False
    End Sub
End Class

Upvotes: 0

Views: 734

Answers (3)

Daniel LB
Daniel LB

Reputation: 47

Microsoft has old style and new style context menus. The Popup event was used for the old style context menus and it received a plain EventArgs object. The new context menus use the Opening event which receives a CancelEventArgs object. If currContextMenuStrip.Items doesn't contain any items, e.Cancel will be set to True when the event handler is called (which caused the problem you encountered). The fix is to add the menu items and then set e.Cancel to False. It should display fine after that. To make sure items were actually added, the assignment of e.Cancel can be guarded with an if statement as follows:

If currContextMenuStrip.Items.Count <> 0 Then
   e.Cancel = False
End If

Upvotes: 1

Dan
Dan

Reputation: 149

Thanks for all the help and suggestions! I ultimately decided to build the superset of menus in the Designer and then just show/hide at run time. That's probably faster on each right click then rebuilding the menu each time.

Upvotes: 1

jmcilhinney
jmcilhinney

Reputation: 54477

If you really need to do things this way, one option is to create your own custom ContextMenuStrip that accounts for the behaviour when there are no items and the requirement for a dummy item. I used this code:

Imports System.ComponentModel

Public Class Form1

    Private WithEvents menu As New ContextMenuStrip

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ContextMenuStrip = menu
    End Sub

    Private Sub menu_Opening(sender As Object, e As CancelEventArgs) Handles menu.Opening
        If menu.Items.Count = 0 Then
            menu.Items.AddRange({New ToolStripMenuItem("First"),
                                 New ToolStripMenuItem("Second"),
                                 New ToolStripMenuItem("Third")})
        End If
    End Sub

    Private Sub menu_ItemClicked(sender As Object, e As ToolStripItemClickedEventArgs) Handles menu.ItemClicked
        MessageBox.Show(e.ClickedItem.Text)
    End Sub

End Class

and saw the same behaviour you describe. I then defined this class:

Public Class ContextMenuStripEx
    Inherits ContextMenuStrip

    Private dummyItem As ToolStripItem

    Public ReadOnly Property IsInitialised As Boolean
        Get
            Return dummyItem Is Nothing
        End Get
    End Property

    Public Sub New()
        dummyItem = Items.Add(CStr(Nothing))
    End Sub

    ''' <inheritdoc />
    Protected Overrides Sub OnItemAdded(e As ToolStripItemEventArgs)
        If Not IsInitialised Then
            Items.Remove(dummyItem)
            dummyItem = Nothing
        End If

        MyBase.OnItemAdded(e)
    End Sub

End Class

and changed my form code to this:

Imports System.ComponentModel

Public Class Form1

    Private WithEvents menu As New ContextMenuStripEx

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ContextMenuStrip = menu
    End Sub

    Private Sub menu_Opening(sender As Object, e As CancelEventArgs) Handles menu.Opening
        If Not menu.IsInitialised Then
            menu.Items.AddRange({New ToolStripMenuItem("First"),
                                 New ToolStripMenuItem("Second"),
                                 New ToolStripMenuItem("Third")})
        End If
    End Sub

    Private Sub menu_ItemClicked(sender As Object, e As ToolStripItemClickedEventArgs) Handles menu.ItemClicked
        MessageBox.Show(e.ClickedItem.Text)
    End Sub

End Class

and it worked as desired. Note that the last code snippet uses the custom type and its custom property.

Upvotes: 2

Related Questions