Cómo eliminar todos los controladores de eventos de un evento

367

Para crear un nuevo controlador de eventos en un control, puede hacer esto

c.Click += new EventHandler(mainFormButton_Click);

o esto

c.Click += mainFormButton_Click;

y para eliminar un controlador de eventos puedes hacer esto

c.Click -= mainFormButton_Click;

Pero, ¿cómo se eliminan todos los controladores de eventos de un evento?

Carrick
fuente
10
Si alguien vino aquí buscando una solución WPF, es posible que desee ver esta respuesta .
Douglas
1
¿No puedes simplemente establecer c.Click = null?
alexania
Esta es una de esas cosas que encuentro ridículamente complicadas. ClearAparentemente
Zimano

Respuestas:

167

Encontré una solución en los foros de MSDN . El siguiente código de muestra eliminará todos los 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
fuente
Si button1 se establece en nulo, ¿están todos los controladores de eventos conectados a button1.Click correctamente dispuesto?
Damien el
3
Corrígeme si me equivoco, pero ¿no debería RemoveClickEventcomenzar la primera línea con FieldInfo f1 = typeof(Button):? Me pongo nulo GetFieldsi uso Control.
Protector uno
2
Esto no parece funcionar para ToolStripButtons. He reemplazado Button en RemoveClickEvent con ToolStripButton, pero los eventos todavía están en su lugar después de llamar a RemoveClickEvent. ¿Alguien tiene una solución para este problema?
Skalli
1
el enlace anterior en MSDN también sugiere probar myButton.Click + = null; si desea eliminar todos los delegados (no para Click, sino para otros eventos ...)
hello_earth
1
@hello_earth no parece funcionar paraObservableCollection.CollectionChanged += null;
Mike de Klerk
146

Ustedes están haciendo este CAMINO demasiado difícil para ustedes mismos. Es así de fácil:

void OnFormClosing(object sender, FormClosingEventArgs e)
{
    foreach(Delegate d in FindClicked.GetInvocationList())
    {
        FindClicked -= (FindClickedHandler)d;
    }
}
Stephen Punak
fuente
58
Esto solo funcionaría si eres el propietario del evento. Intenta hacerlo en un control.
Delyan
227
... y si eres el propietario del evento, puedes escribir lo FindClicked = null;que es bastante más simple.
Jon Skeet
79
¿Qué es FindClicked?
Levitikon
3
Esto no funciona para los eventos de Kinect kinect.ColorFrameReady -= MyEventHander, pero no existe un GetInvocationList()método en las instancias de Kinect para iterar sobre sus delegados.
Brent Faust
GetInvocationListextraviado.
Broma Huang
75

Desde la eliminación de todos los controladores de eventos :

Directamente no, en gran parte porque no puede simplemente establecer el evento como nulo.

Indirectamente, puede hacer que el evento real sea privado y crear una propiedad a su alrededor que rastree a todos los delegados que se agregan / restan.

Toma lo siguiente:

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
fuente
44
Pensé que el OP se refiere a controles generales de .net ... en los que este tipo de ajuste puede no ser posible.
Gishu
44
podría derivar el control, entonces sería
Tom Fobear
Esto también lleva a mantener dos listas, consulte stackoverflow.com/questions/91778/… para restablecer o stackoverflow.com/questions/91778/… para acceder a la lista.
TN.
63

La respuesta aceptada no está completa. No funciona para eventos declarados como {add; eliminar;}

Aquí está el código de trabajo:

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
fuente
44
ESTA versión estaba funcionando para mí. La versión aceptada no funcionó. +1 por eso.
Meister Schnitzel
1
No funcionó para eventos WPF hasta que lo usé BindingFlags.Publicen la primera GetFieldllamada.
Lennart
40

No hace ningún daño eliminar un controlador de eventos no existente. Entonces, si sabe qué controladores pueden existir, simplemente puede eliminarlos todos. Acabo de tener un caso similar. Esto puede ayudar en algunos casos.

Me gusta:

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

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

De hecho, estoy usando este método y funciona perfectamente. Me 'inspiró' el código escrito por Aeonhack aquí .

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

El campo MyEventEvent está oculto, pero existe.

Depuración, puede ver cómo d.targetel objeto realmente maneja el evento, yd.method su método. Solo tienes que eliminarlo.

Funciona muy bien No más objetos que no se GC'ed debido a los controladores de eventos.

Ivan Ferrer Villa
fuente
3
Por favor no escriba respuestas en otros idiomas.
Hille
10

Odiaba las soluciones completas que se muestran aquí, hice una mezcla y probé ahora, trabajé para cualquier controlador 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! Gracias por Stephen Punak.

Lo usé porque utilizo un método local genérico para eliminar los delegados y el método local se llamó después de diferentes casos, cuando se establecen diferentes delegados.

Vinicius Schneider
fuente
4

Si realmente tiene que hacer esto ... se necesitará reflexión y bastante tiempo para hacerlo. Los controladores de eventos se administran en un mapa de evento a delegado dentro de un control. Necesitarías

  • Reflexione y obtenga este mapa en la instancia de control.
  • Iterar para cada evento, obtener el delegado
    • cada delegado a su vez podría ser una serie encadenada de controladores de eventos. Entonces llame a obControl.RemoveHandler (event, handler)

En resumen, mucho trabajo. Es posible en teoría ... Nunca intenté algo como esto.

Vea si puede tener un mejor control / disciplina sobre la fase de suscripción-cancelación de la suscripción para el control.

Gishu
fuente
3

Stephen tiene razón. Es muy 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
fuente
38
Dios, el compilador debería prohibir ese tipo de nombres de variables. graphs_must_be_redrawn en francés.
Gracchus
44
Traducción del francés foreach (EventHandler<MyCompletedArgs> handler in CompletionCompleted.GetInvocationList()) { CompletionCompleted -= handler; }
Anton K
La traducción al inglés de @AntonK funciona bien. Recuerde verificar nulo en el controlador de propiedades.
Brett
2

Acabo de encontrar Cómo suspender eventos al configurar una propiedad de un control WinForms . Eliminará todos los eventos de un 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]);
            }
        }

    }
}
SwDevMan81
fuente
1
Esto fue muy útil, pero hay una cosa que debe cambiarse: en Reanudar (), está agregando los controladores nuevamente en orden inverso (supongo que es una copia / pegado de Suppress, donde desea trabajar hacia atrás, así que para no meterse con una colección sobre la que estás iterando). Algunos códigos cuentan con controladores que disparan en un orden determinado, por lo que no se debe jugar con eso.
Michael
1

Guau. Encontré esta solución, pero nada funcionó como quería. Pero esto es muy bueno:

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
fuente
Esto es ciertamente más limpio que la respuesta anterior. ¿Hace exactamente lo mismo? Parece que sí, pero tal vez me estoy perdiendo algo. Además, ¿por qué necesita crear un nuevo objeto cuando todo lo que quiere es una lista de EventHandler? ¿No hay un c-tor accesible para EventHandlerList, de modo que solo se pueda obtener uno que haya sido construido internamente para un Componente?
Michael
1

Esta página me ayudó mucho. El código que obtuve de aquí estaba destinado a eliminar un evento de clic de un botón. Necesito eliminar eventos de doble clic de algunos paneles y hacer clic en eventos de algunos botones. Así que hice una extensión de control, que eliminará todos los controladores de eventos para un evento determinado.

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

Ahora, el uso de esta extensión. Si necesita eliminar eventos de clic de un botón,

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

Si necesita eliminar eventos de doble clic de un panel,

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

No soy un experto en C #, así que si hay algún error, por favor perdóname y házmelo saber.

Anoop Muraleedharan
fuente
1
El método de extensión .CastTo <> () ¿dónde se encuentra exactamente eso?
IbrarMumtaz
Podrías escribir el tuyo: public static T CastTo <T> (este objeto objectToCast) {return (T) objectToCast; }
KingOfHypocrites
0

A veces tenemos que trabajar con los controles de ThirdParty y necesitamos construir estas soluciones incómodas. Basado en la respuesta de @Anoop Muraleedharan, creé esta solución con el tipo de inferencia y el soporte 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]);
    }

Y puedes usarlo así

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

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

Encontré otra solución de trabajo de Douglas .

Este método elimina todos los controladores de eventos que se establecen en un evento de ruta específico en un elemento.
Úselo 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
fuente
0

elimina todos los controladores para el botón: 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
fuente
-1

Bueno, aquí hay otra solución para eliminar un evento asociado (si ya tiene un método para manejar los eventos para el 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
fuente
Simplemente puede hacer esto.button1.MouseDown - = Delegate.CreateDelegate (typeof (EventHandler), this, "button1_MouseDownClicked"). Por lo tanto, no ayudará a resolver la cuestión de cómo averiguar qué delegado eliminar, especialmente si estaban en línea.
Softlion
-1

Esta no es una respuesta al OP, pero pensé en publicar esto aquí en caso de que pueda ayudar a otros.

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

Encontré esta respuesta y casi se ajusta a mis necesidades. Gracias a SwDevMan81 por la clase. Lo modifiqué para permitir la supresión y reanudación de métodos individuales, y pensé en publicarlo aquí.

// 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
fuente
8
Esta es una solución complicada y nunca debe usarse en software de grado industrial. El mejor enfoque es el mencionado: administre bien la suscripción a su evento y la cancelación de la suscripción, y nunca encontrará tales problemas.
Tri Q Tran
Estoy de acuerdo en que no deberíamos usar la reflexión para desconectar eventos, y la aplicación debe administrar la suscripción y la cancelación de la suscripción. Creo que el tema en debate debería usarse en el momento de DEPURACIÓN, para descubrir si estamos haciendo algo. Esta es una necesidad en las aplicaciones heredadas que está refactorizando.
Tiago Freitas Leal