如何从事件中删除所有事件处理程序

要在控件上创建新的事件处理程序,可以这样做

c.Click += new EventHandler(mainFormButton_Click);

或者这个

c.Click += mainFormButton_Click;

要删除事件处理程序,可以这样做

c.Click -= mainFormButton_Click;

但是如何从事件中删除所有事件处理程序呢?

512954 次浏览

删除所有事件处理程序:

直接不,很大程度上是因为你 不能简单地将事件设置为空

间接地,你可以使实际 事件私有并创建属性 围绕着它,追踪所有的 被添加/删除的委托 它。< / p >

采取以下措施:

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

如果你非常非常必须这样做…这需要深思熟虑和相当长的时间。事件处理程序在控件内的事件委托映射中进行管理。你需要

  • 在控件实例中反射并获取此映射。
  • 迭代每个事件,获取委托
    • 每个委托可以依次是一系列链接的事件处理程序。调用obControl。RemoveHandler(事件处理程序)
    • 李< / ul > < / >

    简而言之,工作量很大。在理论上是可能的。我从没试过这样的东西。

    看看是否可以对订阅-退订阶段有更好的控制/纪律。

我在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
    ~MyClass()
{
if (MyEventEvent != null)
{
foreach (var d in MyEventEvent.GetInvocationList())
{
MyEventEvent -= (MyEvent)d;
}
}


}

字段MyEventEvent是隐藏的,但它确实存在。

调试时,你可以看到d.target是如何实际处理事件的对象,而d.method是它的方法。你只需要把它拿掉。

效果很好。不再有对象因为事件处理程序而被GC。

你们对自己太苛刻了。很简单:

void OnFormClosing(object sender, FormClosingEventArgs e)
{
foreach(Delegate d in FindClicked.GetInvocationList())
{
FindClicked -= (FindClickedHandler)d;
}
}

这个页面对我帮助很大。我从这里得到的代码是为了从按钮中删除单击事件。我需要删除双击事件从一些面板和单击事件从一些按钮。所以我做了一个控件扩展,它将删除某个事件的所有事件处理程序。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Reflection;
public static class EventExtension
{
public static void RemoveEvents<T>(this T target, string eventName) where T:Control
{
if (ReferenceEquals(target, null)) throw new NullReferenceException("Argument \"target\" may not be null.");
FieldInfo fieldInfo = typeof(Control).GetField(eventName, BindingFlags.Static | BindingFlags.NonPublic);
if (ReferenceEquals(fieldInfo, null)) throw new ArgumentException(
string.Concat("The control ", typeof(T).Name, " does not have a property with the name \"", eventName, "\""), nameof(eventName));
object eventInstance = fieldInfo.GetValue(target);
PropertyInfo propInfo = typeof(T).GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList list = (EventHandlerList)propInfo.GetValue(target, null);
list.RemoveHandler(eventInstance, list[eventInstance]);
}
}

现在,这个扩展名的使用。 如果您需要删除按钮的单击事件,

Button button = new Button();
button.RemoveEvents(nameof(button.EventClick));

如果需要从面板中删除双击事件,

Panel panel = new Panel();
panel.RemoveEvents(nameof(panel.EventDoubleClick));

我不是c#方面的专家,所以如果有任何错误,请原谅我,并让我知道。

斯蒂芬是对的。这很简单:

public event EventHandler<Cles_graph_doivent_etre_redessines> les_graph_doivent_etre_redessines;
public void remove_event()
{
if (this.les_graph_doivent_etre_redessines != null)
{
foreach (EventHandler<Cles_graph_doivent_etre_redessines> F_les_graph_doivent_etre_redessines in this.les_graph_doivent_etre_redessines.GetInvocationList())
{
this.les_graph_doivent_etre_redessines -= F_les_graph_doivent_etre_redessines;
}
}
}

好吧,这里有另一个解决方案来删除关联事件(如果你已经有一个方法来处理控件的事件):

EventDescriptor ed = TypeDescriptor.GetEvents(this.button1).Find("MouseDown",true);
Delegate delegate = Delegate.CreateDelegate(typeof(EventHandler), this, "button1_MouseDownClicked");
if(ed!=null)
ed.RemoveEventHandler(this.button1, delegate);

接受的答案不完整。对于声明为{add;删除;}

下面是工作代码:

public static void ClearEventInvocations(this object obj, string eventName)
{
var fi = obj.GetType().GetEventField(eventName);
if (fi == null) return;
fi.SetValue(obj, null);
}


private static FieldInfo GetEventField(this Type type, string eventName)
{
FieldInfo field = null;
while (type != null)
{
/* Find events defined as field */
field = type.GetField(eventName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic);
if (field != null && (field.FieldType == typeof(MulticastDelegate) || field.FieldType.IsSubclassOf(typeof(MulticastDelegate))))
break;


/* Find events defined as property { add; remove; } */
field = type.GetField("EVENT_" + eventName.ToUpper(), BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic);
if (field != null)
break;
type = type.BaseType;
}
return field;
}

哇。我找到了这个解决方案,但没有一个像我想要的那样有效。但这个太好了:

EventHandlerList listaEventos;


private void btnDetach_Click(object sender, EventArgs e)
{
listaEventos = DetachEvents(comboBox1);
}


private void btnAttach_Click(object sender, EventArgs e)
{
AttachEvents(comboBox1, listaEventos);
}


public EventHandlerList DetachEvents(Component obj)
{
object objNew = obj.GetType().GetConstructor(new Type[] { }).Invoke(new object[] { });
PropertyInfo propEvents = obj.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);


EventHandlerList eventHandlerList_obj = (EventHandlerList)propEvents.GetValue(obj, null);
EventHandlerList eventHandlerList_objNew = (EventHandlerList)propEvents.GetValue(objNew, null);


eventHandlerList_objNew.AddHandlers(eventHandlerList_obj);
eventHandlerList_obj.Dispose();


return eventHandlerList_objNew;
}


public void AttachEvents(Component obj, EventHandlerList eventos)
{
PropertyInfo propEvents = obj.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);


EventHandlerList eventHandlerList_obj = (EventHandlerList)propEvents.GetValue(obj, null);


eventHandlerList_obj.AddHandlers(eventos);
}

这不是对OP的回答,但我想我把它贴在这里,以防它能帮助到其他人。

  /// <summary>
/// Method to remove a (single) SocketAsyncEventArgs.Completed event handler. This is
/// partially based on information found here: http://stackoverflow.com/a/91853/253938
///
/// But note that this may not be a good idea, being very .Net implementation-dependent. Note
/// in particular use of "m_Completed" instead of "Completed".
/// </summary>
private static void RemoveCompletedEventHandler(SocketAsyncEventArgs eventArgs)
{
FieldInfo fieldInfo = typeof(SocketAsyncEventArgs).GetField("m_Completed",
BindingFlags.Instance | BindingFlags.NonPublic);
eventArgs.Completed -= (EventHandler<SocketAsyncEventArgs>)fieldInfo.GetValue(eventArgs);
}

我讨厌这里展示的任何完整的解决方案,我现在做了一个混合和测试,为任何事件处理程序工作:

public class MyMain()
public void MyMethod() {
AnotherClass.TheEventHandler += DoSomeThing;
}


private void DoSomething(object sender, EventArgs e) {
Debug.WriteLine("I did something");
AnotherClass.ClearAllDelegatesOfTheEventHandler();
}


}


public static class AnotherClass {


public static event EventHandler TheEventHandler;


public static void ClearAllDelegatesOfTheEventHandler() {


foreach (Delegate d in TheEventHandler.GetInvocationList())
{
TheEventHandler -= (EventHandler)d;
}
}
}

简单!感谢Stephen Punak。

我使用它是因为我使用一个通用的本地方法来删除委托,当设置不同的委托时,在不同的情况下调用本地方法。

有时我们不得不使用第三方控件,我们需要构建这些尴尬的解决方案。基于@Anoop Muraleedharan的答案,我创建了这个具有推理类型和ToolStripItem支持的解决方案

    public static void RemoveItemEvents<T>(this T target, string eventName)
where T : ToolStripItem
{
RemoveObjectEvents<T>(target, eventName);
}


public static void RemoveControlEvents<T>(this T target, string eventName)
where T : Control
{
RemoveObjectEvents<T>(target, eventName);
}


private static void RemoveObjectEvents<T>(T target, string Event) where T : class
{
var typeOfT = typeof(T);
var fieldInfo = typeOfT.BaseType.GetField(
Event, BindingFlags.Static | BindingFlags.NonPublic);
var provertyValue = fieldInfo.GetValue(target);
var propertyInfo = typeOfT.GetProperty(
"Events", BindingFlags.NonPublic | BindingFlags.Instance);
var eventHandlerList = (EventHandlerList)propertyInfo.GetValue(target, null);
eventHandlerList.RemoveHandler(provertyValue, eventHandlerList[provertyValue]);
}

你可以这样使用它

    var toolStripButton = new ToolStripButton();
toolStripButton.RemoveItemEvents("EventClick");


var button = new Button();
button.RemoveControlEvents("EventClick");

删除按钮的所有处理程序: save.RemoveEvents ();< / p >

public static class EventExtension
{
public static void RemoveEvents<T>(this T target) where T : Control
{
var propInfo = typeof(T).GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
var list = (EventHandlerList)propInfo.GetValue(target, null);
list.Dispose();
}
}
虽然有点晚了,但我使用了这个对我来说非常有效的链接: https://www.codeproject.com/Articles/103542/Removing-Event-Handlers-using-Reflection < / p >

这个代码的美妙之处在于它适用于所有人,WFP, Forms, Xamarin Forms。我用它来做沙玛林。请注意,只有当你不拥有这个事件(例如,一个库代码在你不关心的一些事件上崩溃)时,你才需要这种方式使用Reflection。

下面是我稍微修改过的代码:

    static Dictionary<Type, List<FieldInfo>> dicEventFieldInfos = new Dictionary<Type, List<FieldInfo>>();


static BindingFlags AllBindings
{
get { return BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; }
}


static void BuildEventFields(Type t, List<FieldInfo> lst)
{
foreach (EventInfo ei in t.GetEvents(AllBindings))
{
Type dt = ei.DeclaringType;
FieldInfo fi = dt.GetField(ei.Name, AllBindings);
if (fi != null)
lst.Add(fi);
}
}
static List<FieldInfo> GetTypeEventFields(Type t)
{
if (dicEventFieldInfos.ContainsKey(t))
return dicEventFieldInfos[t];


List<FieldInfo> lst = new List<FieldInfo>();
BuildEventFields(t, lst);
dicEventFieldInfos.Add(t, lst);
return lst;
}
static EventHandlerList GetStaticEventHandlerList(Type t, object obj)
{
MethodInfo mi = t.GetMethod("get_Events", AllBindings);
return (EventHandlerList)mi.Invoke(obj, new object[] { });
}
public static void RemoveEventHandler(object obj, string EventName = "")
{
if (obj == null)
return;


Type t = obj.GetType();
List<FieldInfo> event_fields = GetTypeEventFields(t);
EventHandlerList static_event_handlers = null;


foreach (FieldInfo fi in event_fields)
{
if (EventName != "" && string.Compare(EventName, fi.Name, true) != 0)
continue;
var eventName = fi.Name;
// After hours and hours of research and trial and error, it turns out that
// STATIC Events have to be treated differently from INSTANCE Events...
if (fi.IsStatic)
{
// STATIC EVENT
if (static_event_handlers == null)
static_event_handlers = GetStaticEventHandlerList(t, obj);


object idx = fi.GetValue(obj);
Delegate eh = static_event_handlers[idx];
if (eh == null)
continue;


Delegate[] dels = eh.GetInvocationList();
if (dels == null)
continue;


EventInfo ei = t.GetEvent(eventName, AllBindings);
foreach (Delegate del in dels)
ei.RemoveEventHandler(obj, del);
}
else
{
// INSTANCE EVENT
EventInfo ei = t.GetEvent(eventName, AllBindings);
if (ei != null)
{
object val = fi.GetValue(obj);
Delegate mdel = (val as Delegate);
if (mdel != null)
{
foreach (Delegate del in mdel.GetInvocationList())
{
ei.RemoveEventHandler(obj, del);
}
}
}
}
}
}




示例用法:RemoveEventHandler(obj, "Focused");