So entfernen Sie alle Ereignishandler aus einem Ereignis

366

Sie können dies tun, um einen neuen Ereignishandler für ein Steuerelement zu erstellen

c.Click += new EventHandler(mainFormButton_Click);

oder dieses

c.Click += mainFormButton_Click;

und um einen Ereignishandler zu entfernen, können Sie dies tun

c.Click -= mainFormButton_Click;

Aber wie entfernen Sie alle Ereignishandler aus einem Ereignis?

Carrick
quelle
10
Wenn jemand hierher gekommen ist, um nach einer WPF-Lösung zu suchen, sollten Sie sich diese Antwort ansehen .
Douglas
1
Kannst du nicht einfach einstellen c.Click = null?
Alexandania
Dies ist eines der Dinge, die ich lächerlich überkompliziert finde. Eine einfache ClearMethode war anscheinend zu viel Aufwand
Zimano

Antworten:

167

Ich habe in den MSDN-Foren eine Lösung gefunden . Der folgende Beispielcode entfernt alle ClickEreignisse aus 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
quelle
Wenn button1 auf null gesetzt ist, sind alle Event-Handler an button1 angehängt. Klicken Sie richtig angeordnet?
Damien
3
Korrigieren Sie mich, wenn ich falsch liege, aber sollte nicht die erste Zeile mit RemoveClickEventbeginnen : FieldInfo f1 = typeof(Button)? Ich bekomme null von, GetFieldwenn ich benutze Control.
Beschützer ein
2
Dies scheint bei ToolStripButtons nicht zu funktionieren. Ich habe Button in RemoveClickEvent durch ToolStripButton ersetzt, aber die Ereignisse sind nach dem Aufruf von RemoveClickEvent noch vorhanden. Hat jemand eine Lösung für dieses Problem?
Skalli
1
Der obige Link in MSDN schlägt auch vor, myButton.Click + = null zu versuchen. Wenn Sie alle Delegaten entfernen möchten (nicht für Click, sondern für andere Ereignisse ..)
hallo_earth
1
@hello_earth Scheint nicht zu funktionieren fürObservableCollection.CollectionChanged += null;
Mike de Klerk
146

Ihr macht euch diesen Weg zu schwer. So einfach ist das:

void OnFormClosing(object sender, FormClosingEventArgs e)
{
    foreach(Delegate d in FindClicked.GetInvocationList())
    {
        FindClicked -= (FindClickedHandler)d;
    }
}
Stephen Punak
quelle
57
Dies würde nur funktionieren, wenn Sie die Veranstaltung besitzen. Versuchen Sie es auf einem Steuerelement.
Delyan
226
... und wenn Sie die Veranstaltung besitzen, können Sie einfach schreiben, FindClicked = null;was eher einfacher ist.
Jon Skeet
79
Was ist FindClicked?
Levitikon
3
Dies funktioniert nicht für Kinect-Ereignisse - kinect.ColorFrameReady -= MyEventHanderfunktioniert jedoch, aber es gibt keine GetInvocationList()Methode für Kinect-Instanzen, um über ihre Delegaten zu iterieren.
Brent Faust
GetInvocationListnicht gefunden.
Witz Huang
75

Vom Entfernen aller Ereignishandler :

Direkt nein, zum großen Teil, weil Sie das Ereignis nicht einfach auf null setzen können.

Indirekt können Sie das eigentliche Ereignis privat machen und eine Eigenschaft um es herum erstellen, die alle Delegaten verfolgt, die hinzugefügt / subtrahiert werden.

Nehmen Sie Folgendes:

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
quelle
4
Ich dachte, das OP bezieht sich auf allgemeine .net-Steuerelemente, bei denen diese Art der Umhüllung möglicherweise nicht möglich ist.
Gishu
4
Sie könnten die Kontrolle ableiten, dann würde es
Tom Fobear
Dies führt auch dazu, dass zwei Listen verwaltet werden (siehe stackoverflow.com/questions/91778/… zum Zurücksetzen oder stackoverflow.com/questions/91778/…, um auf die Liste zuzugreifen.
TN.
63

Akzeptierte Antwort ist nicht voll. Es funktioniert nicht für Ereignisse, die als {add; entfernen;}

Hier ist Arbeitscode:

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
quelle
4
Diese Version hat bei mir funktioniert. Die akzeptierte Version hat nicht funktioniert. +1 dafür.
Meister Schnitzel
1
Funktionierte nicht für WPF-Ereignisse, bis ich es BindingFlags.Publicbeim ersten GetFieldAufruf verwendete.
Lennart
40

Das Löschen eines nicht vorhandenen Ereignishandlers schadet nicht. Wenn Sie also wissen, welche Handler es möglicherweise gibt, können Sie einfach alle löschen. Ich hatte gerade einen ähnlichen Fall. Dies kann in einigen Fällen hilfreich sein.

Mögen:

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

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

Ich benutze diese Methode tatsächlich und sie funktioniert perfekt. Der Code, den Aeonhack hier geschrieben hat, hat mich "inspiriert" .

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

Das Feld MyEventEvent ist ausgeblendet, aber vorhanden.

Beim Debuggen können Sie sehen, wie d.targetdas Objekt das Ereignis tatsächlich behandelt, undd.method Methode es verwendet. Sie müssen es nur entfernen.

Es funktioniert großartig. Keine Objekte mehr, die aufgrund der Ereignishandler nicht gced wurden.

Ivan Ferrer Villa
quelle
2
Bitte schreiben Sie keine Antworten in anderen Sprachen.
Hille
10

Ich hasste alle hier gezeigten Komplettlösungen, machte einen Mix und testete jetzt, arbeitete für jeden 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;
        }
    }
}

Einfach! Danke für Stephen Punak.

Ich habe es verwendet, weil ich eine generische lokale Methode zum Entfernen der Delegaten verwende und die lokale Methode nach verschiedenen Fällen aufgerufen wurde, wenn verschiedene Delegaten festgelegt wurden.

Vinicius Schneider
quelle
4

Wenn Sie dies wirklich tun müssen ... wird es einige Zeit dauern , bis Sie darüber nachdenken . Ereignishandler werden in einer Ereignis-zu-Delegat-Zuordnung innerhalb eines Steuerelements verwaltet. Sie müssten

  • Reflektieren und erhalten Sie diese Karte in der Steuerinstanz.
  • Iterieren Sie für jedes Ereignis den Delegierten
    • Jeder Delegierte könnte wiederum eine verkettete Reihe von Event-Handlern sein. Rufen Sie also obControl.RemoveHandler (Ereignis, Handler) auf.

Kurz gesagt, viel Arbeit. Theoretisch ist das möglich ... Ich habe so etwas noch nie versucht.

Überprüfen Sie, ob Sie eine bessere Kontrolle / Disziplin über die Abonnement-Abbestellungsphase für die Kontrolle haben können.

Gishu
quelle
3

Stephen hat recht. Es ist sehr leicht:

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
quelle
38
Gott, der Compiler sollte diese Art von Variablennamen verbieten. graphs_must_be_redrawn auf Französisch.
Gracchus
4
Übersetzen aus dem Französischen foreach (EventHandler<MyCompletedArgs> handler in CompletionCompleted.GetInvocationList()) { CompletionCompleted -= handler; }
Anton K
Die englische Übersetzung von @AntonK funktioniert gut. Denken Sie daran, den Eigenschaftenhandler auf null zu prüfen.
Brett
2

Ich habe gerade festgestellt, wie Ereignisse beim Festlegen einer Eigenschaft eines WinForms-Steuerelements angehalten werden . Es werden alle Ereignisse aus einem Steuerelement entfernt:

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
quelle
1
Dies war sehr hilfreich, aber eines muss geändert werden: In Resume () fügen Sie die Handler in umgekehrter Reihenfolge wieder hinzu (ich gehe davon aus, dass es sich um ein Kopieren / Einfügen aus Suppress handelt, bei dem Sie rückwärts arbeiten möchten um sich nicht mit einer Sammlung herumzuschlagen, über die Sie iterieren). Einige Codes zählen für Handler, die in einer bestimmten Reihenfolge feuern, daher sollte man sich damit nicht anlegen.
Michael
1

Beeindruckend. Ich habe diese Lösung gefunden, aber nichts hat so funktioniert, wie ich es wollte. Aber das ist so gut:

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
quelle
Dies ist sicherlich ordentlicher als die vorherige Antwort. Tut es genau das Gleiche? Es sieht so aus, aber vielleicht fehlt mir etwas. Warum müssen Sie ein neues Objekt erstellen, wenn Sie nur eine EventHandlerList benötigen? Gibt es keinen C-Tor, auf den für EventHandlerList zugegriffen werden kann, sodass nur einer abgerufen werden kann, der intern für eine Komponente erstellt wurde?
Michael
1

Diese Seite hat mir sehr geholfen. Der Code, den ich von hier erhalten habe, sollte ein Klickereignis von einer Schaltfläche entfernen. Ich muss Doppelklickereignisse aus einigen Bedienfeldern entfernen und Ereignisse aus einigen Schaltflächen anklicken. Also habe ich eine Steuerungserweiterung erstellt, die alle Ereignishandler für ein bestimmtes Ereignis entfernt.

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

Nun die Verwendung dieser Erweiterung. Wenn Sie Klickereignisse von einer Schaltfläche entfernen müssen,

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

Wenn Sie Doppelklickereignisse aus einem Bereich entfernen müssen,

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

Ich bin kein Experte für C #. Wenn es also Fehler gibt, verzeihen Sie mir bitte und lassen Sie es mich wissen.

Anoop Muraleedharan
quelle
1
Die .CastTo <> () - Erweiterungsmethode, wo genau ist das zu finden?
IbrarMumtaz
Sie können einfach Ihre eigenen schreiben: public static T CastTo <T> (dieses Objekt objectToCast) {return (T) objectToCast; }
KingOfHypocrites
0

Manchmal müssen wir mit ThirdParty-Steuerelementen arbeiten und diese umständlichen Lösungen erstellen. Basierend auf der Antwort von @Anoop Muraleedharan habe ich diese Lösung mit Inferenztyp und ToolStripItem-Unterstützung erstellt

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

Und Sie können es so verwenden

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

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

Ich habe eine andere funktionierende Lösung von Douglas gefunden .

Diese Methode entfernt alle Ereignishandler, die für ein bestimmtes Routet-Ereignis in einem Element festgelegt wurden.
Verwenden Sie es wie

Remove_RoutedEventHandlers(myImage, Image.MouseLeftButtonDownEvent);

Vollständiger Code:

/// <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
quelle
0

Entfernt alle Handler für die Schaltfläche: 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
quelle
-1

Nun, hier gibt es eine andere Lösung zum Entfernen eines zugeordneten Ereignisses (wenn Sie bereits eine Methode zum Behandeln der Ereignisse für das Steuerelement haben):

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
quelle
Sie können dies einfach tun.button1.MouseDown - = Delegate.CreateDelegate (typeof (EventHandler), dies, "button1_MouseDownClicked"). Es hilft also nicht, die Frage zu lösen, wie man herausfindet, welcher Delegat entfernt werden soll, insbesondere wenn er inline war.
Softlion
-1

Dies ist keine Antwort auf das OP, aber ich dachte, ich würde dies hier posten, falls es anderen helfen kann.

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

Ich habe diese Antwort gefunden und sie passte fast zu meinen Bedürfnissen. Vielen Dank an SwDevMan81 für die Klasse. Ich habe es geändert, um die Unterdrückung und Wiederaufnahme einzelner Methoden zu ermöglichen, und ich dachte, ich würde es hier veröffentlichen.

// 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
quelle
8
Dies ist eine komplizierte Lösung und sollte niemals in industrieller Software verwendet werden. Der beste Ansatz ist wie folgt: Verwalten Sie Ihr Event-Abonnement und entfernen Sie das Abonnement gut, und Sie werden nie auf solche Probleme stoßen.
Tri Q Tran
Ich bin damit einverstanden, dass wir Reflection nicht für unerwünschte Ereignisse verwenden sollten und dass das Abonnement und das Abmelden von Ereignissen von der Anwendung verwaltet werden sollten. Ich denke, das Thema in der Debatte sollte zur DEBUG-Zeit verwendet werden, um herauszufinden, ob wir etwas nerven. Dies ist ein Muss für Legacy-Anwendungen, die Sie überarbeiten.
Tiago Freitas Leal