You are on page 1of 22

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="&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 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

Collapse
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 Filep; 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.

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,

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.

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.

• Download source files - 13 Kb


• Download demo - 5.77 Kb

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.

ContextMenu is in some ways simpler than MainMenu, mostly because it is smaller,


and sometimes contains only a list of menu items without any submenus.

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.

Steps to create the context menu

1. Placing ContextMenu Control:

Drag and drop a ContextMenu on to your form from the toolbox.


Simply, if you want to place a context menu on your form, just drop a
ContextMenu object from the form designer toolbox on to the Form object,
then type in the choices you want to present. Bring up the menu in your
program, and respond to the events fired when the user clicks on a context
menu choice.

2. Placing ListView and TextBox Controls:

Drag and drop a ListView and a TextBox control onto your form.

3. Setting ContextMenu Property:

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:

Set the ContextMenu property of each control to ContextMenu1.

A ContextMenu is associated with a control by changing the ContextMenu


property of a control. The properties for the ContextMenu are both in the
properties for the control and the ContextMenu component.

4. MouseUp Event Routine:

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.

Private Sub listView1_MouseUp(Byval Sender as Object, _


Byval e As System.Windows.Forms.MouseEventArgs) _
Handles listView1.MouseUp
'Checking the Mouse right Button
If e.Button = MouseButtons.Right Then
ContextHandler(listView1,e)
listView1.ContextMenu.Show(listView1, New Point(e.X,e.Y))
End if
End sub

Private Sub TextBox1_MouseUp(Byval Sender as Object, _


Byval e As System.Windows.Forms.MouseEventArgs) _
Handles TextBox1.MouseUp

'Checking the Mouse right Button


If e.Button = MouseButtons.Right Then
ContextHandler(TextBox1,e)
TextBox1.ContextMenu.Show(TextBox1, New Point(e.X,e.Y))
End if
End sub

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:

Private Sub HandleMouseUp(Byval Control as Object, _


Byval e As System.Windows.Forms.MouseEventArgs)

'Checking the Mouse right Button


If e.Button = MouseButtons.Right Then
ContextHandler(Control,e)
Control.ContextMenu.Show(Control, New Point(e.X,e.Y))
End if
End sub

Call the routine “HandleMouseUp” to your MouseUp event of ListView1


control.

Private Sub listView1_MouseUp(Byval Sender as Object, _


Byval e As System.Windows.Forms.MouseEventArgs) _
Handles listView1.MouseUp
HandleMouseUp(listView1,e)
End sub

Similarly, call the HandleMouseUp procedure in TextBox1 MouseUp event:

Private Sub TextBox1_MouseUp(Byval Sender as Object, _


Byval e As System.Windows.Forms.MouseEventArgs) _
Handles TextBox1.MouseUp
HandleMouseUp(listView1,e)
End sub
Wow, it reduced a lot of lines in your source code and looks neat. Right! As I
mentioned earlier, you can write your own procedure in your source code
based on your programming expertise or skills and appropriate coding
standards. I would prefer to create a catalog of functions, and then,
depending on the text in your new menu item, you can handle it with only
one handler that checks the text of the sender as shown above.

It won’t bother to walk you through this process, as it is very intuitive.


However, one change that you need to be aware of is that context menus are
no longer taken from a form’s main menu. Instead, you now add a separate
ContextMenu control for each context menu. To edit a context menu, click to
select the corresponding ContextMenu control from the component tray. The
menu will appear at the top as though it were a normal drop-down menu,
except that you’ll be able to edit it as needed.

5. Writing ContextHandler Procedure:

Next step is to write a “ContextHandler” routine.

Collapse

Protected Sub ContextHandler (ByVal sender As System.Object, _


ByVal e As System.EventArgs)

'ListView Menu Captions


Const LVW_NEW_MENU = "&New"
Const LVW_OPEN_MENU = "&Open File"
Const LVW_PREVIEW = "&Preview"
Const LVW_DELETE = "&Delete"

'Text Menu Captions


Const TEXT_MENU1 = "Text Menu&1"
Const TEXT_MENU11 = "Text Menu1&1" ‘Sub Menu 1
Const TEXT_MENU12 = "Text Menu1&2" ‘Sub Menu 2
Const TEXT_CUT = "&Cut"
Const TEXT_COPY = "&Copy"
Const TEXT_PASTE = "&PASTE"
Const TEXT_SELECT_ALL = "&Select All"

Const SEPARATOR = "-"

Const LIST_VIEW_NAME=”listView1”
Const TEXT_BOX_NAME=”TextBox1”

' Clear all previously added MenuItems.


ContextMenu1.MenuItems.Clear()

If sender.name = LIST_VIEW_NAME Then


'ContextMenu1.SourceControl is

'Define the MenuItem object to display for the ListView1.

Dim menuItem1 As New MenuItem(LVW_NEW_MENU)


Dim menuItem2 As New MenuItem(LVW_OPEN_MENU)
Dim menuItem3 As New MenuItem(SEPARATOR)
Dim menuItem4 As New MenuItem(LVW_PREVIEW)
Dim menuItem5 As New MenuItem(SEPARATOR)
Dim menuItem6 As New MenuItem(LVW_DELETE)
' Add MenuItems to display for the TextBox.
ContextMenu1.MenuItems.Add(menuItem1)
ContextMenu1.MenuItems.Add(menuItem2)
ContextMenu1.MenuItems.Add(menuItem3)
ContextMenu1.MenuItems.Add(menuItem4)
ContextMenu1.MenuItems.Add(menuItem5)

If listView1.FocusedItem Is Nothing Then


menuItem2.Enabled = False
menuItem4.Enabled = False
menuItem6.Enabled = False
End If

ElseIf sender.name = TEXT_BOX_NAME Then


'ContextMenu1.SourceControl is

'Define the MenuItem object to display for the TextBox1.

Dim menuItem7 As New MenuItem(TEXT_MENU1)


Dim menuItem8 As New MenuItem(TEXT_MENU11)
Dim menuItem9 As New MenuItem(SEPARATOR)
Dim menuItem10 As New MenuItem(TEXT_MENU12)
Dim menuItem11 As New MenuItem(SEPARATOR)
Dim menuItem12 As New MenuItem(TEXT_CUT)
Dim menuItem13 As New MenuItem(TEXT_COPY)
Dim menuItem14 As New MenuItem(TEXT_PASTE)
Dim menuItem15 As New MenuItem(SEPARATOR)
Dim menuItem16 As New MenuItem(TEXT_SELECT_ALL)

'Set the checked property to true since


'this is the default value.
menuItem8.Checked = True
'Define a shortcut key combination for the menu item.
menuItem9.Shortcut = Shortcut.CtrlS

'Add menuItem8 and menuItem9 to menuItem7's


'list of menu items or sub menus.
MenuItem7.MenuItems.Add(menuItem8)
MenuItem7.MenuItems.Add(SEPARATOR)
MenuItem7.MenuItems.Add(menuItem9)

'Add MenuItems to display for the TextBox.

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)

'If no text, disable the cut, copy, paste menus


If TextBox1.Text = “” Then
MenuItem12.Enabled=false
MenuItem13.Enabled=false
MenuItem14.Enabled=false
MenuItem16.Enabled=false
End If
End If
End Sub
As I mentioned earlier, you can even reduce the number of lines in your
source code breaking it into different functions. The above line of code is split
into different sub routines. This is listed below:

A new procedure called AddContextMenu will add a MenuItem to the context


menu based on the Caption parameter. The details are listed in the following
block of code:

Private Sub AddContextMenu(ByVal Caption As String, _


Optional ByVal IsEnabled As Boolean = True)

Dim objMenuItem As MenuItem

objMenuItem = New MenuItem


objMenuItem.Text = Caption
objMenuItem.Enabled = IsEnabled
ContextMenu1.MenuItems.Add(objMenuItem)
' Handling Popup menu item click event
AddHandler objMenuItem.Click, AddressOf CMenuClick
End Sub

The AddHandler routine will execute with ContextHandler object event at


any time during program execution. AddHandler routine details are
explained in the consuming events section, which is described at the end of
this article.

Based on the new procedure “AddContextMenu”, which we created above,


we can modify the “ContextHandler” procedure as well.

Collapse

Protected Sub ContextHandler (ByVal sender As System.Object, _


ByVal e As System.EventArgs)

'ListView Menu Captions


Const LVW_NEW_MENU = "&New"
Const LVW_OPEN_MENU = "&Open File"
Const LVW_PREVIEW = "&Preview"
Const LVW_DELETE = "&Delete"

'Text Menu Captions


Const TEXT_MENU1 = "Text Menu&1"
Const TEXT_MENU11 = "Text Menu1&1" ‘Sub Menu 1
Const TEXT_MENU12 = "Text Menu1&2" ‘Sub Menu 2
Const TEXT_CUT = "&Cut"
Const TEXT_COPY = "&Copy"
Const TEXT_PASTE = "&PASTE"
Const TEXT_SELECT_ALL = "&Select All"

Const SEPARATOR = "-"

Const LIST_VIEW_NAME=”listView1”
Const TEXT_BOX_NAME=”TextBox1”

' Clear all previously added MenuItems.


ContextMenu1.MenuItems.Clear()

If ContextMenu1.SourceControl is listView1 Then


'or sender.name = LIST_VIEW_NAME
' Define the MenuItem object to display for the ListView1.
AddContextMenu(LVW_NEW_MENU)
AddContextMenu(LVW_OPEN_MENU, listView1.FocusedItem Is Nothing)
AddContextMenu(SEPARATOR)
AddContextMenu(LVW_PREVIEW, listView1.FocusedItem Is Nothing)
AddContextMenu(SEPARATOR)
AddContextMenu(LVW_DELETE, listView1.FocusedItem Is Nothing)

ElseIf ContextMenu1.SourceControl is TextBox1 Then


'or sender.name = TEXT_BOX_NAME Then 'ContextMenu1.SourceControl is
' Define the MenuItem object to display for the 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

' Add menuItem8 and menuItem9 to menuItem7's


' list of menu items or sub menus.
' My AddContextMenu procedure is not handling
' to support nested submenu creation
MenuItem1.MenuItems.Add(SubmenuItem1)
MenuItem1.MenuItems.Add(SEPARATOR)
MenuItem1.MenuItems.Add(SubmenuItem2)

' Add MenuItems to display for the TextBox.


ContextMenu1.MenuItems.Add(menuItem)
AddContextMenu(SEPARATOR)
AddContextMenu(LVW_NEW_MENU)
AddContextMenu(TEXT_CUT, TextBox1.Text = “”)
AddContextMenu(TEXT_COPY, TextBox1.Text = “”)
AddContextMenu(TEXT_PASTE, TextBox1.Text = “”)
AddContextMenu(SEPARATOR)
AddContextMenu(TEXT_SELECT_ALL, TextBox1.Text = “”)
End If
End Sub

In the following HandleMouseUp event, you need to remove the


ContextHandler method in order to use ContextMenu1.SourceControl
method because the function MenuSelected will automatically handle the
event ContextMenu1.Popup. The revised procedure is shown below:

Private Sub HandleMouseUp(Byval Control as Object, _


Byval e As System.Windows.Forms.MouseEventArgs)

' Checking the Mouse right Button


If e.Button = MouseButtons.Right Then
Control.ContextMenu.Show(Control, New Point(e.X,e.Y))
End if
End sub

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”.

Private Sub HandlePopup(ByVal sender As Object, _


ByVal e As System.EventArgs) Handles ContextMenu1.Popup
MyPopupEventHandler(sender, e)
End Sub
Note 1: I used a constant name called “SEPARATOR” for separating menu
items, which is same as the old version of VB. You can even use Break or
BreakLine method from the ContextMenu class to include a separator into
menu items.

Note 2: I replaced Sender.Name method with


ContextMenu1.SourceControl. The Sender.Name will also function as
same as the ContextMenu1.SourceControl, but, I personally feel this is
much better to handle context menu in the right place. If you are using
Sender.Name instead of ContextMenu1.SourceControl, you need to add
the following lines of code in each of the control event.

Note 3: The AddContextMenu procedure does not support adding nested


sub menus.

6. Consuming Events:

An event is an action that informs an application that something has


happened in the object. For example, when you click a particular menu item
from the ContextMenu in order to fire the associated event, you need to
define a new EventHandler routine into your program.

To consume a ContextMenu event in this application, you must provide an


event handler (an event-handling method) that executes program logic in
response to the event, and register the event handler with the event source.

To wire an event handler to the ContextMenu, you must create an instance


of EventHandler that takes a reference to ContextMenu_Clicked in its
argument, and add this delegate instance to the PopUp_Click event of the
ContextMenu, as shown in the following example.

AddHandler method associates an event with an event handler, and it allows


you to start event handling at any time during program execution.

Please go through the program listed in, which is shown below:

Private Sub AddContextMenu(ByVal Caption As String, _


Optional ByVal IsEnabled As Boolean = True)

Dim objMenuItem As MenuItem

objMenuItem = New MenuItem


objMenuItem.Text = Caption
objMenuItem.Enabled = IsEnabled
ContextMenu1.MenuItems.Add(objMenuItem)
' Handling Popup menu item click event
AddHandler objMenuItem.Click, AddressOf CMenuClick

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:

Private Sub CMenuClick(ByVal sender As Object, _


ByVal e As System.EventArgs)

'Point to menu item clicked


Dim objCurMenuItem As MenuItem = CType(sender, MenuItem)
'create the submenu based on whatever selection is being made,
mnuSelection
Select Case objCurMenuItem.Text
Case LVW_NEW_MENU: Msgbox(“New Menu”)
Case LVW_NEW_MENU: Msgbox(“New Menu”)
Case LVW_OPEN_MENU: Msgbox(“New Menu”)
Case LVW_PREVIEW: Msgbox(“New Menu”)
Case LVW_DELETE Msgbox(“New Menu”)
Case TEXT_CUT: Msgbox(“New Menu”)
Case TEXT_COPY: Msgbox(“New Menu”)
Case TEXT_PASTE: Msgbox(“New Menu”)
Case TEXT_SELECT_ALL: Msgbox(“New Menu”)
End Select

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 my application, I made all the functions as UserControls. So in the database, each


function had the name of the DLL and the name of the UserControl to call for each
specific menu item. Then with the activator class, I loaded the UserControl to a panel
in the main form of the application.

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

The database is very simple: it contains three tables:

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

Dim dsItems As New DataSet


Dim i As Integer

'create a dataset with all items possible


Dim adap As New OleDb.OleDbDataAdapter("", conn)
adap.SelectCommand.CommandText = "SELECT * FROM ITEMS"
adap.Fill(dsItems)

'set the hierarchy as primary key to find


'the menu item "parents"
dsItems.Tables(0).PrimaryKey = New DataColumn()
{dsItems.Tables(0).Columns("HIERAR")}
'go one by one all user items
For i = 0 To dsUser.Tables(0).Rows.Count - 1
Dim id_hierar As String 'hierarchy for the item (parents
'and parents parents and so on)
Dim parent As String 'temp var to store parent index

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 d In HashMenu


sonItem = d.Value
sel_key = d.Key
sonMenu = sonItem.MenuItemobj
If sel_key.LastIndexOf(".") >= 0 Then
sel_KeyParent = sel_key.Substring(0, _
sel_key.LastIndexOf("."))
parentItem = HashMenu(sel_KeyParent)
parentMenu = parentItem.MenuItemobj
parentMenu.MenuItems.Add(sonMenu)
Else
Me.MainMenu.MenuItems.Add(sonMenu)
End If
Next
CreateHashFunctions()

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).

Gọi hàm: 996410608


About the functions:

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.

Something like that (on the CallForMenuItem sub):

'Get the dll and class of control to load to main form


'format dllname*classname
funcToCall = HashFunctions(position)

'create the control to load to main Panel

Dim o, s As Object
Dim AssemblyDll, ClassDll As String

AssemblyDll = funcToCall.substring(0,funcToCall.indexOf("*"))
Classe = funcToCall.substring(funcToCall.indexOf("*") + 1)

o = Activator.CreateInstance(AssemblyDll, AssemblyDll & "." &


Classe)
s = o.Unwrap()

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:

Private Sub CallForMenuItem(ByVal obj As Object, ByVal ea As


EventArgs)
'this function is executed every time a menuItem is clicked
Dim i As New MenuItem
Dim pai As New MenuItem
Dim position As String

'Verify the position (which is the hierarchy) of the menu Item


called
i = obj

If TypeOf i.Parent Is MainMenu Then


position = i.Index
Else
pai = i.Parent
position = i.Index
While (pai.Parent.GetType().ToString <>
"System.Windows.Forms.MainMenu")
position = pai.Index & "." & position
pai = pai.Parent
End While
'Store on var position the absolute position of
'the menuItem in the menu (n.n.n) to use it as key for the
'HashTable HashFunctions
position = pai.Index & "." & position
End If

Dim funcToCall As String

funcToCall = HashFunctions(position)

MsgBox(funcToCall)

'here you should place a SELECT CASE (funcToCall)


'or call a function to do that
'to execute the right function for each call

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

For Each d In HashMenu


item = d.Value.MenuItemObj
position = item.Index

If (item.Parent.GetType().ToString =
"System.Windows.Forms.MainMenu") Then
position = item.Index
Else
pai = item.Parent

While (pai.Parent.GetType().ToString <>


"System.Windows.Forms.MainMenu")
position = pai.Index & "." & position
pai = pai.Parent
End While
position = pai.Index & "." & position
End If

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.

Did you mean that?

Ok, here goes the code. Add a TabControl named tabUsers, add two tabs (indexes 0 and
1) and add this function to the code:

Private Sub tabUsers_SelectedIndexChanged(ByVal sender As


System.Object, ByVal e As System.EventArgs) Handles
tabUsers.SelectedIndexChanged
' what to do when user clicks on a different tab

Dim user_id As String

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

'Clean the previous menu items and hashtables


MainMenu.MenuItems.Clear()
HashFunctions.Clear()
HashMenu.Clear()

'select user parameters from the database


'(since I am sure there is a user 1 and 2 I am not checking
' here if it exists, but you should check)
Dim adap As New OleDb.OleDbDataAdapter("", conn)
adap.SelectCommand.CommandText = "SELECT * FROM ITEMS INNER JOIN
PERMS ON PERMS.ID_ITEM = ITEMS.ID_ITEM WHERE ID_USER =" & user_id
dsUser.Clear()
adap.Fill(dsUser)

'Load new menu items to main menu


LoadMenuItems()

End Sub
Yes, I need this. Code to call a form when the user clicks and option.

Help me!

You might also like