3

私のコードは次のとおりです。

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

ただし、eventinfoは、グリッドに属するすべてのコントロールのリストを提供します。定義されているイベントは、メインクラスのKeyDownValidatingの2つだけです。

これらの割り当てられたイベント、つまりキーダウンと検証のリストを取得するにはどうすればよいですか?

4

4 に答える 4

5

Windowsフォーム(WinForms)には、コンポーネントのイベントのトリッキーなモデルがあります(DataGridViewこれはコンポーネントです)。一部のイベントControlFontChangedForeColorChangedなど)はから継承されますが、コンポーネントイベントに固有のすべては、から継承される単一の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

これがWindowsフォーム関連の質問であることを示すこの質問のコメントによると、とにかく反映せずに、リストを反復することによって割り当てられたイベントハンドラーを決定することはほとんど不可能です。

次のテキストは、同様の質問に対するHansPassantの回答からのものです。

Windowsフォームには、これを行うことに対する強力な対策があります。ほとんどのコントロールは、秘密の「cookie」を必要とするリストにイベントハンドラー参照を格納します。Cookieの値は動的に作成されるため、事前に推測することはできません。リフレクションはバックドアです。Cookie変数名を知っている必要があります。Control.Clickイベント用のものは「EventClick」という名前です。たとえば、これはリファレンスソースまたはReflectorで確認できます。

Cookieの名前を知っていると、割り当てられたイベントを取得するのに役立ちますが、反復できるすべての名前のリストがあるかどうかはわかりません。Cookie名がわかっている1つのコントロールからイベントを取得する方法を示すこの他の回答を参照してください。

ここに別の例があります(これは既知のイベント名を使用しています)。

于 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はい....メインクラスで宣言されたイベントは2つだけなので、カイル

イベントには、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 に答える