如何删除“按钮”的“单击”事件的所有事件处理程序?
Posted
技术标签:
【中文标题】如何删除“按钮”的“单击”事件的所有事件处理程序?【英文标题】:How would it be possible to remove all event handlers of the 'Click' event of a 'Button'? 【发布时间】:2011-10-13 06:31:23 【问题描述】:我有一个按钮控件,我需要删除所有附加到其Click event 的事件处理程序。
这怎么可能?
Button button = GetButton();
button.Click.RemoveAllEventHandlers();
【问题讨论】:
你能用 MyButtonClass 替换 Button 吗? 我认为更简单的方法是禁用按钮 我想在之后只添加一个事件处理程序,因此禁用不会有帮助。 How to remove all event handlers from a control的可能重复 我知道这是一个很老的问题,但我认为您可能应该将所选答案更改为 Douglas 给出的反射选项。 “你不能”是一个非常糟糕的答案,尤其是当有一个非常好的例子表明你现在可以。 【参考方案1】:我正在处理 WinForms 项目,我必须从 ToolStripMenuItem 中删除 click-EventHandler 并将其替换为我自己的处理程序。 (我必须修改单击 contextMenu 项时采取的操作)
对我来说,来自 user2113340 的代码不起作用。我不得不像这样修改它以使用 ToolStripMenuItem:
private void RemoveClickEvent(ToolStripMenuItem control)
FieldInfo eventClick = typeof(Control).GetField("EventClick", BindingFlags.NonPublic | BindingFlags.Static);
PropertyInfo eventsProp = typeof(Component).GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList events = (EventHandlerList)eventsProp.GetValue(control, null);
FieldInfo headInfo = events.GetType().GetField("head", BindingFlags.NonPublic | BindingFlags.Instance);
object head = headInfo.GetValue(events);
FieldInfo keyType = head.GetType().GetField("key", BindingFlags.NonPublic | BindingFlags.Instance);
object key = keyType.GetValue(head);
Delegate d1 = events[key];
events.RemoveHandler(key, d1);
【讨论】:
【参考方案2】:注意:由于我发布原始答案的问题已关闭as a duplicate 这个问题,我在这里交叉发布我的答案的改进版本。 此答案仅适用于 WPF。它不适用于 Windows 窗体或任何其他 UI 框架。
以下是一个有用的实用方法,用于删除订阅给定元素上的路由事件的所有事件处理程序。如果您愿意,您可以轻松地将其转换为扩展方法。
/// <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="routedEvent">The routed event for which to remove the event handlers.</param>
public static void RemoveRoutedEventHandlers(UIElement element, RoutedEvent routedEvent)
// Get the EventHandlersStore instance which holds event handlers for the specified element.
// The EventHandlersStore class is declared as internal.
var eventHandlersStoreProperty = typeof(UIElement).GetProperty(
"EventHandlersStore", BindingFlags.Instance | BindingFlags.NonPublic);
object eventHandlersStore = eventHandlersStoreProperty.GetValue(element, null);
// If no event handlers are subscribed, eventHandlersStore will be null.
// Credit: https://***.com/a/16392387/1149773
if (eventHandlersStore == null)
return;
// Invoke the GetRoutedEventHandlers method on the EventHandlersStore instance
// for getting an array of the subscribed event handlers.
var getRoutedEventHandlers = eventHandlersStore.GetType().GetMethod(
"GetRoutedEventHandlers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var routedEventHandlers = (RoutedEventHandlerInfo[])getRoutedEventHandlers.Invoke(
eventHandlersStore, new object[] routedEvent );
// Iteratively remove all routed event handlers from the element.
foreach (var routedEventHandler in routedEventHandlers)
element.RemoveHandler(routedEvent, routedEventHandler.Handler);
然后您可以轻松地为按钮的Click
事件调用此实用程序方法:
RemoveRoutedEventHandlers(button, Button.ClickEvent);
编辑:我复制了bug fix implemented by corona,它会在没有订阅事件处理程序时阻止该方法抛出NullReferenceException
。信用(和赞成)应该去他们的答案。
【讨论】:
是否可以在 Silverlight 中执行此操作? silverlight 没有 RoutedEventHandlerInfo。 ... 这可以扩展成扩展方法吗?那会很甜蜜...【参考方案3】:只是想稍微扩展一下道格拉斯的常规,我非常喜欢。 我发现我需要向 eventHandlersStore 添加额外的 null 检查以处理传递的元素尚未附加任何事件的任何情况。
/// <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="routedEvent">The routed event for which to remove the event handlers.</param>
public static void RemoveRoutedEventHandlers(UIElement element, RoutedEvent routedEvent)
// Get the EventHandlersStore instance which holds event handlers for the specified element.
// The EventHandlersStore class is declared as internal.
var eventHandlersStoreProperty = typeof(UIElement).GetProperty(
"EventHandlersStore", BindingFlags.Instance | BindingFlags.NonPublic);
object eventHandlersStore = eventHandlersStoreProperty.GetValue(element, null);
if (eventHandlersStore == null) return;
// Invoke the GetRoutedEventHandlers method on the EventHandlersStore instance
// for getting an array of the subscribed event handlers.
var getRoutedEventHandlers = eventHandlersStore.GetType().GetMethod(
"GetRoutedEventHandlers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var routedEventHandlers = (RoutedEventHandlerInfo[])getRoutedEventHandlers.Invoke(
eventHandlersStore, new object[] routedEvent );
// Iteratively remove all routed event handlers from the element.
foreach (var routedEventHandler in routedEventHandlers)
element.RemoveHandler(routedEvent, routedEventHandler.Handler);
【讨论】:
【参考方案4】:Jamie Dixon 发布的代码出现空错误问题,考虑到没有 Click 事件。
private void RemoveClickEvent(Control control)
// chenged "FieldInfo f1 = typeof(Control)" to "var f1 = b.GetType()". By changing to
// the type of the passed in control we can use this for any control with a click event.
// using var allows for null checking and lowering the chance of exceptions.
var fi = control.GetType().GetField("EventClick", BindingFlags.Static | BindingFlags.NonPublic);
if (fi != null)
object obj = fi.GetValue(control);
PropertyInfo pi = control.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList list = (EventHandlerList)pi.GetValue(control, null);
list.RemoveHandler(obj, list[obj]);
然后是一个小的变化,它应该适用于任何事件。
private void RemoveClickEvent(Control control, string theEvent)
// chenged "FieldInfo f1 = typeof(Control)" to "var f1 = b.GetType()". By changing to
// the type of the passed in control we can use this for any control with a click event.
// using var allows for null checking and lowering the chance of exceptions.
var fi = control.GetType().GetField(theEvent, BindingFlags.Static | BindingFlags.NonPublic);
if (fi != null)
object obj = fi.GetValue(control);
PropertyInfo pi = control.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList list = (EventHandlerList)pi.GetValue(control, null);
list.RemoveHandler(obj, list[obj]);
我想这可以做得更好,但它可以满足我当前的需求。希望这对某人有用。
【讨论】:
【参考方案5】:我在 *** 上找到了这个答案:
How to remove all event handlers from a control
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]);
原始发帖人找到了here:
【讨论】:
这是特定于实现的——答案出现在 2008 年,我什至不想说它是否适用于 .NET 4。依赖这样的东西是一个非常糟糕的主意。 谢谢,但这一行总是返回 null:typeof(Control).GetField("EventClick", BindingFlags.Static | BindingFlags.NonPublic); 我实现了一个类似的解决方案,它使用 EventHandlerList 对象的更多内部方法工作。见答案***.com/questions/11031149/…【参考方案6】:你不能,基本上 - 至少不能没有反思和很多肮脏。
事件严格来说是“订阅、取消订阅”——您不能取消订阅其他人的处理程序,就像您不能更改其他人对对象的引用一样。
【讨论】:
这正是我想要的;取消订阅别人的处理程序!我正在使用来自第三方的自定义按钮,并想删除作者的点击事件处理程序... @William:你不能,基本上 - 不能不依赖实现细节和反射,除非自定义按钮公开这种行为。事件的封装就是handler之间互不干扰。 @William: 1) 您正在以非设计方式使用控件,因此很容易导致问题。 2)您的解决方法很可能会在未来的版本中中断。 3)它不会在低信任环境中工作。基本上,你希望你的代码有多脆弱? 优点;考虑设计而不仅仅是实现。我创建了自己的自定义按钮,并从原始按钮中复制了我需要的内容(图标等)。以上是关于如何删除“按钮”的“单击”事件的所有事件处理程序?的主要内容,如果未能解决你的问题,请参考以下文章
如何在没有单独的单击事件处理程序的情况下处理多个表单提交 [关闭]
R Shiny - 使用jQuery和onclick事件处理程序删除UI