I am tryng to extract all items from a ContextMenuStrip to a List(Of ToolStripMenuItem)
.
I can get the reference of the ContextMenuStrip of a DataGridView, then - with a recursive function - I want to extract the Name
and Text
properties of all the ToolStripMenuItems
, excluding the ToolStripSeparators
, if any.
I use this code:
allItem = New List(Of Control)
Dim lstDbg As List(Of Control) = FindControlRecursive(allItem, form, GetType(DataGridView))
Dim dictRes As New Dictionary(Of String, String)
For Each dbgCtrl As Control In lstDbg
If dbgCtrl.Name <> "" Then
For Each mnuStrip As ToolStripItem In dbgCtrl.ContextMenuStrip.Items
If mnuStrip.GetType() = GetType(ToolStripMenuItem) Then
Dim lstItem As New List(Of ToolStripMenuItem)
Dim lstTS As List(Of ToolStripMenuItem) = FindControlRecursive_ContextMenu(lstItem, mnuStrip, GetType(ToolStripMenuItem))
For Each item As ToolStripMenuItem In lstTS
dictRes.Add(item.Name, item.Text)
Next
End If
Next
End If
Next
The function of recursion is FindControlRecursive_ContextMenu()
:
Public Function FindControlRecursive_ContextMenu(ByVal list As List(Of ToolStripMenuItem), ByVal parent As ToolStripMenuItem, ByVal ctrlType As System.Type) As List(Of ToolStripMenuItem)
If parent Is Nothing Then Return list
If parent.GetType Is ctrlType Then
list.Add(parent)
End If
For Each child As ToolStripMenuItem In parent.DropDown.Items
FindControlRecursive_ContextMenu(list, child, ctrlType)
Next
Return list
End Function
It works, but if in the DropDown list there is a ToolStripSeparator
, I don't have the DropDown.Items
element and the function generates an exception.
How can I skip the ToolStripSeparators and call the recursive function with the next "child"?
Since only sub-items of type ToolStripMenuItem are needed, you can simply filter the ContextMenuStrip.Items collection beforehand, collecting only Item of that specific type. The Collection.OfType([Type]) method is commonly used for this. OfType()
only returns Items of the type specified: ToolStripSeparator is not of that Type, so it won't be included in the filtered collection (nor will other components types such as ToolStripTextBox, ToolStripComboBox etc.).
The recursive function can be an iterator which returns an IEnumerable(Of ToolStripMenuItem)
that yields each item found in a for each
loop.
Call the main method as, e.g.,:
► Set Option Strict
, Option Explicit
and Option Infer
to On
Dim cmsItems As Dictionary(Of String, String) = GetAllContextMenuItems(contextMenuStrip1)
Imports System.Linq
Private Function GetAllContextMenuItems(contextMenu As ContextMenuStrip) As Dictionary(Of String, String)
If contextMenu Is Nothing OrElse (Not contextMenu.HasChildren) Then Return Nothing
Dim dict = New Dictionary(Of String, String)
For Each item As ToolStripMenuItem In contextMenu.Items.OfType(Of ToolStripMenuItem)
dict.Add(item.Name, item.Text)
For Each subItem In GetSubMenuItems(item)
dict.Add(subItem.Name, subItem.Text)
Next
Next
Return dict
End Function
Private Iterator Function GetSubMenuItems(parent As ToolStripMenuItem) As IEnumerable(Of ToolStripMenuItem)
For Each item As ToolStripMenuItem In parent.DropDownItems.OfType(Of ToolStripMenuItem)
If item.HasDropDownItems Then
For Each subItem As ToolStripMenuItem In GetSubMenuItems(item)
Yield subItem
Next
Else
Yield item
End If
Next
End Function