c.Click += new EventHandler(mainFormButton_Click);
或者这个
c.Click += mainFormButton_Click;
要移除事件处理程序,您可以执行以下操作
c.Click -= mainFormButton_Click;
但是如何从一个事件中移除所有的事件处理程序?
我在MSDN论坛上找到了解决方案。下面的示例代码将删除所有button1
中的Click
事件。
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]);
}
}
RemoveClickEvent
的第一行不应该是这样吗:FieldInfo f1 = typeof(Button)
?如果我使用 Control
,在 GetField
中会得到空值。 - Protector oneObservableCollection.CollectionChanged += null;
似乎不起作用。 - Mike de Klerk你们让这件事情变得太难了。其实很简单:
void OnFormClosing(object sender, FormClosingEventArgs e)
{
foreach(Delegate d in FindClicked.GetInvocationList())
{
FindClicked -= (FindClickedHandler)d;
}
}
FindClicked = null;
,这样更简单易懂。 - Jon Skeetkinect.ColorFrameReady -= MyEventHandler
可以,但是在 Kinect 实例上没有 GetInvocationList()
方法来迭代它们的委托。 - Brent Faust来自 删除所有事件处理程序:
直接的答案是不行的,主要是因为你不能简单地将事件设置为空。
间接地,你可以将实际事件设为私有,并创建一个围绕它的属性来跟踪添加/删除到其中的所有委托。
看下面的例子:
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;
+=
首先执行 -=
, 以确保不会意外地添加相同的处理程序两次。 - ToolmakerSteveint ID
字段,页面上有一个从 ID 到该单元格视图的映射。因此,我可以传递一个 int
参数并找到相应的视图。(我的 ID 是 N * row + column
,其中 N
大于最大列数。) - ToolmakerSteve我讨厌这里展示的任何完整解决方案,现在做了一些混合并测试,适用于任何事件处理程序:
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。
我使用它是因为我使用一种通用的本地方法来移除委托,当不同的委托被设置时,该本地方法会在不同情况下被调用。
AnotherClass
的源代码,则清除方法可以只有一行:TheEventHandler = null;
。(如果 AnotherClass
不在您的代码中,则不允许在事件上调用 GetInvocationList()
。) - ToolmakerSteve实际上我正在使用这种方法,它完美地工作。我从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
~MyClass()
{
if (MyEventEvent != null)
{
foreach (var d in MyEventEvent.GetInvocationList())
{
MyEventEvent -= (MyEvent)d;
}
}
}
MyEventEvent字段已隐藏,但它确实存在。
在调试时,可以看到d.target
是实际处理事件的对象,d.method
是其方法。您只需要将其删除。
它运作得很好。不再因为事件处理程序而导致对象无法被垃圾回收。
我刚刚发现了如何在设置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]);
}
}
}
}
这个页面对我帮助很大。我从这里得到的代码是为了从一个按钮中移除点击事件的。现在我需要从一些面板中移除双击事件以及从一些按钮中移除点击事件。因此,我创建了一个控制扩展,它可以移除特定事件的所有事件处理程序。
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#专家,如果有任何错误,请谅解并告诉我。
c.Click
设置为null
吗? - alexania