【问题标题】: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 吗?
  • 我认为更简单的方法是禁用按钮
  • 我想在之后只添加一个事件处理程序,因此禁用不会有帮助。
  • 我知道这是一个很老的问题,但我认为您可能应该将所选答案更改为 Douglas 给出的反射选项。 “你不能”是一个非常糟糕的答案,尤其是当有一个非常好的例子表明你现在可以。

标签: c# .net events event-handling


【解决方案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://stackoverflow.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】:

        我在 StackOverflow 上找到了这个答案:

        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 对象的更多内部方法工作。见答案stackoverflow.com/questions/11031149/…
        【解决方案6】:

        你不能,基本上 - 至少不能没有反思和很多肮脏。

        事件严格来说是“订阅、取消订阅”——您不能取消订阅其他人的处理程序,就像您不能更改其他人对对象的引用一样。

        【讨论】:

        • 这正是我想要的;取消订阅别人的处理程序!我正在使用来自第三方的自定义按钮,并想删除作者的点击事件处理程序...
        • @William:你不能,基本上 - 不能不依赖实现细节和反射,除非自定义按钮公开这种行为。事件的封装就是handler之间互不干扰。
        • @William: 1) 您正在以非设计方式使用控件,因此很容易导致问题。 2)您的解决方法很可能会在未来的版本中中断。 3)它不会在低信任环境中工作。基本上,你希望你的代码有多脆弱?
        • 优点;考虑设计而不仅仅是实现。我创建了自己的自定义按钮,并从原始按钮中复制了我需要的内容(图标等)。
        猜你喜欢
        • 1970-01-01
        • 2010-10-18
        • 1970-01-01
        • 2021-10-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多