How would it be possible to remove all event handlers of the 'Click' event of a 'Button'?
Solution 1
You can't, basically - at least not without reflection and a lot of grubbiness.
Events are strictly "subscribe, unsubscribe" - you can't unsubscribe someone else's handler, any more than you can change someone else's reference to an object.
Solution 2
Note: Since the question on which I posted my original answer was closed as a duplicate of this question, I'm cross-posting an improved version of my answer here. This answer only applies to WPF. It will not work on Windows Forms or any other UI framework.
The below is a helpful utility method for removing all event handlers subscribed to a routed event on a given element. You can trivially convert this to an extension method if you like.
/// <summary>
/// Removes all event handlers subscribed to the specified routed event from the specified element.
/// </summary>
/// <param name="element">The UI element on which the routed event is defined.</param>
/// <param name="routedEvent">The routed event for which to remove the event handlers.</param>
public static void RemoveRoutedEventHandlers(UIElement element, RoutedEvent routedEvent)
{
// Get the EventHandlersStore instance which holds event handlers for the specified element.
// The EventHandlersStore class is declared as internal.
var eventHandlersStoreProperty = typeof(UIElement).GetProperty(
"EventHandlersStore", BindingFlags.Instance | BindingFlags.NonPublic);
object eventHandlersStore = eventHandlersStoreProperty.GetValue(element, null);
// If no event handlers are subscribed, eventHandlersStore will be null.
// Credit: https://stackoverflow.com/a/16392387/1149773
if (eventHandlersStore == null)
return;
// Invoke the GetRoutedEventHandlers method on the EventHandlersStore instance
// for getting an array of the subscribed event handlers.
var getRoutedEventHandlers = eventHandlersStore.GetType().GetMethod(
"GetRoutedEventHandlers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var routedEventHandlers = (RoutedEventHandlerInfo[])getRoutedEventHandlers.Invoke(
eventHandlersStore, new object[] { routedEvent });
// Iteratively remove all routed event handlers from the element.
foreach (var routedEventHandler in routedEventHandlers)
element.RemoveHandler(routedEvent, routedEventHandler.Handler);
}
You could then easily call this utility method for your button's Click
event:
RemoveRoutedEventHandlers(button, Button.ClickEvent);
Edit: I've copied the bug fix implemented by corona, which stops the method from throwing a NullReferenceException
when no event handlers are subscribed. Credit (and upvotes) should go to their answer.
Solution 3
Just wanted to expand on Douglas' routine slightly, which I liked very much. I found I needed to add the extra null check to eventHandlersStore to handle any cases where the element passed didn't have any events attached yet.
/// <summary>
/// Removes all event handlers subscribed to the specified routed event from the specified element.
/// </summary>
/// <param name="element">The UI element on which the routed event is defined.</param>
/// <param name="routedEvent">The routed event for which to remove the event handlers.</param>
public static void RemoveRoutedEventHandlers(UIElement element, RoutedEvent routedEvent)
{
// Get the EventHandlersStore instance which holds event handlers for the specified element.
// The EventHandlersStore class is declared as internal.
var eventHandlersStoreProperty = typeof(UIElement).GetProperty(
"EventHandlersStore", BindingFlags.Instance | BindingFlags.NonPublic);
object eventHandlersStore = eventHandlersStoreProperty.GetValue(element, null);
if (eventHandlersStore == null) return;
// Invoke the GetRoutedEventHandlers method on the EventHandlersStore instance
// for getting an array of the subscribed event handlers.
var getRoutedEventHandlers = eventHandlersStore.GetType().GetMethod(
"GetRoutedEventHandlers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var routedEventHandlers = (RoutedEventHandlerInfo[])getRoutedEventHandlers.Invoke(
eventHandlersStore, new object[] { routedEvent });
// Iteratively remove all routed event handlers from the element.
foreach (var routedEventHandler in routedEventHandlers)
element.RemoveHandler(routedEvent, routedEventHandler.Handler);
}
Solution 4
I found this answer here on StackOverflow:
How to remove all event handlers from a control
private void RemoveClickEvent(Button b)
{
FieldInfo f1 = typeof(Control).GetField("EventClick",
BindingFlags.Static | BindingFlags.NonPublic);
object obj = f1.GetValue(b);
PropertyInfo pi = b.GetType().GetProperty("Events",
BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList list = (EventHandlerList)pi.GetValue(b, null);
list.RemoveHandler(obj, list[obj]);
}
Which the origional poster found here:
The Light
I love thinking and writing .NET applications and have over 13 years experience + MCPD, MCTS, MCAD, MCP.
Updated on July 09, 2022Comments
-
The Light almost 2 years
I have a button control, and I'd need to remove all the event handlers attached to its Click event.
How would that be possible?
Button button = GetButton(); button.Click.RemoveAllEventHandlers();
-
Jon Skeet almost 13 yearsThat's implementation-specific though - the answer appeared in 2008, I wouldn't even like to say whether it will work on .NET 4. It's a really bad idea to rely on things like this.
-
The Light almost 13 yearsthat's exactly what I want; unsubscribing someone else's handler! I'm using a custom button from a thirdparty and would like to remove the author's click event handler...
-
Jon Skeet almost 13 years@William: You can't, basically - not without relying on implementation details and reflection, unless the custom button exposes such behaviour. The encapsulation of events is that handlers don't interfere with each other.
-
The Light almost 13 yearsthanks but this line always returns null: typeof(Control).GetField("EventClick", BindingFlags.Static | BindingFlags.NonPublic);
-
Jon Skeet almost 13 years@William: 1) You're using the control in a way for which it was not designed, and could thus cause problems very easily. 2) Your workaround could well break in a future version. 3) It won't work in low-trust environments. Basically, how fragile do you want your code to be?
-
The Light almost 13 yearsgood points; thinking about design not just implementation. I've created my own custom button instead and copied what I needed (icon, etc) from the original.
-
Dinis Cruz almost 12 yearsI implemented a similar solution that works using a couple more internal methods of the EventHandlerList object. See answer stackoverflow.com/questions/11031149/…
-
Syaiful Nizam Yahya over 10 yearsIs it possible to do this in silverlight? silverlight doesnt have RoutedEventHandlerInfo.
-
Will about 9 years... Could this be expanded into an extension method? That would be pretty sweet...