そうまでして動的にバインドしたい理由
先日のコード片を見て大体想像はつくかと思うが、やりたかったことはIAction::Runメソッドと、GUI(Form, Control)のイベントのバインドをVisual Studio 2005のデザイナではなく、アノテーションで行うことだ。既に紹介したがIActionインタフェースを実装するActionクラス、例えばHogeActionがあったとする。
public interface IAction { void Run(); } public class HogeAction : IAciton { public void Run() { 〜 なにかのクラスを生成して処理を起動 〜 } }
このクラスはプレゼンテーションとビジネスロジックの中間層に位置するクラスであり、その文脈でFormとControlにアクセスできることとする。(コンストラクタで関連付けるFormの参照を渡したり、プロパティでセットしたり) ようはActionの実装を書くときに、プログラマがこのActionのRunメソッドを、どのコントロールのどのイベントにバインドするかを宣言的に指定できるようにしたい訳だ。
その為に、例えばEventBindings(やEventSubscribings)等のアノテーションをカスタム属性として実装する。そして、Actionの実装クラスに以下のように記述する。
[EventBindings(Control = "button1", Event = "Click")]
public class HogeAction : IAciton
{
public void Run()
{
/*この処理はbutton1のClickイベントが発火した時に実行される*/
}
}
更に、このアノテーションは複数書くことも出来る。
[EventBindings(Control = "button1", Event = "Click")]
[EventBindings(Control = "textbox1", Event = "DoubleClick")]
[EventBindings(Control = "checkbox1", Event = "KeyDown")]
public class HogeAction : IAciton
{
}
クラス側から宣言するMediatorといった趣だろうか。
デザイナでGUIのイベントハンドラ用のプレースホルダを開いたほうが余程簡単だという人もいるだろう。前にも書いたが、私はFormクラスを完全にビューとして扱いたい、コードも出来るだけ書きたくない、という考えに則っており、できるだけそれに沿いたいと考えているだけだ。将来的にはWindowsFormsすら使わないかもしれないし、という思惑もある。
あと、デザイナを使わずともHogeAction内に、以下のように匿名メソッド(デリゲート)で書く方法もある。
public class HogeAction : IAciton { public HogeAction(Form form) :base() { (form as HogeForm).button1.Click += delegate(object sender, EventArgs e) { this.Run(); }; } }
しかし、このように書くには細工が必要な場合があるのだ。Form上にドロップしたコントロールのスコープはC#の場合はデフォルトで"private"だが、このように書く場合、コントロールであるbutton1はHogeActionクラスから見えるスコープ、例えば"public"等にに変更しなくてはならないのである。これも避けたいことの一つなのだ。
アノテーションにコントロール名を書くことで、デザイナによる名前(Nameプロパティ)変更の恩恵に与れなくなる、という弱点はあるが、デザイナは恐らく内部でリファクタリング機能を呼び出しているに過ぎない。従って、リファクタリングの名前変更機能を、コントロールが定義されているフィールド名に対して実行すればアノテーション内の文字列も変更対象とできるのでさほど困らないだろう。