フォーム間の依存関係を最小限に

C#備忘ログ

C#でWindowsフォームを使うようなアプリケーションは殆ど書かないのであまり興味もなかったのですが、今回2つのフォームを使うツールを書いたときに困ったことがあった。

フォームA(class FormA : Form) / フォームB(class FormB : Form) の2つがあって、

1)フォームA内に配置したボタン(Button1)をクリックすると、フォームBを生成すると同時にフォームAは非表示へ。
2)フォームBを閉じると、フォームAを表示する。

通常ならたぶん、フォームAの button1の Clickイベントハンドラに フォームBをnewして Show()メソッドをコール。

//子フォームを生成して表示、自身は非表示にする
private void Button1_Click(object sender, EventArgs e)
{
  this.Hide();
  var formB = new FormB();
  formB.Show(this);
}

そしてフォームBを閉じたり、非表示になった時のイベントハンドラ(FormB_FormClosingとかFormB_VisibleChangeとか)でFormAのShow()メソッドをコール。

//親フォームを表示(復帰)
private void Form1_FormClosing(Object sender, FormClosingEventArgs e)
{
  this.Owner.Show();
}

のような感じになると思うんですが、フォームBを閉じたときに、フォームAを表示するだけじゃなくて、フォームA上のbutton1のTextプロパティの表示も変えたい!とか、button1のEnabledプロパティーを・・・とか、にしたいとき、FormA側にpublicなメソッドを一個追加してコールさせなければならない・・・。

つまり、フォームAの勝手な都合で、全然関係のないフォームBのソースも修正しなければならなくなる。フォームA,フォームBそれぞれがお互いのインスタンスを共有しなければならない、という非常に非効率な状態になる。2つのフォームだけならまだなんとかなるけど、フォーム10個とかになったときの事を考えると恐ろしい。。。

この場合の解決方法はやはり・・・フォームA側から直接フォームBのイベントに+= 演算子でラムダ式を注入するのが一番いい。Showメソッドで自分自身のインスタンスを渡す必要もない。フォームBのソースを汚染することもなくなる。

ま、ちゃんとメソッドを定義して・・・いう書き方の方がいいのか、インラインでラムダ式を放り込む方がいいのか、書き手側の好みの問題でしょうか。

//FormA
private void Button1_Click(object sender, EventArgs e)
{
  this.Hide();
  var formB = new FormB();
  formB.Show();

  //ラムダ式を注入
  formB.FormClosing += ( bs, be ) => {  Show(); button1.Text = "フォームBは閉じられた!";  };
  //デリゲートを追加
  formB.FormClosing += formBClosed; 
}
private void formBClosed(object sender, FormClosedEventArgs e)
{
    Show();
    button1.Text = "フォームBは閉じられた!";
}

また、FormBの子コントロールの特定のイベントにイベントハンドラを注入したければ、子コントロールにもフォームA側からイベントハンドラを注入してあげればいいが、フォームの子コントロールのアクセスレベルは private なので、単純に子コントロールへはアクセスできない。
そういうときは、フォームBのControlsプロパティから目的の子コントロールを得られる。

//FormA 
private void Button1_Click(object sender, EventArgs e)
{
  this.Hide();
  var formB = new FormB();
  formB.FormClosing += ( bs, be ) => {  Show();  };
  formB.Show();
  var ctrl = formB.Controls["button2"]);
  ctrl.Click += (bs,be) => button1.Text = "クリックされたよ";
}
//エラー処理はしていないよ

メインフォーム ⇔ 子フォーム間の依存関係はできるだけ避けたいし、子フォーム間同士の依存関係もできるだけ避けたい。お互いのフォームがお互いのインスタンス(正確にはインスタンスへの参照)を持ってコードを書くとフォームが増えるに従って グチャグチャになってしまう気がする。。。
気をつけよう。