在WPF中,移除一個事件中已經註冊的處理方法,看似簡單,實際仍是很痛苦的一件事情。由於C#的靈活性,定義事件的方法也是多種多樣。
我本身定義了一個事件:this
public event EventHandler TestEvent;
當我想註銷這個事件上註冊的全部方法的時候,我能夠按以下的方法進行code
Delegate[] dels = TestEvent.GetInvocationList(); foreach (var d in dels) { TestEvent-= d as EventHandler; }
可是,當我想註銷一個Window上的Closed上註冊的事件時,發如今Closed上根本沒有GetInvocationList方法。這是怎麼回事呢。經過反編譯查看微軟的代碼,它的定義是這樣的:對象
public event EventHandler Closed { add{} remove{} }
坑爹啊。度娘了半天,終於找到下面的方法參考完成。事件
/// <summary> /// 清除一個對象的某個事件所掛鉤的delegate /// </summary> /// <param name="ctrl">控件對象</param> /// <param name="eventName">事件名稱,默認的</param> public static void ClearEvents(this object ctrl, string eventName = "_EventAll") { if (ctrl == null) return; BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Static; EventInfo[] events = ctrl.GetType().GetEvents(bindingFlags); if (events == null || events.Length < 1) return; for (int i = 0; i < events.Length; i++) { try { EventInfo ei = events[i]; //只刪除指定的方法,默認是_EventAll,前面加_是爲了和系統的區分,防之後雷同 if (eventName != "_EventAll" && ei.Name != eventName) continue; /******************************************************** * class的每一個event都對應了一個同名(變了,前面加了Event前綴)的private的delegate類 * 型成員變量(這點能夠用Reflector證明)。由於private成 * 員變量沒法在基類中進行修改,因此爲了可以拿到base * class中聲明的事件,要從EventInfo的DeclaringType來獲取 * event對應的成員變量的FieldInfo並進行修改 ********************************************************/ FieldInfo fi = ei.DeclaringType.GetField("Event_" + ei.Name, bindingFlags); if (fi != null) { // 將event對應的字段設置成null便可清除全部掛鉤在該event上的delegate fi.SetValue(ctrl, null); } } catch { } } }
原來的代碼中 ei.DeclaringType.GetField("Event_" + ei.Name, bindingFlags);的Event後面沒有下劃線,即寫成了「Event」,執行時發現fi是空。參考上面的代碼「_EventAll」,加一個下劃線嘗試。成功!rem