3

我的代码如下。

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

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

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

4

4 回答 4

5

Windows 窗体(WinForms) 有一个复杂的组件事件模型(并且DataGridView是一个组件)。一些事件继承自Control(如FontChangedForeColorChanged等),但所有特定于组件的事件都存储在单个EventHandlerList对象中,该对象继承自Component(顺便说一句,来自 Control 的事件也存储在那里,请参阅末尾的更新)回答)。有一个受保护的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. 您可以使用与上述相同的方法来获取这些密钥并检查是否附加了处理程序。

于 2013-03-26T07:54:14.837 回答
2

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

以下文字来自Hans Passant对类似问题的回答:

Windows 窗体对此有很强的反制措施。大多数控件将事件处理程序引用存储在需要秘密“cookie”的列表中。cookie 值是动态创建的,您无法预先猜到。反射是一个后门,你要知道cookie的变量名。例如,Control.Click 事件的一个名为“EventClick”,您可以在参考源或反射器中看到它。

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

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

于 2013-03-26T06:48:02.020 回答
0

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

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

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);
                    }
                }          
            }                     
        }
    }
}
于 2013-03-26T07:38:02.250 回答
-2

基于凯尔评论:

@Jacob 是的......因为这是在主类中声明的仅有的两个事件——Kyle

事件将仅包含 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 验证}

于 2013-03-26T07:08:11.070 回答