Как можно удалить все обработчики событий из 'Click' события 'Button'?

У меня есть кнопочный элемент управления, и мне нужно будет удалить все обработчики событий, прикрепленные к его событию Click.

Как это возможно?

Button button = GetButton();
button.Click.RemoveAllEventHandlers();

Ответ 1

Вы не можете, в основном - по крайней мере, не без размышлений и много грубости.

События строго "подписываются, отписываются" - вы не можете отписаться от другого обработчика, больше, чем вы можете изменить ссылку на кого-то другого.

Ответ 2

Примечание. Поскольку вопрос, по которому я опубликовал свой первоначальный ответ, был закрыт как дубликат этого вопроса, опубликовать улучшенную версию моего ответа здесь. Этот ответ применим только к WPF. Он не будет работать в Windows Forms или любой другой инфраструктуре пользовательского интерфейса.

Ниже приведен полезный метод утилиты для удаления всех обработчиков событий, подписанных на маршрутизируемое событие для данного элемента. Вы можете тривиально преобразовать это в метод расширения, если хотите.

/// <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: /questions/24971/how-would-it-be-possible-to-remove-all-event-handlers-of-the-click-event-of-a-button/181999#181999
    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);
}

Затем вы можете легко вызвать этот метод утилиты для своей кнопки Click event:

RemoveRoutedEventHandlers(button, Button.ClickEvent);

Изменить: я скопировал исправление ошибок реализованное короной, что останавливает метод отбрасывания NullReferenceException, когда нет события обработчики подписываются. Кредит (и upvotes) должен пойти на их ответ.

Ответ 3

Просто хотел немного погладить рутину Дугласа, что мне очень понравилось. Я обнаружил, что мне нужно добавить дополнительную проверку null в eventHandlersStore для обработки любых случаев, когда переданный элемент еще не подключался к событиям.

/// <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);
}

Ответ 4

Я нашел этот ответ здесь в StackOverflow:

Как удалить все обработчики событий из элемента управления

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]);
}

Какой оригинальный плакат найден здесь:

Ответ 5

У меня была ошибка с ошибкой с кодом Jamie Dixon, опубликованным для входа в аккаунт, не имеющий события Click.

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]);
    }

}

Затем небольшое изменение и оно должно быть для любого события.

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]);
    }

}

Я предполагаю, что это может быть улучшено, но оно работает для моих текущих потребностей. Надеюсь, это полезно для кого-то.