Attach handlers to ToolStripMenuItems. Got stacked, Please Help

  • Thread starter Thread starter pamelafluente
  • Start date Start date
P

pamelafluente

Hello. I have written the following code (this code is on a control):

Sub InitSomething()
mSet(me.FindForm)
End Sub

Sub mSet(ByVal f As Control)
For Each t As Control In f.Controls
If TypeOf t Is ToolStripMenuItem Then
AddHandler t.MouseEnter, AddressOf Me.SetSemaforoMenu
End If
mSet(t)
Next t
End Sub

because I wanted to attach an handler to the MouseEnter event of all
the
ToolStripMenuItems which are on the form which contains this control.

But this gives error because it says ToolStripMenuItem is not a
control.
Well how do I iterate on my ToolStripMenuItems. Is there a container or
what ?

-P
 
If you search this form for my earlier post titled
"Re: tootip or microhelp for menu items", I posted
code that will add event handlers for one level down.
I couldn't figure out how to do it two or more
levels down.

Good luck.
Robin S.
 
RobinS ÀÛ¼º:
If you search this form for my earlier post titled
"Re: tootip or microhelp for menu items", I posted
code that will add event handlers for one level down.
I couldn't figure out how to do it two or more
levels down.

Hi Robin,

thanks. For the levels there is no problem: as you can see, I am
already doing recursion. My problem is to identify the container which
contains the toolstripitems. It does not seem to be "controls".

Are you suggesting I have to first iterate on toolstrip menu and then
make recursion on toolstrip item? I must be dumb: I cannot see how to
do this apparently simple thing... :(

/P
 
The main container is a MenuStrip. Inside the menu strip, there are
ToolStripMenuItems. This gives you access to the items you can
see on the menu strip. From there, you can access DropDownItems
to get to the options that you see when you drop down. I haven't
figured out how to get to the options that show to the right for
each of the dropdown items yet. This is as far as I got:

'For each option you can see on the menu strip
For each tsItem As ToolStripMenuItem in msMenuStrip.Items
If tsItem.DropDownItems.Count > 0 then
'that entry has a dropdown
For i as Integer = 0 to item.DropDownItems.Count - 1
Dim ddItem as ToolStripItm
ddItem = item.DropDownItems(i)
'Now you can access ddItem, but I couldn't figure out
' how to get from here to the next level of dropdowns.
Next
End If
Next

You're not dumb. Nobody else answered the other post about
this, and I searched google and microsoft and couldn't find
anything telling how to enumerate through all of the levels of
a menustrip/toolstrip.

Let me know if you figure it out; I'm waiting with bated breath.

Robin S.
irish underscore songbird at comcast dot net
-------------------------------------------------------------------------------

RobinS ÀÛ¼º:
If you search this form for my earlier post titled
"Re: tootip or microhelp for menu items", I posted
code that will add event handlers for one level down.
I couldn't figure out how to do it two or more
levels down.

Hi Robin,

thanks. For the levels there is no problem: as you can see, I am
already doing recursion. My problem is to identify the container which
contains the toolstripitems. It does not seem to be "controls".

Are you suggesting I have to first iterate on toolstrip menu and then
make recursion on toolstrip item? I must be dumb: I cannot see how to
do this apparently simple thing... :(

/P
 
RobinS ha scritto:
The main container is a MenuStrip. Inside the menu strip, there are
ToolStripMenuItems. This gives you access to the items you can
see on the menu strip. From there, you can access DropDownItems
to get to the options that you see when you drop down. I haven't
figured out how to get to the options that show to the right for
each of the dropdown items yet. This is as far as I got:

'For each option you can see on the menu strip
For each tsItem As ToolStripMenuItem in msMenuStrip.Items
If tsItem.DropDownItems.Count > 0 then
'that entry has a dropdown
For i as Integer = 0 to item.DropDownItems.Count - 1
Dim ddItem as ToolStripItm
ddItem = item.DropDownItems(i)
'Now you can access ddItem, but I couldn't figure out
' how to get from here to the next level of dropdowns.
Next
End If
Next

You're not dumb. Nobody else answered the other post about
this, and I searched google and microsoft and couldn't find
anything telling how to enumerate through all of the levels of
a menustrip/toolstrip.

Let me know if you figure it out; I'm waiting with bated breath.


that would seem easy. Is this what you mean ?


Public Class Form1

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click

'Visit All Context MenuStrip
RecurseItems(Me.ContextMenuStrip1.Items)

End Sub

Sub RecurseItems(ByVal ToolStripItemCollection As
ToolStripItemCollection)
For Each ToolStripMenuItemChild As ToolStripMenuItem In
ToolStripItemCollection
Me.DoSomethingWithToolStripMenuItem(ToolStripMenuItemChild)
RecurseItems(ToolStripMenuItemChild.DropDownItems)
Next ToolStripMenuItemChild
End Sub

Sub DoSomethingWithToolStripMenuItem(ByVal ToolStripMenuItemChild
As ToolStripMenuItem)
MsgBox("I am on " & ToolStripMenuItemChild.Text)
End Sub

End Class
 
That's cool! I had to put in a Try/Catch to pick up
the InvalidCastException error, because I have
separator lines in my toolbars, and they are not
ToolStripMenuItems. I'm going to send this to that
other guy who was looking for this.

So did you fix your problem with adding an event handler
for Mouse Enter?

Robin S
---------------------------------


RobinS ha scritto:
The main container is a MenuStrip. Inside the menu strip, there are
ToolStripMenuItems. This gives you access to the items you can
see on the menu strip. From there, you can access DropDownItems
to get to the options that you see when you drop down. I haven't
figured out how to get to the options that show to the right for
each of the dropdown items yet. This is as far as I got:

'For each option you can see on the menu strip
For each tsItem As ToolStripMenuItem in msMenuStrip.Items
If tsItem.DropDownItems.Count > 0 then
'that entry has a dropdown
For i as Integer = 0 to item.DropDownItems.Count - 1
Dim ddItem as ToolStripItm
ddItem = item.DropDownItems(i)
'Now you can access ddItem, but I couldn't figure out
' how to get from here to the next level of dropdowns.
Next
End If
Next

You're not dumb. Nobody else answered the other post about
this, and I searched google and microsoft and couldn't find
anything telling how to enumerate through all of the levels of
a menustrip/toolstrip.

Let me know if you figure it out; I'm waiting with bated breath.


that would seem easy. Is this what you mean ?


Public Class Form1

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click

'Visit All Context MenuStrip
RecurseItems(Me.ContextMenuStrip1.Items)

End Sub

Sub RecurseItems(ByVal ToolStripItemCollection As
ToolStripItemCollection)
For Each ToolStripMenuItemChild As ToolStripMenuItem In
ToolStripItemCollection
Me.DoSomethingWithToolStripMenuItem(ToolStripMenuItemChild)
RecurseItems(ToolStripMenuItemChild.DropDownItems)
Next ToolStripMenuItemChild
End Sub

Sub DoSomethingWithToolStripMenuItem(ByVal ToolStripMenuItemChild
As ToolStripMenuItem)
MsgBox("I am on " & ToolStripMenuItemChild.Text)
End Sub

End Class
 
I am working on it. It's subtle because the contextmenu are in
components not in controls.

So recursion on the form seems tricky because "components" is private
(aarg!)

I would not suggest to use Try catch for that. It's ugly. I will try to
work out a general solution in the next minutes which consider
separators too. Warn me if there are other things that should be taken
cared of ...

-P

RobinS ha scritto:
 
Ok here is my complete proposal for the full problem: how to visit menu
and context menu with complete recursion everywhere. Please advise if I
left out somethng or there is a better way to achieve the same result.
It's possible there is some overlooking .. let me know

To avoid the separer or other item error I used the general item

To access components in the nested controls I used reflection, let me
know in case better ways... (watch ot line breaks)



Public Class Form1

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click

'This visit all the items of All the MenuStrip's in all
controls
'or subcontrols (got first) of this form
Me.RecurseMenuStrip(Me)

'This visit all the items of All the ContextMenuStrip's in all
controls
'or subcontrols of this form
Me.RecurseContextMenuStrip(Me)

End Sub

Sub RecurseMenuStrip(ByVal Control As Control)

For Each ControlChild As Control In Control.Controls
If TypeOf ControlChild Is MenuStrip Then
Dim MenuStrip As MenuStrip = DirectCast(ControlChild,
MenuStrip)
Me.RecurseToolStripItems(MenuStrip.Items)
Else
Me.RecurseMenuStrip(ControlChild)
End If
Next ControlChild

End Sub

Sub RecurseContextMenuStrip(ByVal Control As Control)

Dim FieldInfo As System.Reflection.FieldInfo =
Control.GetType.GetField("components", _

Reflection.BindingFlags.Instance Or _

Reflection.BindingFlags.NonPublic)
'for context menu in control components
If FieldInfo IsNot Nothing Then

Dim ControlComponents As System.ComponentModel.IContainer =
DirectCast(FieldInfo.GetValue(Control),
System.ComponentModel.IContainer)
If ControlComponents IsNot Nothing Then
For Each ControlChild As Control In
ControlComponents.Components
If TypeOf ControlChild Is ContextMenuStrip Then
Dim ContextMenuStrip As ContextMenuStrip =
DirectCast(ControlChild, ContextMenuStrip)

Me.RecurseToolStripItems(ContextMenuStrip.Items)
Else
Me.RecurseContextMenuStrip(ControlChild)
End If
Next ControlChild
End If
End If

'for possible context menus in custom controls
For Each ControlChild As Control In Control.Controls
Me.RecurseContextMenuStrip(ControlChild)
Next ControlChild

End Sub

Sub RecurseToolStripItems(ByVal ToolStripItemCollection As
ToolStripItemCollection)
For Each ToolStripItem As ToolStripItem In
ToolStripItemCollection

Me.DoSomethingWithToolStripItem(ToolStripItem)

If TypeOf ToolStripItem Is ToolStripMenuItem Then
Dim ToolStripMenuItem As ToolStripMenuItem =
DirectCast(ToolStripItem, ToolStripMenuItem)

Me.RecurseToolStripItems(ToolStripMenuItem.DropDownItems)
End If
Next ToolStripItem
End Sub

Sub DoSomethingWithToolStripItem(ByVal ToolStripItem As
ToolStripItem)
MsgBox("I am on " & ToolStripItem.Text)
End Sub

End Class
 
I managed to add MouseEnter and MouseLeave event
handlers to all of my menu options using your code.

Can't you just do this?

Private Sub AddMenuStripEventHandlers(ByVal msMenuStrip As MenuStrip)
RecurseItems(Me.MenuStrip.Items)
End Sub

Sub RecurseItems(ByVal ToolStripItemCollection As ToolStripItemCollection)
Try
For Each ToolStripMenuItemChild As ToolStripMenuItem _
In ToolStripItemCollection
'Debug.Print("I am on " & ToolStripMenuItemChild.Text)
If ToolStripMenuItemChild.Tag IsNot Nothing Then
AddHandler ToolStripMenuItemChild.MouseEnter, _
AddressOf MenuItem_MouseEnter
AddHandler ToolStripMenuItemChild.MouseLeave, _
AddressOf MenuItem_MouseLeave
End If
RecurseItems(ToolStripMenuItemChild.DropDownItems)
Next ToolStripMenuItemChild
Catch ex As InvalidCastException
'ignore it; you'll get this for the separator lines
End Try
End Sub

The only sticky wicket is the ToolStripMenuItemChild not being
a ToolStripMenuItem. If it's not, do you want a MouseEnter
event for it anyway? Also, I'm using the Tag in the above
example, because my MouseEnter event sets a status bar
at the bottom of the screen to whatever the Tag is, so I don't
need an event if there's no tag.

Robin S

I am working on it. It's subtle because the contextmenu are in
components not in controls.

So recursion on the form seems tricky because "components" is private
(aarg!)

I would not suggest to use Try catch for that. It's ugly. I will try to
work out a general solution in the next minutes which consider
separators too. Warn me if there are other things that should be taken
cared of ...

-P

RobinS ha scritto:
 
I'm going to ask a stupid question here. When you say context
menus, you mean the menus that show up when you right-click on
something, right?

So this code will go through all the menu items on a form,
whether it be in a menu strip at the top, a tool stip at the
top, or a context menu assigned to a control, right?

Cooooool.

Robin S
--------------------------
 
RobinS ha scritto:
I'm going to ask a stupid question here. When you say context
menus, you mean the menus that show up when you right-click on
something, right?

Is that's how microsoft call them :)
So this code will go through all the menu items on a form,
whether it be in a menu strip at the top, a tool stip at the
top, or a context menu assigned to a control, right?

Cooooool.

I hope it is. But I have written too many lines of codes to not be
absolutely sure this code written so hastily contains somewhere some
bugs :))

Anyway this is the form in which I am going to use it. Since I may need
to use it in several places of my programs it's just the case to have a
reusable class.

Here is a sample of use. Please let me know when discover errors ...


Public Class Form1


'EXAMPLE OF USE OF THE "MENU RECURSOR"
'=====================================

'if anyone uses this and finds bugs or improvements I'd love to
know: (e-mail address removed)

Private WithEvents MenuRecursor As New MenuRecursor

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
Me.MenuRecursor = New MenuRecursor
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click

With Me.MenuRecursor

'This visit all the items of All the MenuStrip's in all
controls
'or subcontrols (got first) of this form

.RecurseMenuStrip(Me)

'This visit all the items of All the ContextMenuStrip's in
all controls
'or subcontrols of this form

.RecurseContextMenuStrip(Me)

End With

End Sub

Sub HandleFoundToolStripMenuItem(ByVal ToolStripItem As
ToolStripItem) Handles MenuRecursor.FoundToolStripMenuItem
MsgBox("I am on " & ToolStripItem.Name)
End Sub

End Class



Public Class MenuRecursor

Public Event FoundToolStripMenuItem(ByVal ToolStripItem As
ToolStripItem)

Sub RecurseMenuStrip(ByVal Control As Control)

For Each ControlChild As Control In Control.Controls
If TypeOf ControlChild Is MenuStrip Then
Dim MenuStrip As MenuStrip = DirectCast(ControlChild,
MenuStrip)
Me.RecurseToolStripItems(MenuStrip.Items)
Else
Me.RecurseMenuStrip(ControlChild)
End If
Next ControlChild

End Sub

Sub RecurseContextMenuStrip(ByVal Control As Control)

Dim FieldInfo As System.Reflection.FieldInfo =
Control.GetType.GetField("components", _

Reflection.BindingFlags.Instance Or _

Reflection.BindingFlags.NonPublic)
'for context menu in control components
If FieldInfo IsNot Nothing Then

Dim ControlComponents As System.ComponentModel.IContainer =
DirectCast(FieldInfo.GetValue(Control),
System.ComponentModel.IContainer)
If ControlComponents IsNot Nothing Then
For Each ControlChild As Control In
ControlComponents.Components
If TypeOf ControlChild Is ContextMenuStrip Then
Dim ContextMenuStrip As ContextMenuStrip =
DirectCast(ControlChild, ContextMenuStrip)

Me.RecurseToolStripItems(ContextMenuStrip.Items)
Else
Me.RecurseContextMenuStrip(ControlChild)
End If
Next ControlChild
End If
End If

'for possible context menus in custom controls
For Each ControlChild As Control In Control.Controls
Me.RecurseContextMenuStrip(ControlChild)
Next ControlChild

End Sub

Sub RecurseToolStripItems(ByVal ToolStripItemCollection As
ToolStripItemCollection)
For Each ToolStripItem As ToolStripItem In
ToolStripItemCollection

RaiseEvent FoundToolStripMenuItem(ToolStripItem)

If TypeOf ToolStripItem Is ToolStripMenuItem Then
Dim ToolStripMenuItem As ToolStripMenuItem =
DirectCast(ToolStripItem, ToolStripMenuItem)

Me.RecurseToolStripItems(ToolStripMenuItem.DropDownItems)
End If
Next ToolStripItem
End Sub

End Class
 
RobinS ha scritto:
I tried out the menu strip stuff, and it worked like a dream.
Good.


Here is a slightly better version, working both with FORMS and CONTROLS
(forms are controls anyway). Replaced events with delegates because
more flexible. COrrected the tooltip component bug ...

When attaching to click (or similar) event of items it's more
convenient to use the itemclicked event so that we can avoid recursing
on items ....

Let me know in case of bugs

'=====================================================

Public Delegate Sub ToolStripMenuItemSub(ByVal ToolStripItem As
ToolStripItem)
Public Delegate Sub ContextMenuStripSub(ByVal ContextMenuStrip As
ContextMenuStrip)
Public Delegate Sub MenuStripSub(ByVal MenuStrip As MenuStrip)

Public Class MenuRecursor

Public ToolStripMenuItemSub As ToolStripMenuItemSub
Public ContextMenuStripSub As ContextMenuStripSub
Public MenuStripSub As MenuStripSub

Sub RecurseMenuStrip(ByVal Control As Control)

For Each ControlChild As Control In Control.Controls

If TypeOf ControlChild Is MenuStrip Then

'Invoke --------
Dim MenuStrip As MenuStrip = DirectCast(ControlChild,
MenuStrip)
If Me.MenuStripSub IsNot Nothing Then
Me.MenuStripSub(MenuStrip)
'---------------

Me.RecurseToolStripItems(MenuStrip.Items)
Else
Me.RecurseMenuStrip(ControlChild)
End If

Next ControlChild

End Sub

Sub RecurseContextMenuStrip(ByVal FormOrControl As Control)

Dim FieldInfo As System.Reflection.FieldInfo =
FormOrControl.GetType.GetField("components",
Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)

'for context menu in control components
If FieldInfo IsNot Nothing Then

Dim ControlComponents As System.ComponentModel.IContainer =
DirectCast(FieldInfo.GetValue(FormOrControl),
System.ComponentModel.IContainer)
If ControlComponents IsNot Nothing Then

For Each o As Object In ControlComponents.Components

If TypeOf o Is ContextMenuStrip Then

'Invoke --------
Dim ContextMenuStrip As ContextMenuStrip =
DirectCast(o, ContextMenuStrip)
If Me.ContextMenuStripSub IsNot Nothing Then
Me.ContextMenuStripSub(ContextMenuStrip)
'---------------


Me.RecurseToolStripItems(ContextMenuStrip.Items)

Else
If TypeOf o Is Control Then
Me.RecurseContextMenuStrip(DirectCast(o,
Control))
End If
End If

Next o
End If
End If

'for possible context menus in custom controls
For Each ControlChild As Control In FormOrControl.Controls
Me.RecurseContextMenuStrip(ControlChild)
Next ControlChild

End Sub

Sub RecurseToolStripItems(ByVal ToolStripItemCollection As
ToolStripItemCollection)

For Each ToolStripItem As ToolStripItem In
ToolStripItemCollection

'Invoke --------
If Me.ToolStripMenuItemSub IsNot Nothing Then
Me.ToolStripMenuItemSub(ToolStripItem)
'---------------

If TypeOf ToolStripItem Is ToolStripMenuItem Then
Dim ToolStripMenuItem As ToolStripMenuItem =
DirectCast(ToolStripItem, ToolStripMenuItem)

Me.RecurseToolStripItems(ToolStripMenuItem.DropDownItems)
End If

Next ToolStripItem

End Sub

End Class
 
Back
Top