433

To create a new event handler on a control you can do this

c.Click += new EventHandler(mainFormButton_Click);

or this

c.Click += mainFormButton_Click;

and to remove an event handler you can do this

c.Click -= mainFormButton_Click;

But how do you remove all event handlers from an event?

Michael Bahig
  • 748
  • 8
  • 17
Carrick
  • 4,423
  • 3
  • 18
  • 12
  • 15
    If anyone came here looking for a WPF solution, you might want to look at [this answer](http://stackoverflow.com/a/12618521/1149773). – Douglas Sep 27 '12 at 09:49
  • 3
    Can you not just set `c.Click = null`? – alexania Feb 06 '20 at 18:04
  • 11
    This is one of those things that I find ridiculously overcomplicated. A simple `Clear` method was too much effort apparently – Zimano Apr 21 '20 at 09:21
  • In .NET 4.5, if the List.Count > 2 lets say, and you try to remove the 1st delegate InvocatorList[0] == mainFormButton_Click... like you did.. it will remove all of them. I think it is a bug! – Latency Jul 28 '20 at 22:12

18 Answers18

192

I found a solution on the MSDN forums. The sample code below will remove all Click events from button1.

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]);
    }
}
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
xsl
  • 17,116
  • 18
  • 71
  • 112
  • If button1 is set to null, are all event handlers attached to button1.Click correctly disposed? – Damien Jul 29 '09 at 04:12
  • just to point it out: seems that this only removes custom events, no original behaviour. – esskar Sep 08 '11 at 09:38
  • 4
    Correct me if I'm wrong, but shouldn't the first line of `RemoveClickEvent` start with: `FieldInfo f1 = typeof(Button)`? I get null from `GetField` if I use `Control`. – Protector one Nov 11 '11 at 10:42
  • 2
    This does not seem to work for ToolStripButtons. I've replaced Button in RemoveClickEvent with ToolStripButton, but the events are still in place after calling RemoveClickEvent. Has anyone a solution for this problem? – Skalli Nov 21 '11 at 09:22
  • 1
    the above link in MSDN also suggests trying myButton.Click += null; if you want to remove all delegates (not for Click, but for other events..) – hello_earth May 31 '13 at 12:37
  • 1
    @hello_earth Does not seem to work for `ObservableCollection.CollectionChanged += null;` – Mike de Klerk Oct 15 '13 at 08:12
  • THIS CAUSES STRANGE ISSUE in specific C# software. (In my case, the controls were doubled by this function) – T.Todua Sep 26 '17 at 13:29
  • Trying to do this with Xamarin UWP does not work, any thoughts? https://stackoverflow.com/questions/72623117/how-to-remove-delegate-from-eventhandler-in-xamarin-uwp – Kikanye Jun 15 '22 at 18:13
  • I just called the remove method just before assigning the click event. works perfect! – Rudy Hinojosa Apr 04 '23 at 13:49
186

You guys are making this WAY too hard on yourselves. It's this easy:

void OnFormClosing(object sender, FormClosingEventArgs e)
{
    foreach(Delegate d in FindClicked.GetInvocationList())
    {
        FindClicked -= (FindClickedHandler)d;
    }
}
Stephen Punak
  • 2,085
  • 1
  • 12
  • 2
  • 69
    This would only work if you own the event. Try doing it on a control. – Delyan May 19 '11 at 20:20
  • 285
    ... and if you own the event, you can just write `FindClicked = null;` which is rather simpler. – Jon Skeet Oct 08 '12 at 16:42
  • 3
    This doesn't work for Kinect events -- `kinect.ColorFrameReady -= MyEventHander` does, but there is no `GetInvocationList()` method on kinect instances to iterate over their delegates. – Brent Faust Jul 12 '13 at 01:44
  • 1
    `GetInvocationList` not found. – huang Feb 28 '20 at 18:49
  • @JonSkeet I've tried this in VS2017, but it seems I'm not allowed to set the event to `null` (only `+=` and `-=` allowed). I've tried this in the `Dispose` of the class that owns the event (and on other, less logical places). Does your comment still hold? – Timo Jan 22 '21 at 09:34
  • @Timo: Yes, that should still work - so long as it's a field-like event. Without more context it's hard to know what's going wrong. Please ask a new question. – Jon Skeet Jan 22 '21 at 09:40
  • @JonSkeet I found an example in my own code where it works as described. The case I was referring to earlier was slightly different: removing the event handlers of an event from the parent class in the child class. From the child class you are not allowed to use `(parent.)event = null`, where `(parent.)` not actually written in the code. – Timo Jan 22 '21 at 10:19
  • 1
    @Timo: Right, it does have to be that exact class. – Jon Skeet Jan 22 '21 at 13:52
  • GetInvocationList() is only available for Delegates, not events – Elo Jul 28 '21 at 09:44
91

From Removing All Event Handlers:

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();
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jorge Ferreira
  • 96,051
  • 25
  • 122
  • 132
  • 6
    I thought the OP is referring to general .net controls.. in which this kind of wrapping may not be possible. – Gishu Sep 18 '08 at 11:47
  • 5
    you could derive the control, then it would – Tom Fobear Apr 13 '11 at 16:06
  • 1
    This also leads to maintain two lists, see http://stackoverflow.com/questions/91778/how-to-remove-all-event-handlers-from-a-control/91803#comment17284514_5475424 for reset or http://stackoverflow.com/questions/91778/how-to-remove-all-event-handlers-from-a-control/5475424#5475424 to access the list. – TN. Sep 04 '13 at 13:45
  • Sadly, I can't wrap this behaivour on an Interface as it can't have instance fields. – Noman_1 Oct 21 '20 at 07:27
  • @Noman_1 - wlith interfaces the solution when you want a field is to instead have a pair of methods (getter/setter). Then it is up to each class to implement that method pair. – ToolmakerSteve Jun 21 '21 at 14:51
73

Accepted answer is not full. It doesn't work for events declared as {add; remove;}

Here is working code:

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;
}
Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
LionSoft
  • 1,469
  • 2
  • 15
  • 20
50

It doesn't do any harm to delete a non-existing event handler. So if you know what handlers there might be, you can simply delete all of them. I just had similar case. This may help in some cases.

Like:

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

// Remove handlers...
c.Click -= DoesSomething;
c.Click -= DoesSomethingElse;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 3
    yes, but the problem is that you can do += multiple times and then there is no way of knowing how many of the same event are attached to the handler. So you don't know how many -= you need to clear it all the way. – Tomislav3008 Jul 14 '21 at 07:24
  • @Tomislav3008 - I'm not aware of any valid situation where you want to have multiple of the exact same handler - that's usually a bug - you might have multiple of the same type for different instances, but that is not the situation here - this answer is code that only makes sense inside an instance. In fact I've sometimes written code where the `+=` first does `-=`, to make sure the identical handler isn't accidentally added twice. – ToolmakerSteve Mar 30 '22 at 17:02
  • What about when you need to remove a handler from your class after it is removed from the control? If you are using a GridView and attach handlers during phase 1, you keep generating handlers as the ContainerContent changes, therefore you need to make sure the same control gets a new handler while deleting the old one. Gonna have to keep a Map in that case. – John Glen Nov 21 '22 at 01:00
  • @ToolmakerSteve What about the case where you have handlers in the item containers in a GridView because you need controls for each element in the GridView? You would have to swap out handlers every time the container changes its contents. – John Glen Nov 21 '22 at 01:03
  • @JohnGlen - I'd have to see specific code, which is beyond the scope of these comments. Unless I misunderstand, I would have a single handler, with the item passed as a parameter. I don't claim that covers all possibilities, but its been sufficient for my needs. I agree that Grid items can get trickier than a listview. IIRC, in one case I added an `int ID` field in each cell's viewmodel data, with page having a map from ID to View for that cell. So I could pass an `int` parameter, and locate the corresponding View. (my ID was `N * row + column`, where `N` was greater than max # columns.) – ToolmakerSteve Nov 21 '22 at 01:15
21

I hated any complete solutions shown here, I did a mix and tested now, worked for any event handler:

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

Easy! Thanks for Stephen Punak.

I used it because I use a generic local method to remove the delegates and the local method was called after different cases, when different delegates are setted.

  • 3
    As per Jon Skeet's comment elsewhere, if you have source code of `AnotherClass`, the clear method can be one line: `TheEventHandler = null;`. (If AnotherClass isn't in your code, then unfortunately its not permitted to call `GetInvocationList()` on the event.) – ToolmakerSteve May 06 '22 at 00:08
16

I'm actually using this method and it works perfectly. I was 'inspired' by the code written by Aeonhack here.


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

    }

The field MyEventEvent is hidden, but it does exist.

Debugging, you can see how d.target is the object actually handling the event, and d.method its method. You only have to remove it.

It works great. No more objects not being GC'ed because of the event handlers.

kiddailey
  • 3,302
  • 3
  • 22
  • 23
Ivan Ferrer Villa
  • 2,129
  • 1
  • 26
  • 23
  • 9
    I disagree - this is a .NET issue and question. VB.NET is as valid as any other .NET language to solve this problem. I would have also included a C# example since it is more common, but it is still nice to see some vb.net once in awhile! – Thomas Phaneuf Sep 03 '20 at 13:35
  • @kiddailey There is no point clearing in Finalyzer, since the object is already marked for collection and will be GC'ed anyway, so it won't reference subscribers anymore. Actually adding Finalyzer to object delays memory reclamation to the next GC. Original VB code is using `Dispose` pattern. – OwnageIsMagic Jan 22 '23 at 15:24
4

If you reaallly have to do this... it'll take reflection and quite some time to do this. Event handlers are managed in an event-to-delegate-map inside a control. You would need to

  • Reflect and obtain this map in the control instance.
  • Iterate for each event, get the delegate
    • each delegate in turn could be a chained series of event handlers. So call obControl.RemoveHandler(event, handler)

In short, a lot of work. It is possible in theory... I never tried something like this.

See if you can have better control/discipline over the subscribe-unsubscribe phase for the control.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Gishu
  • 134,492
  • 47
  • 225
  • 308
3

I just found How to suspend events when setting a property of a WinForms control. It will remove all events from a control:

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

    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
SwDevMan81
  • 48,814
  • 22
  • 151
  • 184
  • 2
    This was very helpful, but there's one thing that needs to be changed: In Resume(), you're adding the handlers back in reverse order (I'm assuming it's a copy/paste from Suppress, where you want to work backwards so as not to mess with a collection that you're iterating over). Some code counts on handlers firing in a given order, so one shouldn't mess with that. – Michael Dec 29 '14 at 16:14
2

This page helped me a lot. The code I got from here was meant to remove a click event from a button. I need to remove double click events from some panels and click events from some buttons. So I made a control extension, which will remove all event handlers for a certain event.

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

Now, the usage of this extenstion. If you need to remove click events from a button,

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

If you need to remove doubleclick events from a panel,

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

I am not an expert in C#, so if there are any bugs please forgive me and kindly let me know about it.

Louis Somers
  • 2,560
  • 3
  • 27
  • 57
2

Stephen has right. It is very easy:

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;
        }
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
mmike
  • 45
  • 1
  • 1
  • 50
    God, the compiler should forbid that kind of variable names. graphs_must_be_redrawn in french. – theor Jan 07 '13 at 22:07
  • 4
    Translating from French `foreach (EventHandler handler in CompletionCompleted.GetInvocationList())` `{` `CompletionCompleted -= handler;` `}` – Anton K May 08 '15 at 22:29
  • English translation by @AntonK works well. Remember to check for null on the property handler. – Brett Dec 27 '16 at 13:50
1

Wow. I found this solution, but nothing worked like I wanted. But this is so good:

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);
}
Brad Larson
  • 170,088
  • 45
  • 397
  • 571
Sergio Cabral
  • 6,490
  • 2
  • 35
  • 37
  • This is certainly neater than the previous answer. Does it do exactly the same thing? It looks like it does, but perhaps I'm missing something. Also, why do you need to create a new object when all you want is an EventHandlerList? Is there no c-tor accessible for EventHandlerList, such that one can only get one that's been constructed internally for a Component? – Michael Dec 29 '14 at 16:19
1

A bit late to the party, but I used this link that worked perfectly well for me: https://www.codeproject.com/Articles/103542/Removing-Event-Handlers-using-Reflection

The beauty of this code is that it works for all, WFP, Forms, Xamarin Forms. I used it for Xamarin. Note that you need this way of using Reflection only if you don't own this event (e.g. a library code that crashes on some event that you don't care about).

Here is my slightly modified code:

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


Example usage: RemoveEventHandler(obj, "Focused");

Vassili
  • 151
  • 1
  • 7
0

Sometimes we have to work with ThirdParty controls and we need to build these awkward solutions. Based in @Anoop Muraleedharan answer I created this solution with inference type and ToolStripItem support

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

And you can use it like this

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

    var button = new Button();
    button.RemoveControlEvents("EventClick");
Jhonattan
  • 492
  • 5
  • 14
0

removes all handlers for button: save.RemoveEvents();

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();
    }
}
Anatoliy
  • 87
  • 4
-2

This is not an answer to the OP, but I thought I'd post this here in case it can help others.

  /// <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);
  }
RenniePet
  • 11,420
  • 7
  • 80
  • 106
-2

Well, here there's another solution to remove an asociated event (if you already have a method for handling the events for the control):

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);
suso
  • 5
  • 2
  • 1
    You can just do this.button1.MouseDown -= Delegate.CreateDelegate(typeof(EventHandler), this, "button1_MouseDownClicked"). So it won't help resolve the question which is how to find out which delegate to remove, especially if they were inline. – Softlion Oct 24 '13 at 10:26
-4

I found this answer and it almost fit my needs. Thanks to SwDevMan81 for the class. I have modified it to allow suppression and resumation of individual methods, and I thought I'd post it here.

// 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);
                    }
                }
            }
        }
    } 
}
ChrisF
  • 134,786
  • 31
  • 255
  • 325
Francine
  • 11
  • 1
  • 8
    This is a convoluted solution and should never be used in industrial grade software. The best approach is as mentioned: Manage your event subscription and un-subscription well and you will not ever encounter such problems. – Tri Q Tran Jun 25 '10 at 02:42
  • I agree we shouldn't use reflection to unwire events, and event subscription and un-subscription should be managed by the application. I think the issue in debate should be used at DEBUG time, to find out wheter we are nissing something. This is a must on legacy applications you are refactoring. – Tiago Freitas Leal Dec 27 '14 at 18:21