2011-07-26 154 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); 
} 
相關問題