Как можно было бы удалить все обработчики событий «Click» для «Button»? - PullRequest
14 голосов
/ 26 июля 2011

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

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

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

Ответы [ 5 ]

33 голосов
/ 27 сентября 2012

Примечание : Поскольку вопрос, на который я разместил свой исходный ответ, был закрыт как дубликат этого вопроса, я пересылаю улучшенную версию моегоответ здесь. Этот ответ относится только к 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: 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);
}

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

RemoveRoutedEventHandlers(button, Button.ClickEvent);

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

11 голосов
/ 06 мая 2013

Просто хотел немного расширить рутину Дугласа, что мне очень понравилось.Я обнаружил, что мне нужно добавить дополнительную нулевую проверку в 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);
}
10 голосов
/ 26 июля 2011

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

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

5 голосов
/ 26 июля 2011

Я нашел этот ответ здесь в 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]);
}

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

1 голос
/ 27 февраля 2013

У меня была нулевая ошибка с кодом, опубликованным Джейми Диксоном, чтобы учесть, что у него нет события 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]);
    }

}

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

...