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

How to remove all event handlers from an event

提问人:Carrick 提问时间:9/18/2008 最后编辑:Michael BahigCarrick 更新时间:7/27/2022 访问量:540660

问:

若要在控件上创建新的事件处理程序,可以执行此操作

c.Click += new EventHandler(mainFormButton_Click);

或者这个

c.Click += mainFormButton_Click;

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

c.Click -= mainFormButton_Click;

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

C# .NET WinForms 事件

评论

15赞 Douglas 9/27/2012
如果有人来这里寻找 WPF 解决方案,您可能需要查看此答案
3赞 alexania 2/7/2020
你不能只是设置吗?c.Click = null
12赞 Zimano 4/21/2020
这是我觉得过于复杂的事情之一。一个简单的方法显然太费力了Clear
0赞 Latency 7/29/2020
在 .NET 4.5 中,如果 List.Count > 2 让我们说,并且您尝试删除第一个委托 InvocatorList[0] == mainFormButton_Click...就像你一样..它将删除所有这些。我认为这是一个错误!

答:

92赞 Jorge Ferreira 9/18/2008 #1

删除所有事件处理程序

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

间接地,你可以使实际 事件私有并创建属性 在它周围跟踪所有 被添加/减去的委托 它。

采取以下措施:

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

评论

6赞 Gishu 9/18/2008
我以为 OP 指的是一般的 .net 控件。.其中可能无法进行这种包装。
5赞 Tom Fobear 4/14/2011
你可以派生出控件,然后它会
1赞 TN. 9/4/2013
这也导致维护两个列表,请参阅 stackoverflow.com/questions/91778/...进行重置,或 stackoverflow.com/questions/91778/...访问列表。
0赞 Noman_1 10/21/2020
可悲的是,我无法将这种行为包装在接口上,因为它不能有实例字段。
0赞 ToolmakerSteve 6/21/2021
@Noman_1 - wlith 接口 当你想要一个字段时,解决方案是改用一对方法(getter/setter)。然后由每个类来实现该方法对。
4赞 Gishu 9/18/2008 #2

如果你真的必须这样做......这需要反思和相当长的时间才能做到这一点。事件处理程序在控件内的事件到委托映射中进行管理。你需要

  • 在控制实例中反映并获取此映射。
  • 迭代每个事件,获取委托
    • 反过来,每个委托可以是一系列链接的事件处理程序。所以调用 obControl.RemoveHandler(event, handler)

简而言之,很多工作。理论上是可能的......我从来没有尝试过这样的事情。

看看是否可以对控件的订阅-取消订阅阶段进行更好的控制/约束。

195赞 xsl 9/18/2008 #3

我在MSDN论坛上找到了解决方案。下面的示例代码将从中删除所有事件。Clickbutton1

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

评论

0赞 Damien 7/29/2009
如果 button1 设置为 null,则所有事件处理程序都附加到 button1。点击正确处置?
4赞 Protector one 11/11/2011
如果我错了,请纠正我,但第一行不应该以:?如果我使用 .RemoveClickEventFieldInfo f1 = typeof(Button)GetFieldControl
2赞 Skalli 11/21/2011
这似乎不适用于 ToolStripButtons。我已将 RemoveClickEvent 中的 Button 替换为 ToolStripButton,但在调用 RemoveClickEvent 后,事件仍然存在。有没有人解决这个问题?
1赞 hello_earth 5/31/2013
MSDN 中的上述链接还建议尝试 myButton.Click += null;如果要删除所有委托(不是针对 Click,而是针对其他事件..)
2赞 Mike de Klerk 10/15/2013
@hello_earth 似乎不适用于ObservableCollection.CollectionChanged += null;
51赞 MdMx 6/23/2009 #4

删除不存在的事件处理程序不会造成任何伤害。因此,如果您知道可能存在哪些处理程序,则可以简单地删除所有处理程序。我刚刚遇到过类似的情况。在某些情况下,这可能会有所帮助。

喜欢:

// Add handlers...
if (something)
{
    c.Click += DoesSomething;
}
else
{
    c.Click += DoesSomethingElse;
}

// Remove handlers...
c.Click -= DoesSomething;
c.Click -= DoesSomethingElse;

评论

3赞 Tomislav3008 7/14/2021
是的,但问题是您可以多次执行 +=,然后无法知道有多少个相同的事件附加到处理程序。所以你不知道你需要多少个 -= 一路清除它。
0赞 ToolmakerSteve 3/31/2022
@Tomislav3008 - 我不知道任何有效的情况,你想拥有多个完全相同的处理程序 - 这通常是一个错误 - 你可能有多个相同类型的不同实例,但这里的情况不是这样 - 这个答案是只在实例内有意义的代码。事实上,我有时会在第一个代码的地方编写代码,以确保相同的处理程序不会意外添加两次。+=-=
0赞 John Glen 11/21/2022
在从控件中删除处理程序后,何时需要从类中删除该处理程序怎么办?如果在第 1 阶段使用 GridView 和附加处理程序,则在 ContainerContent 更改时会继续生成处理程序,因此需要确保同一控件在删除旧处理程序时获取新处理程序。在这种情况下,将不得不保留一张地图。
0赞 John Glen 11/21/2022
@ToolmakerSteve 如果 GridView 的项容器中有处理程序,因为需要对 GridView 中的每个元素进行控件,该怎么办?每次容器更改其内容时,都必须交换处理程序。
0赞 ToolmakerSteve 11/21/2022
@JohnGlen - 我必须看到特定的代码,这超出了这些评论的范围。除非我误解了,否则我将有一个处理程序,该项目作为参数传递。我并不声称这涵盖了所有的可能性,但它足以满足我的需求。我同意网格项可能比列表视图更棘手。IIRC,在一个案例中,我在每个单元格的视图模型数据中添加了一个字段,页面具有从该单元格的 ID 到 View 的映射。因此,我可以传递一个参数,并找到相应的视图。(我的 ID 是 ,其中大于 max # 列。int IDintN * row + columnN
3赞 SwDevMan81 10/21/2009 #5

我刚刚找到了如何在设置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]);
            }
        }

    }
}

评论

2赞 Michael 12/30/2014
这非常有用,但有一件事需要更改:在 Resume() 中,您以相反的顺序重新添加处理程序(我假设它是从 Suppress 复制/粘贴的,您希望在其中向后工作,以免弄乱您正在迭代的集合)。有些代码依赖于以给定顺序触发的处理程序,因此不应弄乱它。
-4赞 Francine 3/5/2010 #6

我找到了这个答案,它几乎符合我的需求。感谢 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);
                    }
                }
            }
        }
    } 
}

评论

8赞 Tri Q Tran 6/25/2010
这是一个复杂的解决方案,永远不应该在工业级软件中使用。最好的方法是如前所述:管理好您的活动订阅和取消订阅,您将永远不会遇到此类问题。
0赞 Tiago Freitas Leal 12/28/2014
我同意我们不应该使用反射来取消连接事件,事件订阅和取消订阅应该由应用程序管理。我认为辩论中的问题应该在 DEBUG 时使用,以找出我们是否在说什么。对于您正在重构的旧应用程序来说,这是必须的。
16赞 Ivan Ferrer Villa 12/4/2010 #7

我实际上正在使用这种方法,它运行良好。我被 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.targetd.method

效果很好。不再有对象由于事件处理程序而未被 GC 处理。

评论

9赞 Thomas Phaneuf 9/3/2020
我不同意 - 这是一个 .NET 问题和疑问。VB.NET 与任何其他 .NET 语言一样有效,可以解决此问题。我还会包括一个 C# 示例,因为它更常见,但偶尔看到一些 vb.net 仍然很高兴!
0赞 OwnageIsMagic 1/22/2023
@kiddailey 在 Finalyzer 中没有清除的意义,因为该对象已经标记为要收集,并且无论如何都会被 GC 处理,因此它不会再引用订阅者。实际上,将 Finalyzer 添加到对象会延迟到下一个 GC 的内存回收。原始 VB 代码正在使用模式。Dispose
187赞 Stephen Punak 3/29/2011 #8

你们把这种方式对自己太苛刻了。就这么简单:

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

评论

70赞 Delyan 5/20/2011
这只有在您拥有该事件时才有效。尝试在控件上执行此操作。
289赞 Jon Skeet 10/9/2012
...如果你拥有这个事件,你可以写一个相当简单的。FindClicked = null;
3赞 Brent Faust 7/12/2013
这不适用于 Kinect 事件 -- 确实如此,但在 kinect 实例上没有方法来遍历其委托。kinect.ColorFrameReady -= MyEventHanderGetInvocationList()
1赞 huang 2/29/2020
GetInvocationList未找到。
1赞 Jon Skeet 1/22/2021
@Timo:是的,它必须是那个确切的类。
2赞 Anoop Muraleedharan 4/4/2011 #9

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

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# 专家,所以如果有任何错误,请原谅我并请告诉我。

评论

1赞 IbrarMumtaz 9/29/2011
这。CastTo<>() 扩展方法究竟在哪里找到?
0赞 KingOfHypocrites 4/2/2012
你可以自己写: public static T CastTo<T>(this object objectToCast) { return (T)objectToCast; }
2赞 mmike 4/22/2011 #10

斯蒂芬说得对。这很简单:

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

评论

50赞 theor 1/8/2013
上帝啊,编译器应该禁止这种变量名。graphs_must_be_redrawn法语。
4赞 Anton K 5/9/2015
从法语翻译foreach (EventHandler<MyCompletedArgs> handler in CompletionCompleted.GetInvocationList()) { CompletionCompleted -= handler; }
0赞 Brett 12/27/2016
@AntonK的英文翻译效果很好。请记住在属性处理程序上检查 null。
-2赞 suso 10/26/2011 #11

好吧,这里还有另一种解决方案可以删除关联的事件(如果您已经有处理控件事件的方法):

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

评论

1赞 Softlion 10/24/2013
你可以只做 this.button1.MouseDown -= Delegate.CreateDelegate(typeof(EventHandler), this, “button1_MouseDownClicked”)。因此,它无助于解决如何找出要删除的委托的问题,尤其是当它们是内联的时。
73赞 LionSoft 11/13/2011 #12

接受的答案未满。它不适用于声明为 {add; remove;} 的事件

以下是工作代码:

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

评论

4赞 Meister Schnitzel 3/11/2014
这个版本对我有用。接受的版本不起作用。+1。
2赞 Lennart 7/24/2017
直到我在第一次调用中使用之前,它才对 WPF 事件起作用。BindingFlags.PublicGetField
1赞 Sergio Cabral 7/27/2012 #13

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

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

评论

0赞 Michael 12/30/2014
这当然比之前的答案更简洁。它做完全一样的事情吗?看起来确实如此,但也许我错过了什么。另外,当您想要的只是一个 EventHandlerList 时,为什么需要创建一个新对象?EventHandlerList 是否没有可访问的 c-tor,因此只能获得一个在内部为组件构造的 c-tor?
-2赞 RenniePet 6/29/2015 #14

这不是对 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);
  }
21赞 Vinicius Schneider 7/21/2016 #15

我讨厌这里显示的任何完整解决方案,我做了一个混合并现在测试,适用于任何事件处理程序:

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

容易!谢谢斯蒂芬·普纳克。

我之所以使用它,是因为我使用通用的本地方法来删除委托,并且当设置了不同的委托时,在不同情况下调用了本地方法。

评论

4赞 ToolmakerSteve 5/6/2022
根据 Jon Skeet 在其他地方的评论,如果您有 的源代码,clear 方法可以是一行:。(如果代码中没有 AnotherClass,则很遗憾,它不允许调用该事件。AnotherClassTheEventHandler = null;GetInvocationList()
0赞 Jhonattan 9/17/2016 #16

有时我们必须使用第三方控件,我们需要构建这些笨拙的解决方案。基于@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");
0赞 Anatoliy 2/19/2020 #17

删除 button 的所有处理程序: 救。删除事件();

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();
    }
}
1赞 Vassili 4/6/2021 #18

聚会有点晚了,但我使用了这个对我来说非常有效的链接:https://www.codeproject.com/Articles/103542/Removing-Event-Handlers-using-Reflection

此代码的美妙之处在于它适用于所有人,WFP、Forms、Xamarin Forms。我将其用于 Xamarin。请注意,只有当您不拥有此事件时,才需要这种使用反射的方式(例如,库代码在您不关心的某些事件上崩溃)。

这是我稍作修改的代码:

    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”);