Как удалить все обработчики событий из события

367

Чтобы создать новый обработчик событий в элементе управления, вы можете сделать это

c.Click += new EventHandler(mainFormButton_Click);

или это

c.Click += mainFormButton_Click;

и удалить обработчик события, вы можете сделать это

c.Click -= mainFormButton_Click;

Но как удалить все обработчики событий из события?

Каррик
источник
10
Если кто-то пришел сюда в поисках решения WPF, вы можете посмотреть на этот ответ .
Дуглас
1
Вы не можете просто установить c.Click = null?
Алексания
Это одна из тех вещей, которые я нахожу до смешного слишком сложными. Простой Clearметод был слишком много усилий, по-видимому
Зимано

Ответы:

167

Я нашел решение на форумах MSDN . Пример кода ниже удалит все Clickсобытия из 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
источник
Если button1 установлен в null, все обработчики событий присоединены к button1.Click правильно расположены?
Дэмиен
3
Поправьте меня , если я ошибаюсь, но не стоит на первой линии RemoveClickEventстарта с: FieldInfo f1 = typeof(Button)? Я получаю ноль, GetFieldесли я использую Control.
Защитник один
2
Похоже, это не работает для ToolStripButtons. Я заменил Button в RemoveClickEvent на ToolStripButton, но события остаются в силе после вызова RemoveClickEvent. У кого-нибудь есть решение этой проблемы?
Скалли
1
ссылка выше в MSDN также предлагает попробовать myButton.Click + = null; если вы хотите удалить всех делегатов (не для
Клика
1
@hello_earth Кажется, не работаетObservableCollection.CollectionChanged += null;
Майк де Клерк
146

Вы, ребята, делаете этот путь слишком тяжелым для себя. Это так просто:

void OnFormClosing(object sender, FormClosingEventArgs e)
{
    foreach(Delegate d in FindClicked.GetInvocationList())
    {
        FindClicked -= (FindClickedHandler)d;
    }
}
Стивен Пунак
источник
58
Это будет работать только если у вас есть событие. Попробуйте сделать это на контроле.
Делян
227
... и если у вас есть событие, вы можете просто написать, FindClicked = null;что проще.
Джон Скит
79
Что такое FindClicked?
Левитикон
3
Это не работает для событий Kinect - kinect.ColorFrameReady -= MyEventHanderработает, но в GetInvocationList()экземплярах kinect нет метода для перебора их делегатов.
Брент Фауст
GetInvocationListне найдено.
Шутка Хуан
75

От удаления всех обработчиков событий :

Напрямую нет, во многом потому, что вы не можете просто установить событие в null.

Косвенно вы можете сделать фактическое событие частным и создать вокруг него свойство, которое будет отслеживать всех делегатов, добавляемых / вычитаемых к нему.

Возьмите следующее:

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();
}
Хорхе Феррейра
источник
4
Я думал, что OP ссылается на общие элементы управления .net .., в которых этот вид упаковки может быть невозможен.
Гишу
4
Вы могли бы получить контроль, тогда это будет
Том Фобер
Это также ведет к поддержанию двух списков, см. Stackoverflow.com/questions/91778/… для сброса или stackoverflow.com/questions/91778/… для доступа к списку.
TN.
63

Принятый ответ не полный. Это не работает для событий, объявленных как {add; Удалить;}

Вот рабочий код:

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
источник
4
Эта версия работала на меня. Принятая версия не работает. +1 за это.
Майстер Шницель,
1
Не работал для событий WPF, пока я не использовал BindingFlags.Publicпервый GetFieldвызов.
Леннарт
40

Это не вредит удалению несуществующего обработчика событий. Так что, если вы знаете, какие там могут быть обработчики, вы можете просто удалить их все. У меня просто был похожий случай. Это может помочь в некоторых случаях.

Подобно:

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

// Remove handlers...
c.Click -= DoesSomething;
c.Click -= DoesSomethingElse;
Питер Мортенсен
источник
16

Я на самом деле использую этот метод, и он работает отлично. Я был «вдохновлен» кодом, написанным здесь Aeonhack .

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

Поле MyEventEvent скрыто, но оно существует.

Отладка, вы можете увидеть, как d.targetобъект на самом деле обрабатывает событие, иd.method его метод. Вы только должны удалить это.

Это прекрасно работает. Больше нет объектов, не являющихся GC'ами из-за обработчиков событий.

Иван Феррер Вилла
источник
3
Пожалуйста, не пишите ответы на других языках.
Хилле
10

Я ненавидел все полные решения, показанные здесь, я сделал смесь и протестировал сейчас, работал для любого обработчика событий:

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

Легко! Спасибо за Стивена Пунака.

Я использовал его, потому что я использую общий локальный метод для удаления делегатов, а локальный метод вызывался после разных случаев, когда установлены разные делегаты.

Виниций Шнайдер
источник
4

Если вам действительно придется сделать это ... это займет отражение и достаточно времени, чтобы сделать это. Обработчики событий управляются в карте события для делегирования внутри элемента управления. Вам нужно будет

  • Отразите и получите эту карту в контрольном экземпляре.
  • Итерировать для каждого события, получить делегата
    • каждый делегат, в свою очередь, может быть цепочкой обработчиков событий. Так что вызовите obControl.RemoveHandler (событие, обработчик)

Короче, много работы. Это возможно в теории ... Я никогда не пробовал что-то подобное.

Посмотрите, сможете ли вы улучшить контроль / дисциплину на этапе подписки-отписки для контроля.

Gishu
источник
3

Стивен имеет право. Это очень просто:

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
источник
38
Боже, компилятор должен запретить такие имена переменных. graphs_must_be_redrawn по-французски.
gracchus
4
Перевод с французского foreach (EventHandler<MyCompletedArgs> handler in CompletionCompleted.GetInvocationList()) { CompletionCompleted -= handler; }
Антон К
Английский перевод @AntonK работает хорошо. Не забудьте проверить нулевое значение в обработчике свойств.
Бретт
2

Я только что нашел, как приостановить события при установке свойства элемента управления WinForms . Это удалит все события из элемента управления:

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
источник
1
Это было очень полезно, но есть одна вещь, которую нужно изменить: в Resume () вы добавляете обработчики обратно в обратном порядке (я предполагаю, что это копирование / вставка из Suppress, где вы хотите работать в обратном направлении, так что как не связываться с коллекцией, которую вы повторяете). Некоторые коды рассчитывают на обработчики, запускаемые в заданном порядке, поэтому не стоит с этим связываться.
Майкл
1

Ух ты. Я нашел это решение, но ничего не получилось, как я хотел. Но это так хорошо

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);
}
Серхио Кабрал
источник
Это, конечно, аккуратнее, чем предыдущий ответ. Это делает то же самое? Похоже, что это так, но, возможно, я что-то упустил. Кроме того, зачем вам нужно создавать новый объект, когда все, что вам нужно, это EventHandlerList? Нет ли c-tor, доступного для EventHandlerList, так что можно получить только тот, который был создан для компонента?
Майкл
1

Эта страница мне очень помогла. Код, который я получил отсюда, предназначался для удаления события нажатия кнопки. Мне нужно удалить события двойного щелчка с некоторых панелей и события щелчка с некоторых кнопок. Поэтому я сделал расширение элемента управления, которое удалит все обработчики событий для определенного события.

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

Теперь, использование этого расширения. Если вам нужно удалить события нажатия кнопки,

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

Если вам нужно удалить события двойного клика с панели,

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

Я не эксперт в C #, поэтому, если есть какие-либо ошибки, пожалуйста, простите меня и сообщите мне об этом.

Anoop Muraleedharan
источник
1
Метод расширения .CastTo <> () где именно это найдено?
ИбрарМумтаз
Вы можете написать свой собственный: public static T CastTo <T> (этот объект objectToCast) {return (T) objectToCast; }
KingOfHypocrites
0

Иногда нам приходится работать с элементами управления ThirdParty, и нам нужно создавать эти неудобные решения. Основываясь на ответе @Anoop Muraleedharan, я создал это решение с типом логического вывода и поддержкой 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]);
    }

И вы можете использовать это так

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

    var button = new Button();
    button.RemoveControlEvents("EventClick");
Jhonattan
источник
0

Я нашел другое рабочее решение Дугласа .

Этот метод удаляет все обработчики событий, которые установлены для конкретного события routet в элементе.
Используйте это как

Remove_RoutedEventHandlers(myImage, Image.MouseLeftButtonDownEvent);

Полный код:

/// <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);
}
Хилле
источник
0

удаляет все обработчики для кнопки: 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();
    }
}
Анатолий
источник
-1

Что ж, здесь есть другое решение для удаления связанного события (если у вас уже есть метод для обработки событий для элемента управления):

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);
Сусо
источник
Вы можете просто сделать это .button1.MouseDown - = Delegate.CreateDelegate (typeof (EventHandler), this, "button1_MouseDownClicked"). Так что это не поможет решить вопрос о том, как выяснить, какого делегата удалить, особенно если он был встроенным.
Softlion
-1

Это не ответ на ФП, но я решил опубликовать это здесь на случай, если это поможет другим.

  /// <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
источник
-3

Я нашел этот ответ, и он почти соответствует моим потребностям. Спасибо SwDevMan81 за класс. Я изменил его, чтобы разрешить подавление и возобновление отдельных методов, и я решил опубликовать его здесь.

// 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);
                    }
                }
            }
        }
    } 
}
Франсин
источник
8
Это запутанное решение и никогда не должно использоваться в программном обеспечении промышленного уровня. Лучший подход, как уже упоминалось: хорошо управляйте подпиской на события и отменой подписки, и вы никогда не столкнетесь с такими проблемами.
Tri Q Tran
Я согласен, что мы не должны использовать рефлексию для разветвления событий, и приложение должно управлять подпиской на события и отменой подписки. Я думаю, что обсуждаемый вопрос следует использовать во время отладки, чтобы выяснить, что мы что-то делаем. Это необходимо для устаревших приложений, которые вы реорганизуете.
Тиаго Фрейтас Леал