VB.NET-XML based Dynamic Menu Component using Recursion Technique

In this article, we will see how to generate Dynamic Menus (using recursion) in VB.net based on the XML data which has the complete details about the Menu like Menu Caption and the correponding Event it has to trigger whenever user click a Menu Item etc., It's a generalized re-usable component written in such a way that it can be easily plugged to any application based on the requirement.

XML based Dynamic Menu Component

The main objective of this component is to generate Menus at runtime based on the values present in the XML Configuration file. Let's first have a look at the XML Configuration File assuming the File Name as Menu.xml and sample contents are as follows,

<root>
<TopLevelMenu id="&amp;File">
       <MenuItem id = "New" OnClick="_New"/>
       <MenuItem id = "Open"/>
       <MenuItem id = "Send To">
              <MenuItem id ="Mail"/>
              <MenuItem id ="My Documents"/>
       </MenuItem>
</TopLevelMenu>
<TopLevelMenu id="&amp;Edit">
       <MenuItem id = "Copy"/>
       <MenuItem id = "Paste" OnClick="_Paste"/>
       <MenuItem id = "Clear">
             <MenuItem id = "F&amp;ormats"/>
              <MenuItem id ="Contents">
                    <MenuItem id = "Test" OnClick="_Test"/>
               </MenuItem>
       </MenuItem>
</TopLevelMenu>
</root>

From the above XML, its evident that Nodes defined as TopLevelMenu will be the Parent/Top level Menu and the Nodes defined as MenuItem will be the corresponding child for it. Menu Captions are defined in the attribute named "id", you can manipulate the XML file to display custom Captions for Menu Items.

File                       - is the Top Level Menu
New                     - is the child of File
Open                    - is the child of File
SendTo                - is the child of File
Mail                      - is the child of SendTo
My Documents     - is the child of SendTo

Edit                      - is the Top Level Menu
Copy                    - is the child of Edit
Paste                    - is the child of Edit
Clear                    - is the child of Edit
Formats                - is the child of Clear
Contents               - is the child of Clear
Test                      - is the child of Contents

Note: The "&amp;" text facilitates the use of short-cut keys for menu items that are being defined.

OnClick - This is a main attribute which defines the Event the particular menu item should trigger whenever user performs a click. For ex., the New Menu Item has OnClick attribute defined as below,

<MenuItem id = "New" OnClick="_New"/>

It means that, VB.NET Form in which the Menu is getting displayed should have below code pasted in,

Private Sub MenuItemOnClick_New(ByVal sender As Object, ByVal e As System.EventArgs)
    MessageBox.Show("New Clicked")
End Sub

Please note that Event Name is framed based on the below format,

MenuItemOnClick        - is the hard-coded value in the component, it doesnt come from your XML File.
_New                             - is as defined in the OnClick Attribute. If you change the _New to _NewItem(lets say) then your Form Event should also be changed to,

Private Sub MenuItemOnClick_NewItem(ByVal sender As Object, ByVal e As System.EventArgs)
 MessageBox.Show("New Clicked")
End Sub

The dynamic menu component exposes Event Handlers for each and every Menu Item you have created with an attribute value of "OnClick". If you dont want to create a Event handler for a menu item, then you need not specify the OnClick attribute for it in the XML file.

Now let us now see the code details of the component which generates Menu dynamically based on the XML content,

DynamicMenu.vb

Public Class DynamicMenu
    ''''''''''''''''''''''variable declarations begins''''''''''''''''''''''''''''
    'Create a main menu object.
    Private mainMenu As New mainMenu()
    'Object for loading XML File
    Private objXML As Xml.XmlDocument
    ' Create menu item objects.
    Private mItem As New MenuItem()
    'Menu handle that should be returned
    Private objMenu As Menu
    'Path of the XML Menu Configuration File
    Public XMLMenuFile As String
    'Form Object in which Menu has to be build
    Public objForm As Object
    ''''''''''''''''''''''variable declarations ends '''''''''''''''''''''''''''''

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    'This method will get invoked by a parent Form.
    'And it returns Menu Object.
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Public Function LoadDynamicMenu()
        Dim oXmlElement As Xml.XmlElement
        Dim objNode As Xml.XmlNode

        objXML = New Xml.XmlDocument()
        'load the XML File
        objXML.Load(XMLMenuFile)
        'Get the documentelement of the XML file.
        oXmlElement = CType(objXML.DocumentElement, Xml.XmlElement)
        'loop through the each Top level nodes
        'For ex., File & Edit becomes Top Level nodes
        'And File -> Open , File ->Save will be treated as
        'child for the Top Level Nodes
        For Each objNode In objXML.FirstChild.ChildNodes
            'Create a New MenuItem for Top Level Nodes
            mItem = New MenuItem()
            ' Set the caption of the menu items.
            mItem.Text = objNode.Attributes("id").Value
            ' Add the menu items to the main menu.
            mainMenu.MenuItems.Add(mItem)
            'Call this Method to generate child nodes for
            'the top level node which was added now(mItem in the above Add statement)
            GenerateMenusFromXML(objNode, mainMenu.MenuItems(mainMenu.MenuItems.Count - 1))
        Next
        'return this Menu handle to the parent Form so that
        'generated menu gets displayed in the Form
        Return objMenu
    End Function

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    'This method takes care of loading Menus based on XML file contents.
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Private Sub GenerateMenusFromXML(ByVal objNode As Xml.XmlNode, ByVal mItm As MenuItem)
        'This method will be invoked in an recursive fashion
        'till all the child nodes are generated. This method
        'drills up to N-levels to generate all the Child nodes
        Dim objNod As Xml.XmlNode
        Dim sMenu As New MenuItem()
        'loop for child nodes
        For Each objNod In objNode.ChildNodes
            sMenu = New MenuItem()
            ' Set the caption of the menu items.
            sMenu.Text = objNod.Attributes("id").Value
            mItm.MenuItems.Add(sMenu)
            'Add a Event handler to the menu item added
            'this method takes care of Binding Event Name(based on the parameter from
            'from xml file) to newly added menu item.
            'for ex., Your Form Code should have a Private sub MenuItemOnClick_New even to handle
            'the click of New Menu Item
            If Not objNod.Attributes("OnClick") Is Nothing Then
                FindEventsByName(sMenu, objForm, True, "MenuItemOn", objNod.Attributes("OnClick").Value)
            End If
            'call the same method to see you have any child nodes
            'for the particular node you have added now(above mItm)
            GenerateMenusFromXML(objNod, mItm.MenuItems(mItm.MenuItems.Count - 1))
        Next
        'assign the generated mainMenu object to objMenu - public object
        'which is to be used in the Main Form
        objMenu = mainMenu
    End Sub

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    'objective of this method is to find out the private event present in Form
    'and attach the newly added menuitem to this event, this was achieved using
    'Reflection technique
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Private Sub FindEventsByName(ByVal sender As Object, _
     ByVal receiver As Object, ByVal bind As Boolean, _
     ByVal handlerPrefix As String, ByVal handlerSufix As String)
        ' Get the sender's public events.
        Dim SenderEvents() As System.Reflection.EventInfo = sender.GetType().GetEvents()
        ' Get the receiver's type and lookup its public
        ' methods matching the naming convention:
        '  handlerPrefix+Click+handlerSufix
        Dim ReceiverType As Type = receiver.GetType()
        Dim E As System.Reflection.EventInfo
        Dim Method As System.Reflection.MethodInfo
        For Each E In SenderEvents
            Method = ReceiverType.GetMethod( _
              handlerPrefix & E.Name & handlerSufix, _
              System.Reflection.BindingFlags.IgnoreCase Or _
              System.Reflection.BindingFlags.Instance Or _
              System.Reflection.BindingFlags.NonPublic)

            If Not Method Is Nothing Then
                Dim D As System.Delegate = System.Delegate.CreateDelegate(E.EventHandlerType, receiver, Method.Name)
                If bind Then
                    'add the event handler
                    E.AddEventHandler(sender, D)
                Else
                    'you can also remove the event handler if you pass bind variable as false
                    E.RemoveEventHandler(sender, D)
                End If
            End If
        Next
    End Sub
End Class

LoadDynamicMenu is the main method which will get invoked from Vb.net Form. It’s a function which returns the Menu handle and it should be assigned to the Me.Menu property.

You can open a New VB.NET Windows Application Project and add the above DynamicMenu.vb to the Project and just paste the below code in Form, it will generate a Dynamic Menu and will associate it to the Form.

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim objMenu As New DynamicMenu()
        'pass the location of the XML Menu - Configuration File
 objMenu.XMLMenuFile = "D:\DynamicMenu\Menu.xml"
        'pass the Form object to Dynamic Menu so as to associate the Menu Event handlers
        objMenu.objForm = Me
 'Load dynamic menu and return the Menu handle to Me.Menu object
        Me.Menu = objMenu.LoadDynamicMenu()
    End Sub

    'whenever user clicks New Menu Item - the below event will get triggered
    Private Sub MenuItemOnClick_New(ByVal sender As Object, ByVal e As System.EventArgs)
        MessageBox.Show("New Clicked")
    End Sub

    'whenever user clicks Paste Menu Item - the below event will get triggered
    Private Sub MenuItemOnClick_Paste(ByVal sender As Object, ByVal e As System.EventArgs)
        MessageBox.Show("Paste Clicked")
    End Sub

    'whenever user clicks Test Menu Item - the below event will get triggered
    Private Sub MenuItemOnClick_Test(ByVal sender As Object, ByVal e As System.EventArgs)
        MessageBox.Show("Test Clicked")
    End Sub

In the above code, we are creating an object objMenu for DynamicMenu Class and we are informing the location of XML Configuration File through XMLMenuFile property to the DynamicMenu Class and finally we invoke LoadDynamicMenu() function
which returns the Generated Menu Handle. It will be assigned back to the Me.Menu property and you are ready to use the Menus.

Just a snapshot of how the generated menus look like in the Form,



The dynamic menu generation logic works in the below way, it basically follows Recursion technique,

  1. Load the XML document
  2. Loop the Top Level nodes (in this case it’s File & Edit)
  3. Add MenuItem for the Top level node (lets say File)
    1. For each Top level node loop child nodes (for ex., New, Open)
    2. Add MenuItem New/Open to parent File node
    3. Create EventHandlers for the newly created Menu Item based on the OnClick Attribute.
    4. Call step 2a using Recursion logic and drill down to N-levels, i.e., loop till you reach the end node.
  4. Retrieve the Generated Menu handle
  5. Assign the generated Menu handle to Me.Menu property so as to display the Menu in the Form.

GenerateMenusFromXML method is invoked recursively for each node to find out whether ChildNodes exists for each node, if Child Nodes exists then it gets added as a MenuItem and further drilled down till it doesn’t return any ChildNodes.

Please download the DynamicMenu Component Source with a Sample Vb.net Form & XML file from the below link,

http://www.eggheadcafe.com/fileupload/-190104164_DynamicMenu.zip


You can add more elements/nodes to the XML file and you can generate Menu elements based on your requirement. It can support N-levels (File->Open->SubMenu->SubMenu and so on) as it’s a completely dynamic in nature which doesnt have any hardcoding. Since, it’s a re-usable component it can be used across various applications.


References

Please refer to MSDN for more info,
http://msdn2.microsoft.com/en-us/system.windows.forms.menuitem.aspx


Biography

I am a software developer and have worked on Microsoft technologies for about eight years now. I have always been fascinated by Microsoft technologies and with the advent of .NET , this fascination has reached new heights. I take a lot of interest in reading technical articles and equally enjoy writing them.






 

By sundar k   Popularity  (4125 Views)