How would it be possible to remove all event handlers of the 'Click' event of a 'Button'?

后端 未结 6 611
孤街浪徒
孤街浪徒 2020-11-28 11:56

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 = GetB         


        
相关标签:
6条回答
  • 2020-11-28 12:29

    I was working on WinForms project where I had to remove the click-EventHandler from a ToolStripMenuItem and replace it with my own Handler. (I had to modify the action taken when a contextMenu Item was clicked)

    for me the code from user2113340 did not work. I had to modify it like this to work with ToolStripMenuItem:

        private void RemoveClickEvent(ToolStripMenuItem control)
        {
            FieldInfo eventClick = typeof(Control).GetField("EventClick", BindingFlags.NonPublic | BindingFlags.Static);
            PropertyInfo eventsProp = typeof(Component).GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
            EventHandlerList events = (EventHandlerList)eventsProp.GetValue(control, null);
            FieldInfo headInfo = events.GetType().GetField("head", BindingFlags.NonPublic | BindingFlags.Instance);
            object head = headInfo.GetValue(events);
            FieldInfo keyType = head.GetType().GetField("key", BindingFlags.NonPublic | BindingFlags.Instance);
            object key = keyType.GetValue(head);
            Delegate d1 = events[key];
            events.RemoveHandler(key, d1);
        }
    
    0 讨论(0)
  • 2020-11-28 12:35

    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:

    0 讨论(0)
  • 2020-11-28 12:44

    I had the null error issue with the code Jamie Dixon posted to take in to account not having a Click event.

    private void RemoveClickEvent(Control control)
    {
        // chenged "FieldInfo f1 = typeof(Control)" to "var f1 = b.GetType()". By changing to 
        // the type of the  passed in control we can use this for any control with a click event.
        // using var allows for null checking and lowering the chance of exceptions.
    
        var fi = control.GetType().GetField("EventClick", BindingFlags.Static | BindingFlags.NonPublic);
        if (fi != null)
        {
            object obj = fi.GetValue(control);
            PropertyInfo pi = control.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
            EventHandlerList list = (EventHandlerList)pi.GetValue(control, null);
            list.RemoveHandler(obj, list[obj]);
        }
    
    }
    

    Then a small change and it should be for any event.

    private void RemoveClickEvent(Control control, string theEvent)
    {
        // chenged "FieldInfo f1 = typeof(Control)" to "var f1 = b.GetType()". By changing to 
        // the type of the  passed in control we can use this for any control with a click event.
        // using var allows for null checking and lowering the chance of exceptions.
    
        var fi = control.GetType().GetField(theEvent, BindingFlags.Static | BindingFlags.NonPublic);
        if (fi != null)
        {
            object obj = fi.GetValue(control);
            PropertyInfo pi = control.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
            EventHandlerList list = (EventHandlerList)pi.GetValue(control, null);
            list.RemoveHandler(obj, list[obj]);
        }
    
    }
    

    I imagine this could be made better but it works for my current need. Hope this is useful for someone.

    0 讨论(0)
  • 2020-11-28 12:45

    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.

    0 讨论(0)
  • 2020-11-28 12:48

    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);
    }
    
    0 讨论(0)
  • 2020-11-28 12:49

    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.

    0 讨论(0)
提交回复
热议问题