如何从事件中删除所有事件处理程序
时间:2020-03-06 14:20:52 来源:igfitidea点击:
要在控件上创建新的事件处理程序,我们可以执行此操作
c.Click += new EventHandler(mainFormButton_Click);
或者这个
c.Click += mainFormButton_Click;
并删除事件处理程序,我们可以执行此操作
c.Click -= mainFormButton_Click;
但是,如何从事件中删除所有事件处理程序?
解决方案
通过删除所有事件处理程序:
Directly no, in large part because you
cannot simply set the event to null.
Indirectly, you could make the actual
event private and create a property
around it that tracks all of the
delegates being added/subtracted to
it.
Take the following:
List<EventHandler> delegates = new List<EventHandler>();
private event EventHandler MyRealEvent;
public event EventHandler MyEvent
{
add
{
MyRealEvent += value;
delegates.Add(value);
}
remove
{
MyRealEvent -= value;
delegates.Remove(value);
}
}
public void RemoveAllEvents()
{
foreach(EventHandler eh in delegates)
{
MyRealEvent -= eh;
}
delegates.Clear();
}
如果确实需要这样做,则需要进行反思,并且需要花费一些时间。事件处理程序在控件内部的事件到委托映射中进行管理。我们需要
- 在控件实例中反映并获取此映射。
总之,很多工作。从理论上讲是有可能的……我从来没有尝试过这样的事情。
查看我们是否可以更好地控制/约束控件的"订阅-取消订阅"阶段。
我在MSDN论坛上找到了解决方案。下面的示例代码将从" button1"中删除所有" Click"事件。
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button1.Click += button1_Click;
button1.Click += button1_Click2;
button2.Click += button2_Click;
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Hello");
}
private void button1_Click2(object sender, EventArgs e)
{
MessageBox.Show("World");
}
private void button2_Click(object sender, EventArgs e)
{
RemoveClickEvent(button1);
}
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]);
}
}
}
删除不存在的事件处理程序不会有任何危害。因此,如果我们知道可能有什么处理程序,则只需删除所有这些处理程序即可。我只是有类似的情况。在某些情况下这可能会有所帮助。
喜欢:
// Add handlers...
if (something)
{
c.Click += DoesSomething;
}
else
{
c.Click += DoesSomethingElse;
}
// Remove handlers...
c.Click -= DoesSomething;
c.Click -= DoesSomethingElse;
我刚刚发现如何在设置WinForms控件的属性时暂停事件。它将删除控件中的所有事件:
namespace CMessWin05
{
public class EventSuppressor
{
Control _source;
EventHandlerList _sourceEventHandlerList;
FieldInfo _headFI;
Dictionary<object, Delegate[]> _handlers;
PropertyInfo _sourceEventsInfo;
Type _eventHandlerListType;
Type _sourceType;
public EventSuppressor(Control control)
{
if (control == null)
throw new ArgumentNullException("control", "An instance of a control must be provided.");
_source = control;
_sourceType = _source.GetType();
_sourceEventsInfo = _sourceType.GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic);
_sourceEventHandlerList = (EventHandlerList)_sourceEventsInfo.GetValue(_source, null);
_eventHandlerListType = _sourceEventHandlerList.GetType();
_headFI = _eventHandlerListType.GetField("head", BindingFlags.Instance | BindingFlags.NonPublic);
}
private void BuildList()
{
_handlers = new Dictionary<object, Delegate[]>();
object head = _headFI.GetValue(_sourceEventHandlerList);
if (head != null)
{
Type listEntryType = head.GetType();
FieldInfo delegateFI = listEntryType.GetField("handler", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo keyFI = listEntryType.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo nextFI = listEntryType.GetField("next", BindingFlags.Instance | BindingFlags.NonPublic);
BuildListWalk(head, delegateFI, keyFI, nextFI);
}
}
private void BuildListWalk(object entry, FieldInfo delegateFI, FieldInfo keyFI, FieldInfo nextFI)
{
if (entry != null)
{
Delegate dele = (Delegate)delegateFI.GetValue(entry);
object key = keyFI.GetValue(entry);
object next = nextFI.GetValue(entry);
Delegate[] listeners = dele.GetInvocationList();
if(listeners != null && listeners.Length > 0)
_handlers.Add(key, listeners);
if (next != null)
{
BuildListWalk(next, delegateFI, keyFI, nextFI);
}
}
}
public void Resume()
{
if (_handlers == null)
throw new ApplicationException("Events have not been suppressed.");
foreach (KeyValuePair<object, Delegate[]> pair in _handlers)
{
for (int x = 0; x < pair.Value.Length; x++)
_sourceEventHandlerList.AddHandler(pair.Key, pair.Value[x]);
}
_handlers = null;
}
public void Suppress()
{
if (_handlers != null)
throw new ApplicationException("Events are already being suppressed.");
BuildList();
foreach (KeyValuePair<object, Delegate[]> pair in _handlers)
{
for (int x = pair.Value.Length - 1; x >= 0; x--)
_sourceEventHandlerList.RemoveHandler(pair.Key, pair.Value[x]);
}
}
}
}
我找到了这个答案,它几乎可以满足我的需求。感谢SwDevMan81上课。我已经对其进行了修改,以允许抑制和恢复单个方法,并且我认为应该在此处发布。
// This class allows you to selectively suppress event handlers for controls. You instantiate
// the suppressor object with the control, and after that you can use it to suppress all events
// or a single event. If you try to suppress an event which has already been suppressed
// it will be ignored. Same with resuming; you can resume all events which were suppressed,
// or a single one. If you try to resume an un-suppressed event handler, it will be ignored.
//cEventSuppressor _supButton1 = null;
//private cEventSuppressor SupButton1 {
// get {
// if (_supButton1 == null) {
// _supButton1 = new cEventSuppressor(this.button1);
// }
// return _supButton1;
// }
//}
//private void button1_Click(object sender, EventArgs e) {
// MessageBox.Show("Clicked!");
//}
//private void button2_Click(object sender, EventArgs e) {
// SupButton1.Suppress("button1_Click");
//}
//private void button3_Click(object sender, EventArgs e) {
// SupButton1.Resume("button1_Click");
//}
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Windows.Forms;
using System.ComponentModel;
namespace Crystal.Utilities {
public class cEventSuppressor {
Control _source;
EventHandlerList _sourceEventHandlerList;
FieldInfo _headFI;
Dictionary<object, Delegate[]> suppressedHandlers = new Dictionary<object, Delegate[]>();
PropertyInfo _sourceEventsInfo;
Type _eventHandlerListType;
Type _sourceType;
public cEventSuppressor(Control control) {
if (control == null)
throw new ArgumentNullException("control", "An instance of a control must be provided.");
_source = control;
_sourceType = _source.GetType();
_sourceEventsInfo = _sourceType.GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic);
_sourceEventHandlerList = (EventHandlerList)_sourceEventsInfo.GetValue(_source, null);
_eventHandlerListType = _sourceEventHandlerList.GetType();
_headFI = _eventHandlerListType.GetField("head", BindingFlags.Instance | BindingFlags.NonPublic);
}
private Dictionary<object, Delegate[]> BuildList() {
Dictionary<object, Delegate[]> retval = new Dictionary<object, Delegate[]>();
object head = _headFI.GetValue(_sourceEventHandlerList);
if (head != null) {
Type listEntryType = head.GetType();
FieldInfo delegateFI = listEntryType.GetField("handler", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo keyFI = listEntryType.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo nextFI = listEntryType.GetField("next", BindingFlags.Instance | BindingFlags.NonPublic);
retval = BuildListWalk(retval, head, delegateFI, keyFI, nextFI);
}
return retval;
}
private Dictionary<object, Delegate[]> BuildListWalk(Dictionary<object, Delegate[]> dict,
object entry, FieldInfo delegateFI, FieldInfo keyFI, FieldInfo nextFI) {
if (entry != null) {
Delegate dele = (Delegate)delegateFI.GetValue(entry);
object key = keyFI.GetValue(entry);
object next = nextFI.GetValue(entry);
if (dele != null) {
Delegate[] listeners = dele.GetInvocationList();
if (listeners != null && listeners.Length > 0) {
dict.Add(key, listeners);
}
}
if (next != null) {
dict = BuildListWalk(dict, next, delegateFI, keyFI, nextFI);
}
}
return dict;
}
public void Resume() {
}
public void Resume(string pMethodName) {
//if (_handlers == null)
// throw new ApplicationException("Events have not been suppressed.");
Dictionary<object, Delegate[]> toRemove = new Dictionary<object, Delegate[]>();
// goes through all handlers which have been suppressed. If we are resuming,
// all handlers, or if we find the matching handler, add it back to the
// control's event handlers
foreach (KeyValuePair<object, Delegate[]> pair in suppressedHandlers) {
for (int x = 0; x < pair.Value.Length; x++) {
string methodName = pair.Value[x].Method.Name;
if (pMethodName == null || methodName.Equals(pMethodName)) {
_sourceEventHandlerList.AddHandler(pair.Key, pair.Value[x]);
toRemove.Add(pair.Key, pair.Value);
}
}
}
// remove all un-suppressed handlers from the list of suppressed handlers
foreach (KeyValuePair<object, Delegate[]> pair in toRemove) {
for (int x = 0; x < pair.Value.Length; x++) {
suppressedHandlers.Remove(pair.Key);
}
}
//_handlers = null;
}
public void Suppress() {
Suppress(null);
}
public void Suppress(string pMethodName) {
//if (_handlers != null)
// throw new ApplicationException("Events are already being suppressed.");
Dictionary<object, Delegate[]> dict = BuildList();
foreach (KeyValuePair<object, Delegate[]> pair in dict) {
for (int x = pair.Value.Length - 1; x >= 0; x--) {
//MethodInfo mi = pair.Value[x].Method;
//string s1 = mi.Name; // name of the method
//object o = pair.Value[x].Target;
// can use this to invoke method pair.Value[x].DynamicInvoke
string methodName = pair.Value[x].Method.Name;
if (pMethodName == null || methodName.Equals(pMethodName)) {
_sourceEventHandlerList.RemoveHandler(pair.Key, pair.Value[x]);
suppressedHandlers.Add(pair.Key, pair.Value);
}
}
}
}
}
}
我实际上正在使用此方法,并且效果很好。这里的Aeonhack编写的代码给我"启发"。
Public Event MyEvent()
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If MyEventEvent IsNot Nothing Then
For Each d In MyEventEvent.GetInvocationList ' If this throws an exception, try using .ToArray
RemoveHandler MyEvent, d
Next
End If
End Sub
MyEventEvent字段是隐藏的,但它确实存在。
调试时,我们可以看到d.target是实际处理事件的对象,而d.method是其方法。我们只需要删除它。
效果很好。没有更多的对象由于事件处理程序而没有被GC处理。

