Question

i want to Remove a specific handler from a WinForms Control with VisualBasic .Net.

For example:

I have a Windows.Forms.ToolStripButton with a Click-Event registered to save Data.

Now i need to remove this Event from the Button, but I have no access to the registerd Event-Method !! I pnly know which Handle is used. In my case "Click"

For better understanding: I have to write an extension for a GridView. When multiple lines are selected, i have to integrate mass-editing for the selected Rows. For that i need to register a new click event for the savebutton and remove the old one. But i have no acces to the Address of the event handler. I only have the button with the registered Event

This is what i tried so far:

Dim btnSaveEventInfo = btnSave.GetType.GetEvent("Click")
Dim method = Activator.CreateInstance(btnSaveEventInfo.GetType, True)

RemoveHandler btnSave.Click, method

This throws InvalidCastException

Anyone any idea?

Gr33tz gangfish

Was it helpful?

Solution

Wow. That was a much harder nut to crack than I expected. It is possible to do this, but I'll warn you, it's a real mess. Ultimately, what you need to do is to get the Delegate object for the event. Once you have that, removing the event handler is easy because you can just do something like this:

Dim d As [Delegate] = ' ... (we'll figure this out later)
For Each handler As [Delegate] In d.GetInvocationList()
    RemoveHandler btnSave.Click, DirectCast(handler, EventHandler)
Next

However, getting the delegate for the event is particularly difficult. Reflection makes it easy to get an EventInfo object for any desired event, but it does not provide a simple way to get the delegate for that event. Technically speaking, there is no way, for sure, to always get the delegate, since events encapsulate their delegate privately and they could be implemented in any custom way. But, since most events are implemented in the "normal" way, it is possible with reflection to get the private delegate hidden in the object. Of course, all of that will break if Microsoft ever decides to change the "normal" way in which it compiles events, but since that's the best option you've got, you're stuck with it.

To further complicate things, the event handlers for WinForm controls are implemented in a custom way (albeit, all consistently with each other), so to get the delegate for a control's event is even more complex. There is an excellent write-up on all of this on this page of Bob Powell's blog. It's too lengthy for me to repeat all of the information here, but I have used it to put together a VB.NET example of how to solve your particular situation:

Dim currType As Type = btnSave.GetType()
Dim eventFieldInfo As FieldInfo = Nothing
Do
    eventFieldInfo = currType.GetField("EventClick", BindingFlags.Static Or BindingFlags.Instance Or BindingFlags.NonPublic)
    If eventFieldInfo Is Nothing Then
        currType = currType.BaseType
    End If
Loop While eventFieldInfo Is Nothing

Dim ehl As EventHandlerList = DirectCast(btnSave.GetType().GetProperty("Events", BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.FlattenHierarchy).GetValue(btnSave), EventHandlerList)
Dim d As [Delegate] = ehl(eventFieldInfo.GetValue(btnSave))

For Each handler As EventHandler In d.GetInvocationList()
    RemoveHandler btnSave.Click, handler
Next

See the link above for the explanation of why and how that works. However, as I said, this is quite ugly and could break in future versions of .NET. Therefore, if there is any other way for you to do this, it would be better to use those alternatives. For instance, if you have control over the code that adds the event handler, you could just keep a separate reference to that delegate so you could remove it later, for instance:

Dim myClickHandler As EventHandler = AddressOf btnSave_Click
AddHandler btnSave.Click, myClickHandler

' ...

RemoveHandler btnSave.Click, myClickHandler
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top