[C#] 1つのコントロールからイベントハンドラを「盗んで」別のコントロールに渡すことは可能ですか?


Answers

いいえ、あなたはこれをすることはできません。 理由はカプセル化です - イベントは単に購読/購読中止です。つまり、すでに購読しているハンドラを見るために内部を覗かせることはできません。

あなたができることはButtonから派生し、 OnClickを呼び出すパブリックメソッドを作成することです。 次に、 btn1をそのクラスのインスタンスにし、 btn1.RaiseClickEvent()を呼び出すbtn1.RaiseClickEvent()ハンドラを登録するか、メソッドと呼ばれるものをサブスクライバするbtn2あります。

私は本当にそれを推薦するとは思わない。 実際に何をしようとしていますか? より大きな写真は何ですか?

編集:私はあなたが反射でイベントの現在のセットをフェッチするバージョンを受け入れているが、元のコントロールでOnXXXハンドラを呼び出す代替に興味がある場合は、ここでサンプルがあります。 私はもともとすべてのイベントをコピーましたが、それは実際には非常に奇妙な影響をもたらします。 このバージョンは、誰でもCopyEvents 呼び出した後に元のボタンでイベントを購読しても、まだフックアップされていることを意味しています。つまり、2つを関連付けるときは問題ありません。

using System;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;

class Test
{
    static void Main()
    {

        TextBox output = new TextBox 
        { 
            Multiline = true,
            Height = 350,
            Width = 200,
            Location = new Point (5, 15)
        };
        Button original = new Button
        { 
            Text = "Original",
            Location = new Point (210, 15)
        };
        original.Click += Log(output, "Click!");
        original.MouseEnter += Log(output, "MouseEnter");
        original.MouseLeave += Log(output, "MouseLeave");

        Button copyCat = new Button
        {
            Text = "CopyCat",
            Location = new Point (210, 50)
        };

        CopyEvents(original, copyCat, "Click", "MouseEnter", "MouseLeave");

        Form form = new Form 
        { 
            Width = 400, 
            Height = 420,
            Controls = { output, original, copyCat }
        };

        Application.Run(form);
    }

    private static void CopyEvents(object source, object target, params string[] events)
    {
        Type sourceType = source.GetType();
        Type targetType = target.GetType();
        MethodInfo invoker = typeof(MethodAndSource).GetMethod("Invoke");
        foreach (String eventName in events)
        {
            EventInfo sourceEvent = sourceType.GetEvent(eventName);
            if (sourceEvent == null)
            {
                Console.WriteLine("Can't find {0}.{1}", sourceType.Name, eventName);
                continue;
            }

            // Note: we currently assume that all events are compatible with
            // EventHandler. This method could do with more error checks...

            MethodInfo raiseMethod = sourceType.GetMethod("On"+sourceEvent.Name, 
                                                          BindingFlags.Instance | 
                                                          BindingFlags.Public | 
                                                          BindingFlags.NonPublic);
            if (raiseMethod == null)
            {
                Console.WriteLine("Can't find {0}.On{1}", sourceType.Name, sourceEvent.Name);
                continue;
            }
            EventInfo targetEvent = targetType.GetEvent(sourceEvent.Name);
            if (targetEvent == null)
            {
                Console.WriteLine("Can't find {0}.{1}", targetType.Name, sourceEvent.Name);
                continue;
            }
            MethodAndSource methodAndSource = new MethodAndSource(raiseMethod, source);
            Delegate handler = Delegate.CreateDelegate(sourceEvent.EventHandlerType,
                                                       methodAndSource,
                                                       invoker);

            targetEvent.AddEventHandler(target, handler);
        }
    }

    private static EventHandler Log(TextBox output, string text)
    {
        return (sender, args) => output.Text += text + "\r\n";
    }

    private class MethodAndSource
    {
        private readonly MethodInfo method;
        private readonly object source;

        internal MethodAndSource(MethodInfo method, object source)
        {
            this.method = method;
            this.source = source;
        }

        public void Invoke(object sender, EventArgs args)
        {
            method.Invoke(source, new object[] { args });
        }
    }
}
Question

私はこのようなことをしたい:

Button btn1 = new Button();
btn1.Click += new EventHandler(btn1_Click);
Button btn2 = new Button();
// Take whatever event got assigned to btn1 and assign it to btn2.
btn2.Click += btn1.Click; // The compiler says no...

btn1_Clickが既にクラスで定義されている場所:

void btn1_Click(object sender, EventArgs e)
{
    //
}

もちろん、これはコンパイルされません( "'System.Windows.Forms.Control.Click'イベントは+ =または - =の左側にのみ表示されます)。 1つのコントロールからイベントハンドラを取得し、それを実行時に別のコントロールに割り当てる方法はありますか? それが不可能な場合は、イベントハンドラを複製し、実行時に他のコントロールに割り当てることができますか?

いくつかのポイント:私はしばらくの間、この1つの地獄を探検し、まだそれを行う方法が見つかりませんでした。 試行されたアプローチのほとんどは反映されているので、質問を読んで答えが非常に明白だと思うなら、まずVisual Studioでコードをコンパイルしてみてください。 答えが本当に信じられないほど明白な場合は、私にそれを叩いてください。 ありがとう、私は本当にこれが可能かどうかを見ることを楽しみにしています。

私はちょうどこれを行うことができます知っている:

btn2.Click += new EventHandler(btn1_Click);

それは私がここで探しているものではありません。

これは私が探しているものでもありません:

EventHandler handy = new EventHandler(btn1_Click);
Button btn1 = new Button();
btn1.Click += handy;
Button btn2 = new Button();
btn2.Click += handy;



ボタンとピクチャボックスに共通のイベントハンドラを使用して(以前の回答のコメントに従って)、「送信者」オブジェクトを使用して実行時にイベントを処理する方法を判断できます。