ダイアログの作り方

System.Windows.Forms.Form クラスには2つの表示メソッド Show() と ShowDialog() があり、ShowDialog() を使用して表示されるフォームはダイアログであるかのように振舞う。
その際の動きは Visual BasicDelphi*1 を利用していた人にとっては馴染の動きと言えるのだが、自己整理の意味も含めてまとめておこうと思った。
仕事でもそこらへんの掲示板などでも、結構知らない人もいるみたいだし。

DialogResult

ダイアログはモーダル表示され、何らかのアクションによって終了するためにある。ダイアログが終了することで、ダイアログの呼び出し元は処理を再開することができるようになっていて、ダイアログは、どのように終了したかを DialogResult という終了コードで呼び出し元に通知することができる。
メッセージを通知するだけのダイアログなどの例外はあるが、ダイアログはユーザの判断や入力を促して、DialogResult を決定することが目的であると言っても過言ではない。


DialogResult は Form 自身のプロパティであり、ShowDialog() を呼び出すと DialogResult.None に設定される。
また、Form の DialogResult を DialogResult.None 以外の値に設定すると、ダイアログは終了する……つまり、ダイアログは閉じられて画面上から消える。


注意したいのは、ダイアログを閉じるために Form の Close() メソッドを利用せず、DialogResult を設定するほうが好ましいということだ。ダイアログが閉じられる場合、ダイアログの呼び出し元に終了コードを通知することになるわけだから、ダイアログを閉じるという行為が Cancel なのか、No なのか、Retry なのか、といった情報は非常に重要といえので、終了コードを設定できない Close() メソッドを呼ぶべきではない。
ただ、実際に MessageBox を含む多くのフォームでは、Windows の標準の UI としてキャプションバーなどに「閉じる」ボタンがあるため、Close() メソッドを含めたそのような操作が DialogResult.Cancel を設定することはダイアログを作る側としてだけでなく、使う側としても知っておく必要があるだろう。

DialogResult ボタン編

System.Windows.Forms.Button などのコントロールにも、DialogResult というプロパティが存在するが、当然ながら Button に対して ShowDialog() を実行できるわけではない。
ヘルプを参照してもらえばわかることだが、ボタンに対して DialogResult.None 以外の DialogResult を設定した場合、そのボタンはフォームに対する「閉じるボタン」であるとみなされ、クリック操作によって Form.DialogResult = Button.DialogResult というような代入が自動的に実行される機能となる。
このため、複雑な条件をもって結果が決まるような場合をのぞいて、ほとんどのダイアログでは Form の DialogResult を設定するのではなく、OK や Cancel といった各ボタンに対して DialogResult を設定しておくことで十分である。

AcceptButton

Form にあるこのプロパティは Windows の一般的なダイアログとしての動作である「デフォルトボタン」設定するためのプロパティであり、デフォルトボタンは、ダイアログ上で ENTER キーを押すことによって自動的にクリック操作となるボタンとしてよく知られていると思う。
いくつかのマウス操作アクセラレーションアプレットは、ダイアログが表示されたときにデフォルトボタンへマウスポインタを自動移動するような機能を備えている。


また、.NET Windows Forms では、ボタン等が IButton.NotifyDefault メソッドを適切に実装している場合、見た目にも変化が与えられる。たとえば、System.Windows.Forms.Button クラスでは、デフォルトボタンであることを示すためのフォーカス矩形が描画される。


ただし、厳密にはこの見た目の変化は、AcceptButton に対して行われるものではなく、内部的に管理されるデフォルトボタンに対して行われるものである。
ためしに、フォーム上に Button を2つ3つ置いて AcceptButton を設定し、TextBox なんかも1つほど置いて実行し、TAB キーなどで配置したコントロールの間を移動して各ボタンの見た目の変化を確認してみるとよいだろう。

CancelButton

AcceptButton に対応して、ESC キーを押された場合にクリック操作とみなすためのボタンとして CancelButton プロパティにもボタンを1つ指定することができるようになっている。
こちらは Windows の標準ダイアログとは多少異なった動作となるが、MFCVCL などのフレームワークで広く採用されているものでもある。
通常、Windows のダイアログで ESC キーを押すと、IDCANCEL を伴う WM_COMMAND が発生することになっているのだが、.NET を含めた多くのフレームワークではダイアログ動作をエミュレートしているだけであるため、この動作を任意のボタンのクリック操作であるように変更していて、そのボタンの設定が CancelButton である。
よって、この CancelButton プロパティが適切に設定されていないフォームは、通常の Windows のダイアログと互換性のない動きをしてしまう。


気をつけないといけないことは、この CancelButton は Close() メソッドや ControlBox における閉じる操作に対して発生する DialogResult.Cancel とは独立しているということだろうか。
たとえば、DialogResult.No を設定したボタンを CancelButton に設定した場合、ユーザが ESC キーを押してダイアログを閉じた場合には ShowDialog() は DialogResult.No を返すのにたいして、キャプションバーの閉じるボタンや Close() メソッドの呼び出しでは常に DialogResult.Cancel が返ることになる。




まだまだ、ダイアログ作成に関して考慮しなければならない点はありそうだが、とりあえず今日はここまで。

*1:C++ Builder を含んだ VCL アプリケーション