イベントハンドラのフォームからの分離

対話型システムで Windows Forms をはじめイベント駆動で処理を記述するプラットフォームでは、ドメインロジックからキーの処理まで雑多な処理が集中しがちになるといった反省から、レイヤーアーキテクチャを採用しています。書籍を読んだりインターネットの情報を吸収してそれなりの効果を上げていると考えています。

雑多な処理を分離するもう一つの方法があります。それはイベントハンドラをフォームから分離してしまうものです。下記コードはWindows Forms に対応するもので、エンターキーでフォーカス移動したいとの要望にこたえるものです(但し当方のライブラリの呼び出しがありますのでそのまま使えるものではありません)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/// <summary>
/// Enter キーが押されたら Tab キーに変換します。
/// </summary>
public class EnterToTabHandler
{
Form Form;

void FormKeyDown(object sender, KeyEventArgs e)
{
if (e.Handled) return;
if (e.KeyCode == Keys.Enter && (e.Modifiers & (Keys.Alt | Keys.Control)) == 0) {
var control = ViewUtility.GetCurrentControl(Form);
if (control is TextBoxBase && !(control as TextBoxBase).Multiline ||
control is ComboBoxBase && !(control as ComboBoxBase).DroppedDown ||
control is CheckBox || control is RadioButton) {
SendKeys.Send("{TAB}");
e.SuppressKeyPress = true;
}
}
}

/// <summary>
/// <see cref="EnterToTabHandler"/> 型のインスタンスを作成します。
/// </summary>
/// <param name="form">処理対象となるフォーム</param>
public EnterToTabHandler(Form form)
{
Form = form;
form.KeyDown += FormKeyDown;
}
}

利用するフォーム側ではEnterToTabHandlerのコンストラクタ呼び出しを1行記述だけです。

他にもボタンを押したらモデルのコマンドを実行するもの、ファンクションキーを押したら特定のボタンをクリックしたことにするものなど、多くの処理をフォームから追い出すことができます。

この方法は次のようなメリットがあります。

  • 継承を気にしないで使うことができるので機能間の分離が良い
  • 継承とは異なり、多重度に縛られない(モデルのコマンドを実行する例ではボタンとコマンドの組み合わせをいくつ持っても良い)

そしてこの方法はAccessVBAでも使えて当方では長く実績があったものです。

現在は Windows Forms でレイヤーアーキテクチャと組み合わせて使っています。従来はビューとモデルがパラレル継承階層となっていて硬直していたのですが、ビューの機能継承に縛られなくなったので、ビューはもっぱらデザインの継承階層、モデルは編集、一覧など機能での継承階層と全く別のベクトルで継承するようにしました。