如何检查分配了哪个事件?

时间:2013-03-26 06:45:01

标签: c# winforms reflection controls

我的代码如下。

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

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

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

4 个答案:

答案 0 :(得分:4)

Windows Forms(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。您可以使用与上面相同的方法来获取这些密钥并检查处理程序是否已连接。

答案 1 :(得分:2)

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

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

  

Windows Forms有很强的反措施来阻止这样做。最   controls将事件处理程序引用存储在需要的列表中   秘密'cookie'。 cookie值是动态创建的,你不能   在前面猜它。反思是一个后门,你必须知道   cookie变量名称。 Control.Click事件的名称已命名   例如,“EventClick”,你可以在参考源或   用反射器。

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

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

答案 2 :(得分: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);
                    }
                }          
            }                     
        }
    }
}

答案 3 :(得分:-2)

基于凯尔评论:

  

@Jacob是....因为这些是在中声明的唯一两个事件   主要类 - 凯尔

事件仅包含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 Validating}