如何删除"Button"的"Click"事件的所有事件处理程序?

The*_*ght 16 .net c# events event-handling

我有一个按钮控件,我需要删除附加到其Click事件的所有事件处理程序.

怎么可能呢?

Button button = GetButton();
button.Click.RemoveAllEventHandlers();
Run Code Online (Sandbox Code Playgroud)

Dou*_*las 37

注意:由于我发布原始答案的问题已作为此问题的副本而关闭,因此我在此处交叉发布了我的答案的改进版本.这个答案仅适用于WPF.它不适用于Windows窗体或任何其他UI框架.

下面是一个有用的实用程序方法,用于删除订阅给定元素上的路由事件的所有事件处理程序.如果您愿意,可以将其简单地转换为扩展方法.

/// <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);
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以轻松地为按钮的Click事件调用此实用程序方法:

RemoveRoutedEventHandlers(button, Button.ClickEvent);
Run Code Online (Sandbox Code Playgroud)

编辑:我已经复制了电晕实现错误修复,NullReferenceException当没有订阅任何事件处理程序时,它会停止抛出方法.信用(和赞成)应该回答他们的答案.


小智 12

只是想略微扩展道格拉斯的常规,我非常喜欢.我发现我需要在eventHandlersStore中添加额外的null检查来处理传递的元素还没有附加任何事件的任何情况.

/// <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);
}
Run Code Online (Sandbox Code Playgroud)


Jon*_*eet 10

你不能,基本上 - 至少不是没有反思和很多贪婪.

事件严格地"订阅,取消订阅" - 您不能取消订阅其他人的处理程序,除了您可以更改其他人对对象的引用.

  • @William:1)你正在以一种没有设计的方式使用控件,因此很容易引起问题.2)您的解决方法可能会在未来的版本中突破.3)它不适用于低信任环境.基本上,您希望代码有多脆弱? (4认同)

Jam*_*xon 5

我在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]);
}
Run Code Online (Sandbox Code Playgroud)

这里找到的原始海报:

  • 这是特定于实现的 - 答案出现在2008年,我甚至不想说它是否适用于.NET 4.依靠这样的事情是一个非常糟糕的主意. (2认同)
  • 谢谢,但这行总是返回null:typeof(Control).GetField("EventClick",BindingFlags.Static | BindingFlags.NonPublic); (2认同)