derek hatchard

aggregating from ardentdev.com and derekhat.com

Find the Name of MenuItem Controls on Your Form

At the Atlantic Canada Code Camp I was talking with a gentleman who wanted to iterate through the MenuItem controls in his form (WinForms) so he could act on specific menu items that followed a certain naming convention (I believe it was to provide English and French versions for the menu text – but it might have been so he could change certain labels to “Mystery Meat”, I don’t recall).  The scenario as I understood it was that varying subsets of standard menus would be used on many forms.  Hand coding the label lookup operation for each MenuItem on the form would be tedious and prone to human error.

Everyone’s first instinct is to loop through the Controls collection and look at the Name property of each control that is a MenuItem.  But the MenuItem class does not expose a Name property.  A proposed solution from an online forum was to inherit from MenuItem and expose a property that could be used in lieu of Name (like a Tag property – the MenuItem class is a grumpy old miser so it does not have one of those either).  Of course the inheritance approach would require everyone on your team to use the derived control and manually set a special property for every menu.  Yucky.  Not to mention that the true Name of the control might be different from the “name” someone provides.

Hopefully everyone on the team is already giving their menu items a meaningful name:  mnuEditCut instead of MenuItem16.  If you can get everyone to follow a naming convention, you should be able to loop through the menu items and use the name to lookup the appropriate text value for the menu.  But I just said that the MenuItem class does not expose a Name property.  Holy shatner!  They’re not making this easy on me.

We know that the form (let’s call it JimmyForm) has member variables holding references to all of the MenuItem objects:

    Private Sub InitializeComponent()

        Me.MainMenu1 = New System.Windows.Forms.MainMenu

        Me.mnuJimmyFile = New System.Windows.Forms.MenuItem

        Me.mnuNewJimmyLegs = New System.Windows.Forms.MenuItem

        Me.mnuOpenMouthInsertFoot = New System.Windows.Forms.MenuItem

        Me.mnuGetMeOutOfHereJimmy = New System.Windows.Forms.MenuItem

        Me.mnuHelpTopMenu = New System.Windows.Forms.MenuItem

        Me.mnuHelpMeJimmy = New System.Windows.Forms.MenuItem

        Me.mnuAboutJimmy = New System.Windows.Forms.MenuItem

        Me.ListBox1 = New System.Windows.Forms.ListBox

        Me.Button1 = New System.Windows.Forms.Button

        Me.SuspendLayout()

The information we want is there but we aren’t provided an easy way to get it.  We need some way to get a list of the member variables of our JimmyForm.  We want our JimmyForm to be introspective – to look upon itself and ask, “Of what do I consist?  From what am I made?”  We want JimmyForm to reflect upon its inner workings.  Wait, did I just say “reflect”?  That’s interesting.  There is a System.Reflection namespace that I’ve noticed in my Intellisense dropdown when I’m instantiating System.Random to decide how many hours to work each day.

You can use reflection to find out all kinds of things about classes and objects.  The code below uses reflection to get all the declared instance properties of the JimmyForm type.  Then it loops through each property and retrieves the value of that property for the current instance of JimmyForm (p.GetValue(Me, Nothing)).  If the object we get back is a MenuItem then we have a menu and we know its name (p.Name).  We’re cooking with propane now!

 

Dim typeOfThisForm As Type

typeOfThisForm = Me.GetType()

 

Dim props() As System.Reflection.PropertyInfo

 

props = typeOfThisForm.GetProperties( _

    Reflection.BindingFlags.NonPublic + _

    Reflection.BindingFlags.Public + _

    Reflection.BindingFlags.Instance + _

    Reflection.BindingFlags.DeclaredOnly)

 

For Each p As System.Reflection.PropertyInfo In props

    Dim menuCandidate As Object

    menuCandidate = p.GetValue(Me, Nothing)

    If TypeOf menuCandidate Is MenuItem Then

        Dim menu As MenuItem = DirectCast(menuCandidate, MenuItem)

        ListBox1.Items.Add(p.Name)

        menu.Text = menu.Text.ToUpper()

    End If

Next

Disclaimer:  If I was building this type of app from scratch I would dynamically generate all of my menus programmatically instead of creating them in the Forms designer.

Comments

No Comments