Introduction
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 corresponding Event it has to trigger whenever the user clicks 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.
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="&File">
<MenuItem id = "New" OnClick="_New"/>
<MenuItem id = "Open"/>
<MenuItem id = "Send To">
<MenuItem id ="Mail"/>
<MenuItem id ="My Documents"/>
</MenuItem>
</TopLevelMenu>
<TopLevelMenu id="&Edit">
<MenuItem id = "Copy"/>
<MenuItem id = "Paste" OnClick="_Paste"/>
<MenuItem id = "Clear">
<MenuItem id = "F&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 "&" 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 the user performs a click. For example, 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<BR>
Private mainMenu As New mainMenu()
Private objXML As Xml.XmlDocument
Private mItem As New MenuItem()
Private objMenu As Menu
Public XMLMenuFile As String
Public objForm As Object
Public Function LoadDynamicMenu()
Dim oXmlElement As Xml.XmlElement
Dim objNode As Xml.XmlNode
objXML = New Xml.XmlDocument()
oXmlElement = CType(objXML.DocumentElement, Xml.XmlElement)
For Each objNode In objXML.FirstChild.ChildNodes
mItem = New MenuItem()
mItem.Text = objNode.Attributes("id").Value
mainMenu.MenuItems.Add(mItem)
GenerateMenusFromXML(objNode, _
mainMenu.MenuItems(mainMenu.MenuItems.Count - 1))
Next
Return objMenu
End Function
Private Sub GenerateMenusFromXML(ByVal objNode As Xml.XmlNode, _
ByVal mItm As MenuItem)
Dim objNod As Xml.XmlNode
Dim sMenu As New MenuItem()
For Each objNod In objNode.ChildNodes
sMenu = New MenuItem()
sMenu.Text = objNod.Attributes("id").Value
mItm.MenuItems.Add(sMenu)
If Not objNod.Attributes("OnClick") Is Nothing Then
FindEventsByName(sMenu, objForm, True, "MenuItemOn", _
objNod.Attributes("OnClick").Value)
End If
GenerateMenusFromXML(objNod, _
mItm.MenuItems(mItm.MenuItems.Count-1))
Next
objMenu = mainMenu
End Sub<
Private Sub FindEventsByName(ByVal sender As Object, _
ByVal receiver As Object, ByVal bind As Boolean, _
ByVal handlerPrefix As String, ByVal handlerSufix As String)
Dim SenderEvents() As System.Reflection.EventInfo =
sender.GetType().GetEvents()
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
E.AddEventHandler(sender, D)
Else
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()
objMenu.XMLMenuFile = "D:\DynamicMenu\Menu.xml"
objMenu.objForm = Me
Me.Menu = objMenu.LoadDynamicMenu()
End Sub
Private Sub MenuItemOnClick_New(ByVal sender As Object, _
ByVal e As System.EventArgs)
MessageBox.Show("New Clicked")
End Sub
Private Sub MenuItemOnClick_Paste(ByVal sender As Object, _
ByVal e As System.EventArgs)
MessageBox.Show("Paste Clicked")
End Sub
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,
- Load the XML document
- Loop the Top Level nodes (in this case it’s File & Edit)
- Add
MenuItem for the Top level node (lets say File)
- For each Top level node loop child nodes (for ex., New, Open)
- Add
MenuItem New/Open to parent File node
- Create
EventHandlers for the newly created Menu Item based on the OnClick Attribute.
- Call step 2a using Recursion logic and drill down to N-levels, i.e., loop till you reach the end node.
- Retrieve the Generated Menu handle
- 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.
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