Как удалить все обработчики событий из события
создать новый обработчик события на элемент управления, вы можете сделать это
c.Click += new EventHandler(mainFormButton_Click);
или это
c.Click += mainFormButton_Click;
и чтобы удалить обработчик событий, вы можете сделать это
c.Click -= mainFormButton_Click;
но как удалить все обработчики событий из события?
16 ответов:
Я нашел решение на форумы 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]); } } }
вы, ребята, делаете это слишком тяжело для себя. Это так просто:
void OnFormClosing(object sender, FormClosingEventArgs e) { foreach(Delegate d in FindClicked.GetInvocationList()) { FindClicked -= (FindClickedHandler)d; } }
С Удаление Всех Обработчиков Событий:
напрямую нет, во многом потому, что вы нельзя просто установить событие на 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(); }
принятый ответ не полный. Он не работает для событий, объявленных как {add; remove;}
вот рабочий код:
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; }
удаление несуществующего обработчика событий не повредит. Поэтому, если вы знаете, какие обработчики могут быть, вы можете просто удалить их все. Просто у меня был похожий случай. Это может помочь в некоторых случаях.
Как:
// Add handlers... if (something) { c.Click += DoesSomething; } else { c.Click += DoesSomethingElse; } // Remove handlers... c.Click -= DoesSomething; c.Click -= DoesSomethingElse;
Я на самом деле использую этот метод, и он отлично работает. Я был "вдохновлен" кодом, написанным 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'Ed из-за обработчиков событий.
Если вы reaallly нужно этого делать... это займет размышление и довольно много времени, чтобы сделать это. Обработчики событий управляются в сопоставлении событие-делегат внутри элемента управления. Вам нужно будет
- отразить и получить карту в экземпляре управления.
- итерации для каждого события, получить делегат
- каждый делегат в свою очередь может быть цепочкой обработчиков событий. Поэтому вызовите obControl.RemoveHandler (событие, обработчик)
короче говоря, много работы. Это возможно в теории... Я никогда не пробовал ничего подобного.
посмотрите, можете ли вы лучше контролировать/дисциплинировать фазу подписки-отмены подписки для управления.
Стивен был прав. Это очень просто:
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; } } }
Я только что нашел как приостановить события при установке свойства элемента управления 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]); } } } }
Я ненавидел любые полные решения, показанные здесь, я сделал микс и протестировал сейчас, работал для любого обработчика событий:
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; } } }легко! Спасибо за Стивена Пунака.
я использовал его, потому что я использую универсальный локальный метод для удаления делегатов и локальный метод был вызван после разных случаев, когда разные делегаты устанавливаются.
Вау. Я нашел это решение, но ничего не работало так, как я хотел. Но это так хорошо:
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); }
эта страница мне очень помогла. Код, который я получил отсюда, предназначался для удаления события click из кнопки. Мне нужно удалить события двойного щелчка с некоторых панелей и нажать события с некоторых кнопок. Поэтому я сделал расширение элемента управления, которое удалит все обработчики событий для определенного события.
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]); } }Теперь, использование этого расширения. Если вам нужно удалить события click от кнопки
Button button = new Button(); button.RemoveEvents(nameof(button.EventClick));Если вам нужно удалить события doubleclick из a панель,
Panel panel = new Panel(); panel.RemoveEvents(nameof(panel.EventDoubleClick));Я не специалист в C#, так что если есть какие-либо ошибки, пожалуйста, прости меня и пожалуйста, дайте мне знать об этом.
Это не ответ на 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); }
иногда нам приходится работать с третьими сторонами управления, и мы должны построить эти неудобные решения. Основываясь на ответе @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");
Ну, здесь есть еще одно решение для удаления связанного события (если у вас уже есть метод обработки событий для элемента управления):
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);
Я нашел этот ответ, и это почти соответствует моим потребностям. Спасибо 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); } } } } } }
Comments