菊池 Blog

移転しました 続・菊池 和彦の足跡


AILight Blogs

目次

Blog 利用状況

ニュース

移転しました http://aka-kazuk.spaces.live.com/

記事のカテゴリ

過去の記事

カテゴリ

移転先

Controlのイベントに動的に処理をアタッチする

Kazzzの「JとNの狭間で」 - Controlのイベントに動的に処理をアタッチする

Clickイベントだとこれで問題無し。しかし、当然ながらイベントの型がEventHandler以外だと例外が発生する。イベントの型はEventInfo::EventTypeで取れるので、この情報を使ってイベントハンドラ自体も生成しないと駄目。

確かにイベントハンドラの型はあわせないといけませんけど、動的に型を生成するのはそんなに面倒ではありません。

どうやるのかは以下のコードを参照下さい。

簡単に言うとGetGenericTypeDefinition からMakeGenericTypeで型が動的生成されます。

    1 using System;

    2 using System.Collections.Generic;

    3 using System.Windows.Forms;

    4 using System.Reflection;

    5 

    6 namespace WindowsApplication9

    7 {

    8 

    9     interface IAction

   10     {

   11         void Run();

   12     }

   13 

   14     class EventHandler<T> where T : EventArgs

   15     {

   16         public EventHandler( IAction action )

   17         {

   18             this.action = action;

   19         }

   20 

   21         IAction action;

   22 

   23         public void Handler(object sender,T e)

   24         {

   25             action.Run();

   26         }

   27     }

   28 

   29     static class Program

   30     {

   31         private static MethodInfo GetDelegateMethodInfo( Delegate deleg )

   32         {

   33             return deleg.Method;

   34         }

   35 

   36         public static void AttachEvent(Form form, string controlName, string eventName, IAction action)

   37         {

   38             Control[] controls = form.Controls.Find(controlName, true);

   39             if (controls != null && controls.Length > 0)

   40             {

   41                 foreach (Control control in controls)

   42                 {

   43                     EventInfo eventInfo = control.GetType().GetEvent(eventName);

   44                     if (eventInfo != null)

   45                     {

   46                         Type tHandler = eventInfo.EventHandlerType;

   47                         MethodInfo method = tHandler.GetMethod("Invoke");

   48                         ParameterInfo[] param = method.GetParameters();

   49                         Type t = typeof(WindowsApplication9.EventHandler<EventArgs>);

   50                         t = t.GetGenericTypeDefinition();

   51                         Type tHandlerInstance = t.MakeGenericType( param[1].ParameterType );

   52                         object o = tHandlerInstance.GetConstructor( new Type[] { typeof(IAction) } )

   53                                     .Invoke(new object[] { action } );           

   54                         MethodInfo m= tHandlerInstance.GetMethod("Handler");

   55 

   56                         eventInfo.AddEventHandler(control,

   57                             Delegate.CreateDelegate( eventInfo.EventHandlerType,o,m )

   58                         );

   59                     }

   60                 }

   61             }

   62         }

   63 

   64         class ActionImpl : IAction

   65         {

   66             public void Run()

   67             {

   68                 System.Diagnostics.Debug.WriteLine("Action Executed");

   69             }

   70         }

   71 

   72         /// <summary>

   73         /// The main entry point for the application.

   74         /// </summary>

   75         [STAThread]

   76         static void Main()

   77         {

   78             IAction action = new ActionImpl();

   79             Application.EnableVisualStyles();

   80             Application.SetCompatibleTextRenderingDefault(false);

   81             Form1 f = new Form1();

   82             AttachEvent( f,"textBox1","Validating",action );

   83 

   84             Application.Run(f);

   85         }

   86     }

   87 }

投稿日時 : 2006年6月23日 18:18

コメントを追加

# re: Controlのイベントに動的に処理をアタッチする 2006/06/23 18:20 菊池

あ、匿名メソッドがdelegateじゃないってのが問題だよねってのは同意。

# re: Controlのイベントに動的に処理をアタッチする 2006/06/23 20:06 Kazzz

素晴らしいです。私がやりたかったことは、正にここに書かれていることでした。
私といえば、ずっとILGeneratorでコードを動的に書いてやることにのめり込んでいたのですが、まさか匿名メソッドからコンパイラが生成していたクラスのことは日記にも書きましたが、まさかそれ自体を、それも引数をGenericsにして生成してやることは考えませんでした。(それだけ煮詰まっていたともいえます)
コードは殆どそのまま使わせていただきます。ありがとうございました。

# re: Controlのイベントに動的に処理をアタッチする 2006/06/23 22:56 菊池

> コードは殆どそのまま使わせていただきます。ありがとうございました。

どうぞご利用下さい。

Generics を使わずにって解は .NET Framework 1.1時代にCodeDomからCompileするパターンと、ILをEmitしていく方法で2回書きました。

CodeDom版は今週のクラスの記事にコードがあります。
http://www.ailight.jp/blog/kazuk/articles/6284.aspx

Generics関係の絡みでリフレクションだけでもかなり動的生成の幅が広がってますので動的生成で複雑になりすぎそうになったらGenericsをからめてみると良いと思います。

# re: Controlのイベントに動的に処理をアタッチする 2010/01/08 14:51 aetos

GetDelegateMethodInfo 要らないですよね。
あと、Generic 型の Type は

Type t = typeof(WindowsApplication9.EventHandler<>);

で取れます。

# re: Controlのイベントに動的に処理をアタッチする 2010/01/08 16:44 aetos

もうちょっと省けますね。
EventHandler.Handler は Generic である必要はなく、Handler(object, EventArgs) で大丈夫です。

# re: Controlのイベントに動的に処理をアタッチする 2010/01/10 23:23 菊池

>EventHandler.Handler は Generic である必要はなく、Handler(object, EventArgs) で大丈夫です。

まず、Kazzzさんの所の一連記事読んで来い

void Handler( object sender, EventArgs e ) な EventHandler がどんなイベントにもバインドできるならこんなblogもともと要らない。

デリゲート引数に対して共変(covariant)なコンパイラとランタイムの組み合わせなら当たり前だが、delegateのバインディングは反変(contravariant) 、引数型が異なるメソッドにdelegateはバインドできない。
少なくとも C#2.0コンパイラとランタイムではそう。
(3年半前のblogなんで、最近のコンパイラやランタイムは知らん)

# re: Controlのイベントに動的に処理をアタッチする 2010/01/12 17:20 aetos

System.EventHandler<HogeEventArgs> なイベントに System.EventHandler なメソッドを += で結びつけることは普通にできますが、AddEventHandler だと例外が出ます。
が、CreateDelegate を通してやれば、バインド先のメソッド(CreateDelegateの第3引数)は System.EventHandler であろうともちゃんと動きます、ということです。
2005では検証してませんけど。

タイトル  
名前  
URL
コメント