Introduction
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.
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,
It means that, VB.NET Form in which the Menu is getting displayed should have
below code pasted in,
Please note that Event Name is framed based on the below format,
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
Collapse
Public Class DynamicMenu
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'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
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'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)
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.
Collapse
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,
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.
Contents
• Introduction
• ContextMenu in .NET
• Example
• Steps to create the context menu
1. Placing ContextMenu Control
2. Placing ListView and TextBox Controls
3. Setting ContextMenu Property
4. MouseUp Event Routine
5. Writing ContextHandler Procedure
6. Consuming Events
• Sample Screenshots
• Requirements
• Summary
Introduction
This article targets those who have not yet worked with ContextMenu in .NET
environment and would like to understand a little easy way of how ContextMenu
and its associated event handler works. How can a context menu for a notify icon be
programmatically generated when the user right clicks on the notify icon? As a
prerequisite, you should have a little bit idea of object oriented programming.
This Windows application feature allows gives you the ability to right-click on any
control of your Windows Form and get a context-sensitive menu. If a user right-clicks
or left-clicks in a .NET application, the form shows its own menu. This is very helpful
for the user to know what options are attached to a particular control or an object.
ContextMenu in .NET
A ContextMenu is a shortcut menu, which you can use to give context-specific menu
options of a specified object item to the user. You can write the code to display the
shortcut menu whenever the user right-clicks on a specified control such as
TextBox, ListBox, ListView etc. Typically, a shortcut menu is displayed when a
user clicks the right mouse button over a control or the form itself. The beauty of
.NET in terms of ContextMenu is that it is possible to use one ContextMenu in
more than one control. What you need to do is set the ContextMenu property of
each control to ContextMenu class. Even you can also create nested submenus in a
ContextMenu.
Example
The following example gives a step by step instruction to use a shortcut menu
assigned to the ListView and TextBox control to display some of the menu items
based on the user right-click event of the mouse in a specified control. This means
the context menu will be changed based on each control. This example creates an
event handler for the PopUp event of the ContextMenu. The code in the event
handler determines which of two controls, a ListView named listView1 and a
TextBox named textBox1, is the control displaying the shortcut menu. Depending
on which control caused the ContextMenu to display its shortcut menu, the control
adds the appropriate MenuItem objects to the ContextMenu. This example assumes
that you have an instance of the ContextMenu class, named contextMenu1,
defined within the form. This example also assumes that you have a TextBox and
ListView added to a form, and that the ContextMenu property of these controls is
set to ContextMenu1.
Drag and drop a ListView and a TextBox control onto your form.
Add a ContextMenu to your form and set the ContextMenu property of your
TextBox and ListBox to it. Then, in the PopUp event of your
ContextMenu, you can dynamically create the MenuItems as shown below.
You can trap which menu item was selected using an old school Select
Case based on the MenuItem text:
What kind of events are you firing and what will you be doing with
ContextMenu control? As you can see from the code in the next section, you
can determine which item was clicked by casting the "sender" variable to a
MenuItem and querying the Text property. You can use that value to pass to
another function or do something with it.
Write a routine on the “MouseUp” event on your control. This will happen if
the right mouse button was clicked and released; display the context menu
assigned to the listView1 or TextBox1 control.
Based on your programming skills, you can even reduce the number of lines
of your source code by putting this event in a separate procedure and calling
this procedure in control mouse up event. For example, I have recreated this
source code in a different way, which you can see below:
Collapse
Const LIST_VIEW_NAME=”listView1”
Const TEXT_BOX_NAME=”TextBox1”
ContextMenu1.MenuItems.Add(menuItem7)
ContextMenu1.MenuItems.Add(menuItem10)
ContextMenu1.MenuItems.Add(menuItem11)
ContextMenu1.MenuItems.Add(menuItem12)
ContextMenu1.MenuItems.Add(menuItem13)
ContextMenu1.MenuItems.Add(menuItem14)
ContextMenu1.MenuItems.Add(menuItem15)
ContextMenu1.MenuItems.Add(menuItem16)
Collapse
Const LIST_VIEW_NAME=”listView1”
Const TEXT_BOX_NAME=”TextBox1”
' Set the checked property to true since this is the default value.
SubmenuItem1.Checked = True
' Define a shortcut key combination for the menu item.
SubmenuItem2.Shortcut = Shortcut.CtrlS
Again, in order to get fully functional, you need to add a new handle for
ContextMenu1 popup, which I created below and named as “HandlePopup”.
6. Consuming Events:
End Sub
The AddHandler statement in the above code will fire an event whenever a
user clicks on the menu item, and executes the procedure cMenuClick which
is listed below:
objCurMenuItem = Nothing
End Sub
Sample Screenshots
Introduction
I searched around for a menu based on a database, where each user in the database
would "see" a different menu in the application depending on his permissions and
privileges. Since I couldn't find one, I came up with a simple idea. I built tables for
users and privileges in an Access database and loaded them to the menu application
when the user logged in.
In this simple example, I just made a message box for each menu, that says what to
do with each menu item based on the information in the database.
The database
1. USERS
o Contains username, user ID and password.
2. ITEMS
o Contains all menu items - text is the text displayed in the menu and
func is the function to call when the item is clicked (it can also be the
name of the UserControl, so the Activator can call it to the main panel
in the form).
3. PERMS
o Contains all permissions for the user.
The code
When the user logs in, the application gets all the information about the user from
the database. The code builds two Hashtables to map the items with the respective
functions to call when the menu item is clicked. The first Hashtable is called
HashMenu and it contains all the information about the menu items that the user can
access. Since it is mandatory for the user to also have the parent items the function
LoadMenuItems() gets from the database all the items that must be seen, so the
user can access the ones he has permission to.
Collapse
Function LoadMenuItems()
'Load all menu items and sub-items to menu
id_hierar = dsUser.Tables(0).Rows(i)("HIERAR")
Dim menu_it As New DinamicMenu
menu_it.Text = dsUser.Tables(0).Rows(i)("TEXT")
menu_it.Func = dsUser.Tables(0).Rows(i)("FUNC")
menu_it.MenuItemobj = New MenuItem
menu_it.MenuItemobj.Text = dsUser.Tables(0).Rows(i)("TEXT")
AddHandler menu_it.MenuItemobj.Click, AddressOf CallForMenuItem
If Not HashMenu.ContainsKey(dsUser.Tables(0).Rows(i)("HIERAR")) Then
HashMenu.Add(dsUser.Tables(0).Rows(i)("HIERAR"), menu_it)
End If
While id_hierar.IndexOf(".") > 0
parent = id_hierar.Substring(0, id_hierar.LastIndexOf("."))
id_hierar = parent
Dim dRow As DataRow 'dataRow to store the item row from db
dRow = dsItems.Tables(0).Rows.Find(parent)
If Not dRow Is Nothing Then
If Not HashMenu.ContainsKey(parent) Then
Dim it_dinamic As New DinamicMenu
it_dinamic.Text = dRow("TEXT")
it_dinamic.Func = dRow("FUNC")
it_dinamic.MenuItemobj = New MenuItem
it_dinamic.MenuItemobj.Text = dRow("TEXT")
AddHandler it_dinamic.MenuItemobj.Click, _
AddressOf CallForMenuItem
HashMenu.Add(parent, it_dinamic)
End If
End If
End While
Next
Dim d As DictionaryEntry
Dim parentMenu As New MenuItem
Dim sonMenu As New MenuItem
Dim parentItem As New DinamicMenu
Dim sonItem As New DinamicMenu
Dim sel_key, sel_KeyParent As String
For each user, the order is different, because they have different permissions, so we
have to build a hashtable, called HashFunctions, to create a relation between the
place of the item and the item function. There is another function called
CallForMenuItem() that executes the function based on the place the menu item
was called (since the relation between the place and the function is stored in the
hashFunctions Hashtable).
In my case, I have created Dlls with classes as userControls. On the database, each
menu item is associated to a dll and to a class (control).
I created a panel in my main form. Every time user clicks on menu, the application
checks on the database wich dll to open and wich userControl to create. Then create an
instance of the control and add it to the main form panel.
Dim o, s As Object
Dim AssemblyDll, ClassDll As String
AssemblyDll = funcToCall.substring(0,funcToCall.indexOf("*"))
Classe = funcToCall.substring(funcToCall.indexOf("*") + 1)
Me.mainPanel.Controls.Add(s)
if you want to order item menu: You can add the code
Dim parentMenu As New MenuItem
Dim sonMenu As New MenuItem
Dim parentItem As New clsMenuDinamic
Dim sonItem As New clsMenuDinamic
Dim sel_key, sel_KeyParent As String
Dim al As ArrayList
al = New ArrayList(Hashmenu.Keys)
al.Sort()
Dim k As Integer
For k = 0 To al.Count - 1
For Each d In Hashmenu
If al(k) = d.Key Then
sonItem = d.Value
sel_key = d.Key
sonMenu = sonItem.MenuItemO
If sel_key.LastIndexOf(".") >= 0 Then
sel_KeyParent = sel_key.Substring(0, sel_key.LastIndexOf("."))
parentItem = Hashmenu(sel_KeyParent)
parentMenu = parentItem.MenuItemO
parentMenu.MenuItems.Add(sonMenu)
Else
Me.MainMenu.MenuItems.Add(sonMenu)
End If
End If
Next
Next
I did not thought about this case (of only 1 item as being the mainMenu).
We should test this matter at the CallForMenuItem() function.
We also have to make a little change at the CreateHashFunctions() function, since I did
not relate a function (func) to a mainMenu Item.
If you make this 2 changes you will have no problem. I am posting the functions with the
changes:
funcToCall = HashFunctions(position)
MsgBox(funcToCall)
End Sub
and
Function CreateHashFunctions()
'Creatre a HashTable with information about the functions to call
'from each position at the menu
Dim position As String
Dim pai As New MenuItem
Dim item As New MenuItem
Dim d As New DictionaryEntry
Dim func As String
If (item.Parent.GetType().ToString =
"System.Windows.Forms.MainMenu") Then
position = item.Index
Else
pai = item.Parent
func = d.Value.Func
'Create an entry with relation between menuitem position and
function to call
HashFunctions.Add(position, func)
Next
End Function
I hope I understand what you mean. I added a tabcontrol named tabUsers to the project
with two tabs (Daniel and Debora). When user clicks on Daniel he gets a menu for Daniel
and when he clicks on Debora he gets Debora´s menu.
Ok, here goes the code. Add a TabControl named tabUsers, add two tabs (indexes 0 and
1) and add this function to the code:
If tabUsers.SelectedIndex = 0 Then
'user will be Daniel
user_id = 1
End If
If tabUsers.SelectedIndex = 1 Then
'user will be Debora
user_id = 2
End If
End Sub
Yes, I need this. Code to call a form when the user clicks and option.
Help me!