【问题标题】:How to check which event are assigned?如何检查分配了哪些事件?
【发布时间】:2013-03-26 06:45:01
【问题描述】:

我的代码如下。

Control[] FoundControls = null;
FoundControls = MyFunctionToFilter(TF, c => c.Name != null && c.Name.StartsWith("grid"));
var eventinfo = FoundControls[0].GetType().GetEvents();

但是,eventinfo 为我提供了属于网格的所有控件的列表。 而在主类中只定义了两个事件,即 KeyDownValidating

如何获取这些已分配事件的列表,即 Keydown 和 Validating?

【问题讨论】:

  • 您只想返回 Keydown 和 Validating 事件?
  • @Jacob 是的......因为这是在主类中声明的仅有的两个事件
  • 您的意思是“声明的事件处理程序”而不是“声明的两个事件”?
  • @Kyle 是 winforms 吗?
  • 是的,它是 winform..在我的主要表单中,我有 private void gridControl1_EditorKeyDown(object sender, KeyEventArgs e) { }

标签: c# winforms reflection controls


【解决方案1】:

Windows Forms (WinForms) 有一个复杂的组件事件模型(DataGridView 是一个组件)。一些事件继承自Control(如FontChangedForeColorChanged等),但所有特定于组件的事件都存储在单个EventHandlerList对象中,该对象继承自Component(顺便说一句,事件来自控制也存储在那里,请参阅答案末尾的更新)。有一个受保护的Events 属性:

protected EventHandlerList Events
{
    get
    {
        if (this.events == null)            
            this.events = new EventHandlerList(this);            
        return this.events;
    }
}

下面是为DataGridView 事件添加事件处理程序的方式:

public event DataGridViewCellEventHandler CellValueChanged
{
    add { Events.AddHandler(EVENT_DATAGRIDVIEWCELLVALUECHANGED, value); }
    remove { Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLVALUECHANGED, value); }
}

如您所见,委托(值)通过一些键值传递给EventHandlerList。所有事件处理程序都按键存储在那里。您可以将EventHandlerList 视为以对象为键、委托为值的字典。所以,这里是如何通过反射获取组件的事件。第一步是获取这些密钥。正如您已经注意到的,它们被命名为EVENT_XXX

private static readonly object EVENT_DATAGRIDVIEWCELLVALUECHANGED;
private static readonly object EVENT_DATAGRIDVIEWCELLMOUSEUP;
// etc.

所以我们开始吧:

var keys = typeof(DataGridView) // You can use `GetType()` of component object.
   .GetFields(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy)
   .Where(f => f.Name.StartsWith("EVENT_"));

接下来,我们需要我们的EventHandlerList

var events = typeof(DataGridView) // or GetType()
          .GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic);
// Could be null, check that
EventHandlerList handlers = events.GetValue(grid) as EventHandlerList;

最后一步,获取附加了处理程序的键列表:

var result = keys.Where(f => handlers[f.GetValue(null)] != null)
                 .ToList();

这会给你钥匙。如果您需要委托,则只需在处理程序列表中查找它们即可。

更新:从Control 继承的事件也存储在EventHandlerList 中,但由于某些未知原因,它们的键具有不同的名称,例如EventForeColor。您可以使用与上述相同的方法来获取这些密钥并检查是否附加了处理程序。

【讨论】:

  • @Kyle 谢谢,对我来说深入研究 DataGridView 实现很有趣 :)
  • +1 for Where(f => f.Name.StartsWith("EVENT_") 这就是答案
【解决方案2】:

根据这个问题中的 cmets 表明这是一个与 Windows 窗体相关的问题,几乎不可能通过迭代列表来确定分配的事件处理程序,无论如何也不是没有反射。

以下文字来自Hans Passant's answer 类似问题:

Windows 窗体对此有很强的反制措施。最多 控件将事件处理程序引用存储在需要 秘密“饼干”。 cookie 值是动态创建的,您不能 猜对了。反射是一个后门,你要知道 cookie 变量名。 Control.Click 事件的名称为 例如“EventClick”,您可以在参考源或 带反射器。

知道 cookie 名称将帮助您获得分配的事件,但我不确定是否有这样的列表,其中包含您可以迭代的所有名称。请参阅this other answer,它演示了如何从一个已知 cookie 名称的控件中获取事件。

在这里您可以找到another example(仍然使用已知的事件名称)。

【讨论】:

  • 我认为这不是正确的答案。 EventInfo 永远不会为空
  • 我得到了我之前得到的所有事件的列表
  • @TwoMore 更新的答案也是不正确。您不能只获取字段,因为在 winform 中使用了 EventHandlerList。为什么人们总是对错误的答案投赞成票?
  • 这是一个winform问题吗?
  • @TwoMore 是的,见 cmets 提问
【解决方案3】:

你不能使用反射来查看处理程序列表吗?

这是一个简单的控制台应用程序,它查看与串行端口实例的事件挂钩的处理程序:

using System;
using System.IO.Ports;
using System.Reflection;

class Program
{
    static void OnErrorReceived(object sender, SerialErrorReceivedEventArgs e){}

    static void Main(string[] args)
    {
        var serialPort = new SerialPort();

        // Add a handler so we actually get something out.
        serialPort.ErrorReceived += OnErrorReceived;  

        foreach (var eventInfo in serialPort.GetType().GetEvents())
        {
            var field = serialPort.GetType().GetField(eventInfo.Name, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField);
            if (field != null)
            {
                var backingDelegate = (Delegate)field.GetValue(serialPort);
                if (backingDelegate != null)
                {
                    var subscribedDelegates = backingDelegate.GetInvocationList();

                    foreach (var subscribedDelegate in subscribedDelegates)
                    {
                        Console.WriteLine(subscribedDelegate.Method.Name + " is hooked on " + eventInfo.Name);
                    }
                }          
            }                     
        }
    }
}

【讨论】:

    【解决方案4】:

    基于凯尔的评论:

    @Jacob 是的......因为这是在 主班——凯尔

    Events 将仅包含 KeyDown 和 Validating 事件。

            Control a = new TextBox();
            var events = a.GetType().GetEvents().Where(eventInfo => eventInfo != null && (eventInfo.Name == "KeyDown" || eventInfo.Name == "Validating"));
    

    +events.ToList()[0] {System.Windows.Forms.KeyEventHandler KeyDown}

    +events.ToList()[1] {System.ComponentModel.CancelEventHandler 验证}

    【讨论】:

    • 我没有投票给你的答案,但这不是正确的答案。 OP 想要定义哪些事件分配有处理程序
    • @lazyberezovsky:看到这个后我明白了这个问题:stackoverflow.com/questions/3855985/…。谢谢!
    猜你喜欢
    • 2016-02-14
    • 2018-10-02
    • 1970-01-01
    • 2011-12-21
    • 1970-01-01
    • 2015-08-30
    • 2010-12-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多