이벤트에서 모든 이벤트 핸들러를 제거하는 방법


366

컨트롤에서 새 이벤트 처리기를 만들려면 다음을 수행하십시오.

c.Click += new EventHandler(mainFormButton_Click);

아니면 이거

c.Click += mainFormButton_Click;

이벤트 핸들러를 제거하려면 다음을 수행하십시오.

c.Click -= mainFormButton_Click;

그러나 이벤트에서 모든 이벤트 핸들러를 어떻게 제거합니까?


10
WPF 솔루션을 찾는 사람이 여기에 있다면 이 답변을 참조하십시오 .
더글러스

1
당신은 단지 설정할 수 c.Click = null있습니까?
alexania

이것은 내가 엄청나게 복잡하게 생각하는 것들 중 하나입니다. 간단한 Clear방법은 명백히 너무 많은 노력이었다
Zimano

답변:


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

button1이 null로 설정되면 모든 이벤트 핸들러가 button1에 연결되어 있습니까?
Damien

3
만약 내가 틀렸다면 정정 해줘,하지만해야하지의 첫 번째 줄 RemoveClickEvent에 시작 : FieldInfo f1 = typeof(Button)? 내가 사용하면 null이 GetField발생합니다 Control.
수호자 1

2
ToolStripButtons에서는 작동하지 않는 것 같습니다. RemoveClickEvent의 Button을 ToolStripButton으로 교체했지만 RemoveClickEvent를 호출 한 후에도 이벤트가 그대로 유지됩니다. 누구 든지이 문제에 대한 해결책이 있습니까?
Skalli

1
MSDN의 위의 링크는 myButton.Click + = null; 모든 대의원을 제거하려면 (Click이 아닌 다른 이벤트의 경우).
hello_earth

1
@hello_earth 작동하지 않는 것 같습니다ObservableCollection.CollectionChanged += null;
Mike de Klerk

146

너희들은이 길을 너무 힘들게 만들고있다. 이 쉬운 일이다 :

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

57
이벤트를 소유 한 경우에만 작동합니다. 컨트롤에서 해보십시오.
Delyan

226
... 이벤트를 소유하고 있다면 FindClicked = null;더 간단한 것을 작성할 수 있습니다 .
Jon Skeet

79
FindClicked 란 무엇입니까?
Levitikon

3
Kinect 이벤트에서는 작동하지 않습니다 kinect.ColorFrameReady -= MyEventHander. 그러나 Kinect GetInvocationList()인스턴스에서 델리게이트를 반복하는 방법 은 없습니다 .
브렌트 파우스트

GetInvocationList찾을 수 없습니다.
농담 황

75

모든 이벤트 핸들러 제거 에서 :

이벤트를 단순히 널로 설정할 수 없기 때문에 직접 아니오.

간접적으로 실제 이벤트를 비공개로 만들고 주변에 추가 / 삭제되는 모든 대리자를 추적하는 속성을 만들 수 있습니다.

다음을 수행하십시오.

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 컨트롤을 참조한다고 생각했습니다.
Gishu

4
당신은 컨트롤을 파생시킬 수 있습니다.
Tom Fobear

또한 두 목록을 유지 관리 합니다. 재설정에 대해서는 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;
}

4
이 버전은 나를 위해 일하고 있었다. 승인 된 버전이 작동하지 않았습니다. +1입니다.
Meister Schnitzel

1
BindingFlags.Public첫 번째 GetField통화 에서 사용할 때까지 WPF 이벤트에는 작동하지 않았습니다 .
Lennart

40

존재하지 않는 이벤트 핸들러를 삭제해도 아무런 해가 없습니다. 따라서 어떤 핸들러가 있는지 아는 경우 간단히 모든 핸들러를 삭제할 수 있습니다. 방금 비슷한 경우가있었습니다. 경우에 따라 도움이 될 수 있습니다.

처럼:

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

// Remove handlers...
c.Click -= DoesSomething;
c.Click -= DoesSomethingElse;

16

실제로이 방법을 사용하고 있으며 완벽하게 작동합니다. I이었다 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되지 않는 오브젝트가 없습니다.


2
다른 언어로 답변을 쓰지 마십시오.
Hille

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

쉬운! Stephen Punak에게 감사합니다.

나는 일반적인 로컬 메소드를 사용하여 델리게이트를 제거하고 다른 델리게이트가 설정 될 때 로컬 메소드가 다른 경우에 호출 되었기 때문에 그것을 사용했습니다.


4

실제로이 작업을 수행 해야하는 경우 ...이 작업을 수행하려면 시간이 많이 걸립니다. 이벤트 핸들러는 컨트롤 내부의 이벤트 대 델리 맵에서 관리됩니다. 당신은해야 할 것

  • 컨트롤 인스턴스에서이 맵을 반영하고 가져옵니다.
  • 각 이벤트를 반복하고 대리인을 얻습니다.
    • 각 대리인은 차례로 일련의 일련의 이벤트 처리기가 될 수 있습니다. 따라서 obControl.RemoveHandler (event, handler)를 호출하십시오.

한마디로 많은 작업이 있습니다. 이론적으로는 가능합니다. 나는 이런 식으로 시도하지 않았습니다.

제어에 대한 구독-가입 취소 단계에 대해 더 나은 제어 / 수양을 가질 수 있는지 확인하십시오.


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

38
컴파일러는 이런 종류의 변수 이름을 금지해야합니다. 프랑스어로 graphs_must_be_redrawn.
gracchus

4
프랑스어 번역 foreach (EventHandler<MyCompletedArgs> handler in CompletionCompleted.GetInvocationList()) { CompletionCompleted -= handler; }
Anton K

@AntonK의 영어 번역은 잘 작동합니다. 특성 핸들러에서 널을 점검해야합니다.
Brett

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

    }
}

1
이것은 매우 도움이되었지만 변경해야 할 사항이 하나 있습니다. Resume ()에서 처리기를 역순으로 다시 추가하고 있습니다. 반복하는 컬렉션을 망칠 필요가 없습니다). 일부 코드는 주어진 순서대로 실행되는 핸들러를 계산하므로 엉망이되어서는 안됩니다.
Michael

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 인 경우 새 객체를 만들어야하는 이유는 무엇입니까? EventHandlerList에 액세스 가능한 c-tor가 없어서 Component에 대해 내부적으로 구성된 것만 얻을 수 있습니까?
Michael

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 #의 전문가가 아니므로 버그가 있으면 저를 용서하고 친절하게 알려주십시오.


1
.CastTo <> () 확장 메소드는 정확히 어디에 있습니까?
IbrarMumtaz

다음과 같이 직접 작성할 수 있습니다. public static T CastTo <T> (this object 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");

0

Douglas의 또 다른 작업 솔루션을 찾았습니다 .

이 메소드는 요소의 특정 라우트 이벤트에 설정된 모든 이벤트 핸들러를 제거합니다.
처럼 사용

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), 이것, "button1_MouseDownClicked"). 따라서 특히 인라인 인 경우 제거 할 델리게이트를 찾는 방법에 대한 질문을 해결하는 데 도움이되지 않습니다.
Softlion

-1

이것은 OP에 대한 답변은 아니지만 다른 사람들을 도울 수 있도록 여기에 게시 할 것이라고 생각했습니다.

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

-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

이벤트 연결을 해제하기 위해 리플렉션을 사용해서는 안되며, 이벤트 구독 및 구독 취소는 애플리케이션에서 관리해야합니다. 토론의 문제는 DEBUG 시간에 사용해야한다고 생각합니다. 리팩토링중인 레거시 애플리케이션에서 필수입니다.
Tiago Freitas Leal
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.