2011-07-26 157 views

回答

10

你不能,基本上 - 至少不是没有反思和大量的不满。

事件严格地是“订阅,取消订阅” - 你不能取消订阅别人的处理程序,除了你可以改变别人对对象的引用。

+0

这正是我想要的;取消订阅别人的处理程序!我正在使用第三方的自定义按钮,并希望删除作者的单击事件处理程序... –

+0

@William:你基本上不能 - 除非自定义按钮公开这样的行为,否则不能不依赖实现细节和反射。事件的封装是处理程序不会相互干扰。 –

+0

使用反射它有什么问题? –

4

我在这里找到这个答案在计算器上:

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

其中的原始海报发现here

+2

虽然这是特定于实现的 - 答案出现在2008年,但我甚至不想说它是否可以在.NET 4上工作。依靠这样的事情是一个非常糟糕的主意。 –

+2

谢谢,但这行总是返回null:typeof(Control).GetField(“EventClick”,BindingFlags.Static | BindingFlags.NonPublic); –

+0

我实现了一个类似的解决方案,它使用EventHandlerList对象的更多内部方法。看到答案http://stackoverflow.com/questions/11031149/solution-to-remove-event-handler-dynamically-using-reflection-is-there-a-bett –

28

注意:由于这个问题上,我贴我的原答案已关闭as a duplicate这个问题,我交叉发布我的答案的改进版本他回覆。 此答案只适用于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); 
} 

然后,您可以方便地调用您的按钮的Click情况下,本实用方法:

RemoveRoutedEventHandlers(button, Button.ClickEvent); 

编辑:我已经复制了bug fix implemented by corona,从扔NullReferenceException在任何情况下,停止方法处理程序已订阅。信用(和upvotes)应该去他们的答案。

+0

是否有可能在silverlight做到这一点? silverlight没有RoutedEventHandlerInfo。 –

+0

...这可以扩展成扩展方法吗?这将是非常甜蜜的... – Will

1

我的代码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]); 
    } 

} 

我想这可以做得更好,但它适用于我目前的需要。希望这对某人有用。

10

只是想扩大道格拉斯的例程略,我非常喜欢。 我发现我需要将额外的空检查添加到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); 
} 
相关问题