Resolved Obtain the event handlers of a given ToolStripMenuItem

aaaron

Well-known member
Joined
Jan 23, 2011
Messages
216
Programming Experience
10+
The Component class has a property called Events that returns an EventHandlerList which provides the list of event handlers which can be used to get the event handlers for a control.

However, the ToolStripMenuItem does not inherit Component Control so I can't use that.

I been looking for a similar way to obtain the event handlers of a given ToolStripMenuItem but have been unabe to do so.

I it possible?
 
Last edited:
Solution
This works for me in .Net 8, can copy all event handlers for any component, controls and menu items alike.
VB.NET:
Private Sub CopyEvents(source As Component, target As Component)
    Dim info = GetType(Component).GetProperty("Events", BindingFlags.NonPublic Or BindingFlags.Instance)
    Dim list1 = DirectCast(info.GetValue(source), EventHandlerList)
    Dim list2 = DirectCast(info.GetValue(target), EventHandlerList)
    list2.AddHandlers(list1)
End Sub
Control doesn't have a Events property, that is inherited from Component.
 
I'm probably not using the correct terminology. I thought a Control had an Events property that it got from Component.

It would take me a little while to find the code but I'm quite sure some time ago I got the event handlers from a Control and then tried using the code on a ToolStripMenuItem and it didnot work. That was when I found out the ToolstripMenuItem did not inherit from Control.

What I need is to know is how to get the event handlers from a ToolStripMenuItem or at least to know that I can't do that.
 
You would use the same code to get Events property from any component.
 
I can't get it to work. For example I tried the following but eventField is always nothing.
Even if I change the parameter type to Control and pass a Button.

VB.NET:
 ' A function that takes a ToolStripMenuItem and uses Debug.WriteLine to list the text
 ' property of all the event handlers in it
 Public Sub ListEventHandlers(ByVal item As ToolStripMenuItem)
     ' Get the type of the item
     Dim itemType As Type = item.GetType()
     ' Get all the events declared in the type
     Dim events As EventInfo() = itemType.GetEvents()
     ' Loop through each event

     For Each eventQ As EventInfo In events
         ' Get the field that stores the event delegate
         Dim eventField As FieldInfo = itemType.GetField(eventQ.Name, BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.GetField)
         ' If the field is not null, get the value of the field
         If eventField IsNot Nothing Then
             Dim eventDelegate As [Delegate] = CType(eventField.GetValue(item), [Delegate])
             ' If the delegate is not null, get the invocation list of the delegate
             If eventDelegate IsNot Nothing Then
                 Dim handlersQ As [Delegate]() = eventDelegate.GetInvocationList()
                 ' Loop through each handler in the invocation list
                 For Each handler As [Delegate] In handlersQ
                     ' Use Debug.WriteLine to print the text property of the handler
                     Debug.WriteLine(handler.Target.ToString())
                 Next
             End If
         End If
     Next
 End Sub
 
Last edited:
You're using a different approch there, and not the Component.Events property that you asked about. Is that the same approch you're using for controls?
There is no field with same name as the event that I can see, neither for controls or other components.

Also, which .Net version are you using?
 
net8.0-windows

I have much code. Some very old. When I found the code for controls I found it does not work.

I had also tried this which does not work:
VB.NET:
  Public Sub ExtractAndAddEventHandlers(menuItemOut As ToolStripMenuItem, menuItemIn As ToolStripMenuItem)
      'Might also use BindingFlags.FlattenHierarchy
      Dim eventsOfMenuItemOut As System.Reflection.PropertyInfo = menuItemOut.GetType().GetProperty("Events", System.Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.Public)
      'Get a list of delegates
      Dim eventHandlersOfMenuItemOut As System.ComponentModel.EventHandlerList = CType(eventsOfMenuItemOut.GetValue(menuItemOut, Nothing), EventHandlerList)
      'Now do the other ToolStripMenuItem
      Dim eventsOfMenuItemIn As System.Reflection.PropertyInfo = menuItemIn.GetType().GetProperty("Events", System.Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.Public)
      Dim eventHandlersOfMenuItemIn As System.ComponentModel.EventHandlerList = CType(eventsOfMenuItemIn.GetValue(menuItemIn, Nothing), EventHandlerList)
      'Add handlers from menuItemIn to any existing handlers on menuItemOut
      eventHandlersOfMenuItemOut.AddHandlers(eventHandlersOfMenuItemIn)
      'Need to apply handlers to the ToolstripMenuItem
  End Sub
 
Last edited:
This works for me in .Net 8, can copy all event handlers for any component, controls and menu items alike.
VB.NET:
Private Sub CopyEvents(source As Component, target As Component)
    Dim info = GetType(Component).GetProperty("Events", BindingFlags.NonPublic Or BindingFlags.Instance)
    Dim list1 = DirectCast(info.GetValue(source), EventHandlerList)
    Dim list2 = DirectCast(info.GetValue(target), EventHandlerList)
    list2.AddHandlers(list1)
End Sub
 
Solution
You need the key, which is an object in a shared field in the class or any base class (event DeclaringType or base). The field naming may take several forms based off the event name. See the fields in Control class for example: Source Browser, specifically that is the s_clickEvent
VB.NET:
Dim key = GetType(Control).GetField("s_clickEvent", BindingFlags.NonPublic Or BindingFlags.Static).GetValue(Nothing)
Dim handler = list(key)
Note that you must use GetField on the specific class that declares it, shared fields are not inherited and base classes are not included in reflection search.
 
To use with a ToolstripMenuItem I had to change GetType(Control) to declaringType where
VB.NET:
 Dim declaringType As Type = eventInfo.DeclaringType

I couldn't make much sense out of the SourceBrowser which is disapoining since that seems a good thing to be able to use. I tried to see how you found s_clickEvent but couldn't.
 
Oh, I forgot you were doing ToolStripMenuItem, base class ToolStripItem defines lots of the events that Control class normally has, including Click event.
 
Before I posted I saw much code on the internet to do what you did . They all used ClickEvent or EventClick or somethig like that and when I ran the code the GetField always returned Nothing.

I wish I knew how you found s_clickEvent. Suppose I'm interested in a SelectionChange event. Is it simply s_selectionchangedEvent?

Thanks for resolving.
 
Back
Top