Cara menghapus semua penangan acara dari suatu acara

367

Untuk membuat pengendali acara baru pada kontrol Anda bisa melakukan ini

c.Click += new EventHandler(mainFormButton_Click);

atau ini

c.Click += mainFormButton_Click;

dan untuk menghapus event handler Anda dapat melakukan ini

c.Click -= mainFormButton_Click;

Tetapi bagaimana Anda menghapus semua penangan acara dari suatu acara?

Carrick
sumber
10
Jika ada yang datang ke sini mencari solusi WPF, Anda mungkin ingin melihat jawaban ini .
Douglas
1
Tidak bisakah Anda mengatur c.Click = null?
alexania
Ini adalah salah satu hal yang menurut saya sangat rumit. Sebuah Clearmetode sederhana ternyata terlalu banyak usaha
Zimano

Jawaban:

167

Saya menemukan solusi di forum MSDN . Kode sampel di bawah ini akan menghapus semua Clickacara 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
sumber
Jika button1 disetel ke nol, apakah semua penangan acara dilampirkan ke button1.Klik dibuang dengan benar?
Damien
3
Perbaiki saya jika saya salah, tetapi bukankah seharusnya baris pertama RemoveClickEventdimulai dengan FieldInfo f1 = typeof(Button):? Saya mendapat null GetFieldjika saya gunakan Control.
Pelindung satu
2
Ini sepertinya tidak berfungsi untuk ToolStripButtons. Saya telah mengganti Tombol di RemoveClickEvent dengan ToolStripButton, tetapi acara masih ada setelah memanggil RemoveClickEvent. Adakah yang punya solusi untuk masalah ini?
Skalli
1
tautan di atas dalam MSDN juga menyarankan untuk mencoba myButton.Klik + = null; jika Anda ingin menghapus semua delegasi (bukan untuk Klik, tetapi untuk acara lainnya ..)
hello_earth
1
@hello_earth Sepertinya tidak berhasilObservableCollection.CollectionChanged += null;
Mike de Klerk
146

Kalian membuat WAY ini terlalu keras untuk dirimu sendiri. Ini semudah ini:

void OnFormClosing(object sender, FormClosingEventArgs e)
{
    foreach(Delegate d in FindClicked.GetInvocationList())
    {
        FindClicked -= (FindClickedHandler)d;
    }
}
Stephen Punak
sumber
58
Ini hanya akan berfungsi jika Anda memiliki acara tersebut. Coba lakukan dengan kontrol.
Delyan
227
... dan jika Anda memiliki acara tersebut, Anda bisa menulis FindClicked = null;yang lebih sederhana.
Jon Skeet
79
Apa itu FindClicked?
Levitikon
3
Ini tidak berfungsi untuk acara Kinect - kinect.ColorFrameReady -= MyEventHandertidak, tetapi tidak ada GetInvocationList()metode pada instance kinect untuk beralih pada delegasi mereka.
Brent Faust
GetInvocationListtidak ditemukan.
Lelucon Huang
75

Dari Menghapus Semua Penangan Acara :

Secara langsung tidak, sebagian besar karena Anda tidak bisa begitu saja mengatur acara ke nol.

Secara tidak langsung, Anda dapat menjadikan acara aktual itu pribadi dan membuat properti di sekitarnya yang melacak semua delegasi yang ditambahkan / dikurangi.

Ambil yang berikut ini:

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
sumber
4
Saya pikir OP mengacu pada kontrol .net umum .. di mana pembungkus semacam ini tidak mungkin dilakukan.
Gishu
4
Anda bisa mendapatkan kontrol, maka itu akan
Tom Fobear
Ini juga mengarah untuk mempertahankan dua daftar, lihat stackoverflow.com/questions/91778/… untuk mengatur ulang atau stackoverflow.com/questions/91778/… untuk mengakses daftar.
TN.
63

Jawaban yang diterima tidak penuh. Ini tidak berfungsi untuk acara yang dinyatakan sebagai {add; menghapus;}

Ini kode kerjanya:

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
sumber
4
Versi INI bekerja untuk saya. Versi yang diterima tidak berfungsi. +1 untuk itu.
Meister Schnitzel
1
Tidak berfungsi untuk acara WPF sampai saya menggunakan panggilan BindingFlags.Publicpertama GetField.
Lennart
40

Tidak ada salahnya untuk menghapus event handler yang tidak ada. Jadi, jika Anda tahu penangan apa yang ada, Anda bisa menghapus semuanya. Saya hanya punya kasus serupa. Ini dapat membantu dalam beberapa kasus.

Suka:

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

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

Saya sebenarnya menggunakan metode ini dan itu bekerja dengan sempurna. Saya 'terinspirasi' oleh kode yang ditulis oleh Aeonhack di sini .

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

Bidang MyEventEvent disembunyikan, tetapi memang ada.

Debugging, Anda dapat melihat bagaimana d.targetobjek sebenarnya menangani acara, dand.method metodenya. Anda hanya perlu menghapusnya.

Ini bekerja dengan baik. Tidak ada lagi objek yang tidak GC karena penangan acara.

Ivan Ferrer Villa
sumber
3
Tolong jangan menulis jawaban dalam bahasa lain.
Hille
10

Saya benci solusi lengkap yang ditampilkan di sini, saya melakukan campuran dan diuji sekarang, bekerja untuk pengendali acara:

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

Mudah! Terima kasih untuk Stephen Punak.

Saya menggunakannya karena saya menggunakan metode lokal umum untuk menghapus delegasi dan metode lokal dipanggil setelah kasus yang berbeda, ketika delegasi yang berbeda diselesaikan.

Vinicius Schneider
sumber
4

Jika Anda benar -benar harus melakukan ini ... itu akan membutuhkan refleksi dan cukup waktu untuk melakukan ini. Penangan event dikelola dalam event-to-delegate-map di dalam kontrol. Anda harus melakukannya

  • Refleksikan dan dapatkan peta ini dalam instance kontrol.
  • Iterate untuk setiap acara, dapatkan delegasi
    • masing-masing delegasi pada gilirannya bisa menjadi rangkaian event handler yang dirantai. Jadi panggil obControl.RemoveHandler (event, handler)

Singkatnya, banyak pekerjaan. Mungkin saja secara teori ... Saya tidak pernah mencoba hal seperti ini.

Lihat apakah Anda dapat memiliki kontrol / disiplin yang lebih baik atas fase berlangganan-berhenti berlangganan untuk kontrol.

Gishu
sumber
3

Stephen benar. Ini sangat mudah:

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
sumber
38
Ya Tuhan, kompiler harus melarang nama-nama variabel semacam itu. graphs_must_be_redrawn dalam bahasa Prancis.
gracchus
4
Menerjemahkan dari Prancis foreach (EventHandler<MyCompletedArgs> handler in CompletionCompleted.GetInvocationList()) { CompletionCompleted -= handler; }
Anton K
Terjemahan bahasa Inggris oleh @AntonK berfungsi dengan baik. Ingatlah untuk memeriksa nol pada penangan properti.
Brett
2

Saya baru saja menemukan Cara menunda acara saat mengatur properti dari kontrol WinForms . Ini akan menghapus semua acara dari kontrol:

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
sumber
1
Ini sangat membantu, tetapi ada satu hal yang perlu diubah: Di Resume (), Anda menambahkan handler kembali dalam urutan terbalik (saya berasumsi itu adalah salinan / tempel dari Suppress, di mana Anda ingin bekerja mundur sehingga agar tidak mengacaukan koleksi yang sedang Anda ulangi). Beberapa kode mengandalkan penangan yang menembak dalam urutan tertentu, jadi orang tidak boleh macam-macam dengan itu.
Michael
1

Wow. Saya menemukan solusi ini, tetapi tidak ada yang berhasil seperti yang saya inginkan. Tapi ini sangat bagus:

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
sumber
Ini tentu lebih rapi dari jawaban sebelumnya. Apakah ia melakukan hal yang persis sama? Sepertinya memang begitu, tapi mungkin aku melewatkan sesuatu. Juga, mengapa Anda perlu membuat objek baru ketika semua yang Anda inginkan adalah EventHandlerList? Apakah tidak ada c-tor yang dapat diakses untuk EventHandlerList, sehingga seseorang hanya dapat memperolehnya yang telah dibangun secara internal untuk sebuah Komponen?
Michael
1

Halaman ini banyak membantu saya. Kode yang saya dapatkan dari sini dimaksudkan untuk menghapus acara klik dari sebuah tombol. Saya perlu menghapus acara klik ganda dari beberapa panel dan klik acara dari beberapa tombol. Jadi saya membuat ekstensi kontrol, yang akan menghapus semua penangan acara untuk acara tertentu.

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

Sekarang, penggunaan extenstion ini. Jika Anda perlu menghapus acara klik dari sebuah tombol,

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

Jika Anda perlu menghapus acara klik dua kali dari panel,

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

Saya bukan ahli C #, jadi jika ada bug tolong maafkan saya dan beri tahu saya tentang itu.

Anoop Muraleedharan
sumber
1
Metode ekstensi .CastTo <> () di mana tepatnya ditemukan?
IbrarMumtaz
Anda bisa menulis sendiri: public static T CastTo <T> (objek ini objectToCast) {return (T) objectToCast; }
KingOfHypocrites
0

Terkadang kita harus bekerja dengan kontrol ThirdParty dan kita perlu membangun solusi yang canggung ini. Berbasis di @Anoop Muraleedharan jawaban saya membuat solusi ini dengan tipe inferensi dan dukungan 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]);
    }

Dan Anda bisa menggunakannya seperti ini

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

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

Saya menemukan solusi kerja lain oleh Douglas .

Metode ini menghapus semua penangan acara yang ditetapkan pada acara router tertentu pada suatu elemen.
Gunakan seperti

Remove_RoutedEventHandlers(myImage, Image.MouseLeftButtonDownEvent);

Kode lengkap:

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

menghapus semua penangan untuk tombol: 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
sumber
-1

Nah, di sini ada solusi lain untuk menghapus acara yang terkait (jika Anda sudah memiliki metode untuk menangani acara untuk kontrol):

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
sumber
Anda bisa melakukan ini.button1.MouseDown - = Delegate.CreateDelegate (typeof (EventHandler), ini, "button1_MouseDownClicked"). Jadi itu tidak akan membantu menyelesaikan pertanyaan yang adalah bagaimana mencari tahu delegasi mana yang harus dihapus, terutama jika mereka sebaris.
Softlion
-1

Ini bukan jawaban untuk OP, tapi saya pikir saya akan memposting ini di sini kalau-kalau itu bisa membantu orang lain.

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

Saya menemukan jawaban ini dan hampir sesuai dengan kebutuhan saya. Terima kasih kepada SwDevMan81 untuk kelasnya. Saya telah memodifikasinya untuk memungkinkan penindasan dan melanjutkan metode individual, dan saya pikir saya akan mempostingnya di sini.

// 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
sumber
8
Ini adalah solusi berbelit-belit dan tidak boleh digunakan dalam perangkat lunak kelas industri. Pendekatan terbaik adalah seperti yang disebutkan: Kelola langganan acara Anda dan lepaskan langganan dengan baik dan Anda tidak akan pernah mengalami masalah seperti itu.
Tri Q Tran
Saya setuju bahwa kita tidak boleh menggunakan refleksi untuk acara yang tidak diinginkan, dan berlangganan acara dan berhenti berlangganan harus dikelola oleh aplikasi. Saya pikir masalah dalam debat harus digunakan pada waktu DEBUG, untuk mencari tahu apakah kita mengabaikan sesuatu. Ini adalah keharusan pada aplikasi lama yang Anda refactoring.
Tiago Freitas Leal