如何检查分配了哪个事件

本文关键字:事件 分配 何检查 检查 | 更新日期: 2023-09-27 18:06:04

代码如下:

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

然而,eventinfo给了我属于网格的所有控件的列表。然而在主类中只有两个事件被定义为KeyDown validation

我如何获得这些分配事件的列表,即Keydown和validation ?

如何检查分配了哪个事件

Windows Forms (WinForms)有一个棘手的组件事件模型(DataGridView是一个组件)。一些事件继承自Control(如FontChanged, ForeColorChanged等),但所有特定的组件事件存储在一个单一的EventHandlerList对象,这是继承自Component (BTW,事件从控制也存储在那里,看到在答案的末尾更新)。有一个受保护的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); }
}

可以看到,delegate (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();

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

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

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

以下文字摘自汉斯·帕桑对类似问题的回答:

Windows窗体有强大的反措施来防止这样做。大多数控件将事件处理程序引用存储在一个列表中,该列表需要秘密的"饼干"。cookie值是动态创建的,不支持先猜一下。反射是一个后门,你必须知道Cookie变量名。控制中心的那个。单击事件命名例如"EventClick",您可以在参考源代码或反射器。

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

您可以在这里找到另一个示例(仍然使用已知的事件名称)

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

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

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);
                    }
                }          
            }                     
        }
    }
}

基于Kyle的评论:

@Jacob是的…因为只有这两个事件在主类- Kyle

Events将只包含KeyDown和validation事件。

        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。keyventhandler KeyDown}

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