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

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?

Encontré una solución en los foros de MSDN . El siguiente código de ejemplo eliminará todos los eventos Click 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]); } } } 

Ustedes están haciendo esto de MANERA muy dura para ustedes. Es así de fácil:

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

Eliminando todos los manejadores de eventos :

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

Indirectamente, puede hacer que el evento real sea privado y crear una propiedad a su alrededor que haga un seguimiento de todos los delegates que se agregan / restan a él.

Tome lo siguiente:

 List delegates = new List(); 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(); } 

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

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

No hace ningún daño eliminar un controlador de eventos no existente. Entonces, si sabe qué manejadores podría haber, 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; 

De hecho, estoy usando este método y funciona perfectamente. Me inspiré en 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.

Depurando, puede ver cómo d.target es el objeto que realmente maneja el evento, y d.method su método. Solo tienes que eliminarlo.

Funciona muy bien. No hay más objetos sin GC por causa de los controladores de eventos.

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

  • Refleja y obtén 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. Así que llame a obControl.RemoveHandler (evento, controlador)

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-desuscripción para el control.

Stephen tiene razón. Es muy fácil:

 public event EventHandler les_graph_doivent_etre_redessines; public void remove_event() { if (this.les_graph_doivent_etre_redessines != null) { foreach (EventHandler 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; } } } 

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 _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 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 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 pair in _handlers) { for (int x = pair.Value.Length - 1; x >= 0; x--) _sourceEventHandlerList.RemoveHandler(pair.Key, pair.Value[x]); } } } } 

Odiaba cualquier solución completa que se muestra aquí, hice una mezcla y probé ahora, funcionó 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 delegates y se llamó al método local después de diferentes casos, cuando se configuran diferentes delegates.

Guau. Encontré esta solución, pero nada funcionó como yo 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); } 

Esta página me ayudó mucho. El código que obtuve aquí fue para 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(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 amablemente házmelo saber.

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

  ///  /// 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". ///  private static void RemoveCompletedEventHandler(SocketAsyncEventArgs eventArgs) { FieldInfo fieldInfo = typeof(SocketAsyncEventArgs).GetField("m_Completed", BindingFlags.Instance | BindingFlags.NonPublic); eventArgs.Completed -= (EventHandler)fieldInfo.GetValue(eventArgs); } 

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 tipo de inferencia y soporte ToolStripItem

  public static void RemoveItemEvents(this T target, string eventName) where T : ToolStripItem { RemoveObjectEvents(target, eventName); } public static void RemoveControlEvents(this T target, string eventName) where T : Control { RemoveObjectEvents(target, eventName); } private static void RemoveObjectEvents(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"); 

Bueno, aquí hay otra solución para eliminar un evento asociado (si ya tienes 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); 

Encontré esta respuesta y casi se ajusta a mis necesidades. Gracias a SwDevMan81 por la clase. Lo he modificado para permitir la supresión y la reanudación de métodos individuales, y pensé que lo publicaría 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 suppressedHandlers = new Dictionary(); 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 BuildList() { Dictionary retval = new Dictionary(); 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 BuildListWalk(Dictionary 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 toRemove = new Dictionary(); // 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 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 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 dict = BuildList(); foreach (KeyValuePair 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); } } } } } }