Como remover todos os manipuladores de eventos de um evento

367

Para criar um novo manipulador de eventos em um controle, você pode fazer isso

c.Click += new EventHandler(mainFormButton_Click);

ou isto

c.Click += mainFormButton_Click;

e para remover um manipulador de eventos, você pode fazer isso

c.Click -= mainFormButton_Click;

Mas como você remove todos os manipuladores de eventos de um evento?

Carrick
fonte
10
Se alguém veio aqui à procura de uma solução WPF, convém procurar esta resposta .
Douglas
11
Você não pode apenas definir c.Click = null?
alexania
Essa é uma daquelas coisas que considero ridiculamente complicadas. Um Clearmétodo simples , aparentemente, era muito esforço
Zimano

Respostas:

167

Encontrei uma solução nos fóruns do MSDN . O código de exemplo abaixo removerá todos os Clickeventos de 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]);
        }
    }
}
xsl
fonte
Se o button1 estiver definido como nulo, todos os manipuladores de eventos serão anexados ao button1.
307 Damien
3
Corrija-me se estiver errado, mas a primeira linha de RemoveClickEventpartida não deve começar com FieldInfo f1 = typeof(Button):? Eu fico nulo GetFieldse eu usar Control.
Protector one
2
Isso não parece funcionar para ToolStripButtons. Substituí Button em RemoveClickEvent por ToolStripButton, mas os eventos ainda estão em vigor após a chamada RemoveClickEvent. Alguém tem uma solução para este problema?
quer
11
o link acima no MSDN também sugere tentar myButton.Click + = null; se você quiser remover todos os delegados (não para Click, mas para outros eventos ..)
hello_earth
11
@hello_earth Não parece funcionar paraObservableCollection.CollectionChanged += null;
Mike de Klerk
146

Vocês estão dificultando muito esse caminho. É fácil assim:

void OnFormClosing(object sender, FormClosingEventArgs e)
{
    foreach(Delegate d in FindClicked.GetInvocationList())
    {
        FindClicked -= (FindClickedHandler)d;
    }
}
Stephen Punak
fonte
58
Isso só funcionaria se você possuir o evento. Tente fazer isso em um controle.
Delyan
227
... e se você é o proprietário do evento, basta escrever o FindClicked = null;que é bastante mais simples.
9138 Jon Skeet
79
O que é FindClicked?
Levitikon 9/10/12
3
Isso não funciona para eventos do Kinect - kinect.ColorFrameReady -= MyEventHanderfunciona, mas não há GetInvocationList()método nas instâncias do kinect para iterar sobre seus delegados.
Brent Faust
GetInvocationListnão encontrado.
Piada Huang
75

De remover todos os manipuladores de eventos :

Diretamente não, em grande parte porque você não pode simplesmente definir o evento como nulo.

Indiretamente, você pode tornar o evento real privado e criar uma propriedade ao seu redor que rastreie todos os delegados que estão sendo adicionados / subtraídos a ele.

Faça o seguinte:

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();
}
Jorge Ferreira
fonte
4
Eu pensei que o OP está se referindo a controles gerais .net .. nos quais esse tipo de embalagem pode não ser possível.
Gishu 18/09/08
4
você pode derivar o controle, então ele faria
Tom Fobear
Isso também leva à manutenção de duas listas, consulte stackoverflow.com/questions/91778/… para redefinir ou stackoverflow.com/questions/91778/… para acessar a lista.
TN.
63

A resposta aceita não está completa. Não funciona para eventos declarados como {add; retirar;}

Aqui está o código de trabalho:

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;
}
LionSoft
fonte
4
ESTA versão estava funcionando para mim. A versão aceita não funcionou. +1 para isso.
Meister Schnitzel
11
Não funcionou para eventos WPF até que eu usei BindingFlags.Publicna primeira GetFieldchamada.
Lennart
40

Não faz mal excluir um manipulador de eventos inexistente. Portanto, se você souber quais manipuladores podem existir, basta excluir todos eles. Eu apenas tive um caso semelhante. Isso pode ajudar em alguns casos.

Gostar:

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

// Remove handlers...
c.Click -= DoesSomething;
c.Click -= DoesSomethingElse;
Peter Mortensen
fonte
16

Na verdade, estou usando esse método e funciona perfeitamente. Fui 'inspirado' pelo código escrito por Aeonhack aqui .

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

O campo MyEventEvent está oculto, mas existe.

Depurando, você pode ver como d.targeto objeto está realmente lidando com o evento e d.methodseu método. Você só precisa removê-lo.

Isso funciona muito bem. Não há mais objetos que não estão sendo colocados em GC por causa dos manipuladores de eventos.

Ivan Ferrer Villa
fonte
2
Por favor, não escreva respostas em outros idiomas.
Hille
10

Eu odiava qualquer solução completa mostrada aqui, fiz um mix e testei agora, trabalhei para qualquer manipulador de eventos:

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

Fácil! Obrigado por Stephen Punak.

Eu usei porque usei um método local genérico para remover os delegados e o método local foi chamado após casos diferentes, quando delegados diferentes são definidos.

Vinicius Schneider
fonte
4

Se você realmente precisa fazer isso ... vai levar reflexão e algum tempo para fazer isso. Manipuladores de eventos são gerenciados em um mapa de delegação de eventos dentro de um controle. Você precisaria

  • Reflita e obtenha esse mapa na instância de controle.
  • Repita para cada evento, obtenha o delegado
    • cada delegado, por sua vez, pode ser uma série encadeada de manipuladores de eventos. Então chame obControl.RemoveHandler (event, handler)

Em suma, muito trabalho. É possível em teoria ... Eu nunca tentei algo assim.

Veja se você pode ter um melhor controle / disciplina sobre a fase de inscrição / cancelamento da inscrição para o controle.

Gishu
fonte
3

Stephen tem razão. Isso é muito fácil:

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;
        }
    }
}
mmike
fonte
38
Deus, o compilador deve proibir esse tipo de nomes de variáveis. graphs_must_be_redrawn em francês.
Graco
4
Traduzindo do francês foreach (EventHandler<MyCompletedArgs> handler in CompletionCompleted.GetInvocationList()) { CompletionCompleted -= handler; }
Anton K
A tradução para o inglês por @AntonK funciona bem. Lembre-se de verificar se há nulo no manipulador de propriedades.
Brett
2

Acabei de encontrar Como suspender eventos ao definir uma propriedade de um controle WinForms . Ele removerá todos os eventos de um controle:

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
fonte
11
Isso foi muito útil, mas há uma coisa que precisa ser alterada: Em Resume (), você está adicionando os manipuladores de volta na ordem inversa (suponho que seja uma cópia / pasta do Suprimir, onde você deseja trabalhar para trás, para não mexer com uma coleção que você está repetindo). Alguns códigos contam com manipuladores disparando em uma determinada ordem, portanto, não se deve mexer com isso.
Michael
1

Uau. Encontrei esta solução, mas nada funcionou como eu queria. Mas isso é tão bom:

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);
}
Sergio Cabral
fonte
Isso é certamente mais limpo do que a resposta anterior. Faz exatamente a mesma coisa? Parece que sim, mas talvez eu esteja perdendo alguma coisa. Além disso, por que você precisa criar um novo objeto quando tudo que você quer é um EventHandlerList? Não existe um c-tor acessível para EventHandlerList, de modo que só se possa obter um que tenha sido construído internamente para um Componente?
Michael
1

Esta página me ajudou muito. O código que obtive daqui foi destinado a remover um evento de clique de um botão. Preciso remover eventos de clique duplo de alguns painéis e eventos de clique de alguns botões. Então, eu fiz uma extensão de controle, que removerá todos os manipuladores de eventos para um determinado evento.

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

Agora, o uso dessa extensão. Se você precisar remover os eventos de clique de um botão,

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

Se você precisar remover os eventos de clique duplo em um painel,

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

Eu não sou especialista em C #, por isso, se houver algum erro, por favor me perdoe e por favor me avise.

Anoop Muraleedharan
fonte
11
O método de extensão .CastTo <> () onde exatamente é encontrado?
IbrarMumtaz 28/09
Você pode simplesmente escrever: public static T CastTo <T> (este objeto objectToCast) {return (T) objectToCast; }
KingOfHypocrites
0

Às vezes, temos que trabalhar com controles de terceiros e precisamos criar essas soluções estranhas. Baseado na resposta do @Anoop Muraleedharan, criei esta solução com o tipo de inferência e o suporte 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]);
    }

E você pode usá-lo assim

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

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

Encontrei outra solução de trabalho de Douglas .

Este método remove todos os manipuladores de eventos definidos em um evento de roteta específico em um elemento.
Use-o como

Remove_RoutedEventHandlers(myImage, Image.MouseLeftButtonDownEvent);

Código completo:

/// <summary>
/// Removes all event handlers subscribed to the specified routed event from the specified element.
/// </summary>
/// <param name="element">The UI element on which the routed event is defined.</param>
/// <param name="RoutetEvent_ToRemove">The routed event for which to remove the event handlers.</param>
public static void RemoveRoutedEventHandlers(UIElement UIElement_Target, RoutedEvent RoutetEvent_ToRemove)
{
    // Get the EventHandlersStore instance which holds event handlers for the specified element.
    // The EventHandlersStore class is declared as internal.
    PropertyInfo PropertyInfo_EventHandlersStore = typeof(UIElement).GetProperty(
        "EventHandlersStore", BindingFlags.Instance | BindingFlags.NonPublic);
    object oEventHandlersStore = PropertyInfo_EventHandlersStore.GetValue(UIElement_Target, null);

    // If there's no event handler subscribed, return
    if (oEventHandlersStore == null) return;

    // Invoke the GetRoutedEventHandlers method on the EventHandlersStore instance 
    // for getting an array of the subscribed event handlers.
    MethodInfo MethodInfo_RoutedEventHandlers = oEventHandlersStore.GetType().GetMethod(
        "GetRoutedEventHandlers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
    RoutedEventHandlerInfo[] RoutedEventHandlerInfos = (RoutedEventHandlerInfo[])MethodInfo_RoutedEventHandlers.Invoke(
        oEventHandlersStore, new object[] { RoutetEvent_ToRemove });

    // Iteratively remove all routed event handlers from the element.
    foreach (RoutedEventHandlerInfo RoutedEventHandlerInfo_Tmp in RoutedEventHandlerInfos)
        UIElement_Target.RemoveHandler(RoutetEvent_ToRemove, RoutedEventHandlerInfo_Tmp.Handler);
}
Hille
fonte
0

remove todos os manipuladores do botão: 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
fonte
-1

Bem, aqui está outra solução para remover um evento associado (se você já possui um método para manipular os eventos para o controle):

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
fonte
Você pode fazer isso.button1.MouseDown - = Delegate.CreateDelegate (typeof (EventHandler), este, "button1_MouseDownClicked"). Portanto, não ajudará a resolver a pergunta: como descobrir qual delegado remover, especialmente se eles estavam em linha.
Softlion
-1

Esta não é uma resposta para o OP, mas pensei em publicá-la aqui, caso possa ajudar outras pessoas.

  /// <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
fonte
-3

Encontrei esta resposta e ela quase atendeu às minhas necessidades. Obrigado a SwDevMan81 pela classe. Eu o modifiquei para permitir a supressão e a retomada de métodos individuais, e pensei em publicá-lo aqui.

// 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);
                    }
                }
            }
        }
    } 
}
Francine
fonte
8
Esta é uma solução complicada e nunca deve ser usada em software de nível industrial. A melhor abordagem é a mencionada: gerencie bem sua assinatura e cancelamento de assinatura de eventos e você nunca encontrará esses problemas.
Tri Q Tran
Concordo que não devemos usar a reflexão para desfazer eventos, e a assinatura e a não assinatura do evento devem ser gerenciadas pelo aplicativo. Eu acho que a questão em debate deve ser usada no momento do DEBUG, para descobrir se estamos irritando alguma coisa. Isso é obrigatório nos aplicativos herdados que você está refatorando.
Tiago Freitas Leal