Dynamic menu items and event handlers

E

eaguilar

Hi,

I am trying to dynamically generate a menu, based on entries on a text or
xml file. The text file contains the "tree" after which the menu will need to
be created. Something like the following:

Level 1
-- Level 2
-- Level 2
Level 1
-- Level 2
---- Level 3

I can read the file and generate each menu item definition, with the
corresponding "nesting", without a problem.

What I'm having difficulties with is deciding on how to handle the events
for each of the menu items. Generating the full menu dynamically would mean
having to create the event handlers dynamically, unless I have a fixed amount
of event handlers, which I'm not sure is the best approach. I might be wrong
here...

1. What's the best way to generate event handlers dynamically? I've read
about runtime IL generation, which makes sense but is a bit complex.

2. Using regular event handlers (hard coded), can I capture which of the
menu items is being clicked and what component is the menu being displayed
on? i.e., if the menu is being displayed after a right click on a button or a
picture box, can I: 1) know if it's the picture box or the button displaying
the menu? and 2) can I know what menu/sub menu is being clicked?

Any ideas will be greatly appreciated.

Thanks in advance,

</edwin>
 
H

Herfried K. Wagner [MVP]

eaguilar said:
I am trying to dynamically generate a menu, based on entries on a text or
xml file. The text file contains the "tree" after which the menu will need
to
be created. Something like the following:

Level 1
-- Level 2
-- Level 2
Level 1
-- Level 2
---- Level 3

I can read the file and generate each menu item definition, with the
corresponding "nesting", without a problem.

What I'm having difficulties with is deciding on how to handle the events
for each of the menu items. Generating the full menu dynamically would
mean
having to create the event handlers dynamically, unless I have a fixed
amount
of event handlers, which I'm not sure is the best approach. I might be
wrong
here...

1. What's the best way to generate event handlers dynamically? I've read
about runtime IL generation, which makes sense but is a bit complex.

Take a look at the 'AddHandler' and 'RemoveHandler' statements.
2. Using regular event handlers (hard coded), can I capture which of the
menu items is being clicked

Check out the event handler's 'sender' parameter. It contains a reference
to the source of the event.
and what component is the menu being displayed
on?

Context menu objects have a 'SourceControl' property which references the
control the menu has been shown on.
 
J

Jack Jackson

Hi,

I am trying to dynamically generate a menu, based on entries on a text or
xml file. The text file contains the "tree" after which the menu will need to
be created. Something like the following:

Level 1
-- Level 2
-- Level 2
Level 1
-- Level 2
---- Level 3

I can read the file and generate each menu item definition, with the
corresponding "nesting", without a problem.

What I'm having difficulties with is deciding on how to handle the events
for each of the menu items. Generating the full menu dynamically would mean
having to create the event handlers dynamically, unless I have a fixed amount
of event handlers, which I'm not sure is the best approach. I might be wrong
here...

1. What's the best way to generate event handlers dynamically? I've read
about runtime IL generation, which makes sense but is a bit complex.

2. Using regular event handlers (hard coded), can I capture which of the
menu items is being clicked and what component is the menu being displayed
on? i.e., if the menu is being displayed after a right click on a button or a
picture box, can I: 1) know if it's the picture box or the button displaying
the menu? and 2) can I know what menu/sub menu is being clicked?

Any ideas will be greatly appreciated.

Thanks in advance,

</edwin>

I would use AddHandler (in VB) to add a single handler to the
ItemClicked event on all of the menus. The event handler has two
arguments. 'sender' is the object (menu) that raised the event. 'e'
is the event information, and e.ClickedItem will be the item in the
'sender' menu that was clicked.

You can use various properties of the menu items (Tag, Name, Text) to
identify which item it is.

If the same set of menus are connected to multiple controls, then you
might need to handle the Opening event of the top menu and capture its
owning control at that time.
 
E

eaguilar

Thanks a lot for the response Jack.

I'm already using AddHandler to create the handlers (actually a single
handler for all menuItem objects' Click event now), and it works fine.

Like you say, a single set of menues (a single
System.Windows.Forms.ContextMenuStrip object) is connected to multiple
controls, and I got currentMenu.SourceControl.Name to work for all "first
level" menu entries. For deeper "nesting" in the menues, the property returns
Nothing for some reason.

Any ideas what I'm doing wrong?

Here's a little sample of the code:

' To save the menu and access its SourceControl property
Public currentMenu As System.Windows.Forms.ContextMenuStrip

' The actual menu, which is then assigned to the
' currentMenu object, for persistence within the class
menuZone = New System.Windows.Forms.ContextMenuStrip(Me.components)
currentMenu = menuZone

' A "second level" item, under menuZone
' Others like this one are also created
menuCountry = New System.Windows.Forms.ToolStripMenuItem
menuCountry.Name = "menuCountry"
menuCountry.Size = New System.Drawing.Size(152, 22)
menuCountry.Text = "Country"
AddHandler menuCountry.Click, AddressOf Me.menu_Click

' ^^ Others like this one are also created

' All "lower level" items are assigned to the first level
menuZone.Items.AddRange(New System.Windows.Forms.ToolStripItem()
{menuCountry, menuCounty, menuBorrough})


'
' Now the handler
'
Private Sub menu_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs)
Try
txtOutput.Text += sender.Name + vbNewLine
txtOutput.Text += currentMenu.SourceControl.Name + vbNewLine
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub

This works for "first level" menu items, but for lower level items,
SourceControl turns to Nothign for some reason...

Any ideas?

Many thanks in advance,

</edwin>
 
E

eaguilar

Hi Herfried, thanks for the reply.

I am already using AddHandler to create que events and their handlers, and
the sender property to access some info. Both are working fine.

SourceControl property is partially working - for "Level 1" entry objects I
can access and display the name of the control triggering the menu, but for
"higher" level entries, SourceControl returns Nothing for some reason...

Any ideas? I could send code samples if needed...

Thanks in advance again,
 
J

Jack Jackson

Thanks a lot for the response Jack.

I'm already using AddHandler to create the handlers (actually a single
handler for all menuItem objects' Click event now), and it works fine.

Like you say, a single set of menues (a single
System.Windows.Forms.ContextMenuStrip object) is connected to multiple
controls, and I got currentMenu.SourceControl.Name to work for all "first
level" menu entries. For deeper "nesting" in the menues, the property returns
Nothing for some reason.

Any ideas what I'm doing wrong?

Here's a little sample of the code:

' To save the menu and access its SourceControl property
Public currentMenu As System.Windows.Forms.ContextMenuStrip

' The actual menu, which is then assigned to the
' currentMenu object, for persistence within the class
menuZone = New System.Windows.Forms.ContextMenuStrip(Me.components)
currentMenu = menuZone

' A "second level" item, under menuZone
' Others like this one are also created
menuCountry = New System.Windows.Forms.ToolStripMenuItem
menuCountry.Name = "menuCountry"
menuCountry.Size = New System.Drawing.Size(152, 22)
menuCountry.Text = "Country"
AddHandler menuCountry.Click, AddressOf Me.menu_Click

' ^^ Others like this one are also created

' All "lower level" items are assigned to the first level
menuZone.Items.AddRange(New System.Windows.Forms.ToolStripItem()
{menuCountry, menuCounty, menuBorrough})


'
' Now the handler
'
Private Sub menu_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs)
Try
txtOutput.Text += sender.Name + vbNewLine
txtOutput.Text += currentMenu.SourceControl.Name + vbNewLine
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub

This works for "first level" menu items, but for lower level items,
SourceControl turns to Nothign for some reason...

Any ideas?

Many thanks in advance,

</edwin>

You don't get a SourceControl for lower level menu items because their
'parent' is not a control, it is another menu. Only the top level
will have SourceControl set.

That is why I suggested you subscribe to the top level menu's Opening
event and capture the SourceControl there.

You could also try to walk back through the levels to get to the root
ContextMenuStrip by looking at the Owner and/or OwnerItem properties,
although I have found situations in which I could not find the next
level up.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top