[[BORLAND C++]]
INDEX
Section.7 DialogBox
Section.6 までで、Windowsのアプリを開いて、部品を使う方法とGUI部品のクラス化について、色々とトライアルしてきた訳だが、この辺りの仕組みと、クラス化の手順が見えてきた所で、一旦クラスライブラリ化の作業はお休みして、ダイヤログボックスと、GUI以外の処理について、触れていく事にする。
最初は、以前から話はしていた、ダイヤログボックスである。これまでの所で、クラス化した事によって、かなり簡略化出来たとは思うが、『これでもまだ難しい,もっと簡単に出来ないのか??』と思う人も居るだろう?その様な場合はダイヤログボックスを使うと、もっと簡単にWindowsのアプリが構築出来る。但し、MainWindowを開いて制御する場合と比べて、EDITTEXTで扱える容量が少ない,速度が遅い等々、何かと制限が付くので、あくまでも、簡単なツール用と考えた方が良い。

  • ダイヤログとは?
    Windowsアプリの設定画面等で、灰色で四角形のオブジェクトが現れて、そこに、テキストや、ボタン等が配置されたものを見たことがあるだろうか?これがダイヤログボックスである。そういう意味では、MessageBoxの表示等も、ダイヤログだな…また、多分,Windows標準の電卓は、ダイヤログボックスだと思う…などと、色々使われている事に気付くだろう。これは、Windowsがリソースとして提供している部品なので、ある一定のフォーマットに従って、全体のサイズや配置する部品の位置等を、テキストで記述して、メインのプログラムから、DialogBox()関数等(他にもCreateDialog()も使う)で呼び出すだけで動作する代物なのである。処理速度が遅くなるとか,容量的にも制限があるとか、MDIが使えない等という制限も有る様なのだが、簡単(小規模)なツール程度であれば、全然問題無く使えるので、上手く利用すれば、これだけで色々なアプリが造れるのだから、利用しない手は無いのである。

  • ダイヤログの設計
    まず、ダイヤログを設計するのだが、前にも書いたと思うが、メインのウインドウと座標系が違うので、注意しなくてはならない。長方形の形を定義するパラメータは、CreateWindow()でも使用した、Left,Top,Width,Hightの4つであり、ダイヤログの左上が原点(0,0)というのも同じであるが、1ドットとして数える座標が違うのだ。使う環境によっても違う可能性が有るので、はっきり『こうだ!!』と言い切れない所は弱いが…自分の確認した限り、2ドット区切りで座標が切られている様だった。
    要は、これを頭に入れて、設計すれば良いのだが、簡単に設計と言っても簡単では無いのだ。出来るだけ、どこかからダイヤログエディタを、手に入れた方が良いだろう。 因みに、ダイヤログボックスとか、リソースの書式については、英語版のドキュメントが [ここ/MicrosoftのMSDNドキュメント]辺りに有ったが、日本語版は見つけられなかった。一応、自分の分かる範囲で簡単に説明しておくが、詳しくは、参考書等を参照するのが良いだろう。

    DIALOG定義の書式
    • 1行目,ダイヤログボックス全体の形と名前の定義
      nameID DIALOG x, y, width, height
      • nameIDは、メインプログラムからダイヤログを呼び出す際の、DialogBox()関数にて使用するものを記述する。
      • DIALOGの記述は、ここからがDIALOGである旨を示すものであるので、そのまま記述する。
      • x, y, width, heightは、ダイヤログボックス全体の座標を記述すれば良い。

    • 2行目〜BEGINの行まで,ダイヤログボックスのスタイル(optional-statement)
      STYLE,MENU,CAPTION,VERSION,FONT等が1行毎に指定出来る。因みに、リソースでMENUを定義しておき、MENU menunameと指定すれば、メニューを表示する事も出来る。

    • BEGINの行
      BEGIN若しくは{(右中括弧)を記述する。ここからダイヤログボックス内に配置するコントロールアイテムを記述するのだが、この記述をBEGIN〜END又は{〜}の組で囲めば良い。

    • コントロールアイテムの行
      BEGIN〜ENDの間の行に、ボタンやテキスト等のコントロールを配置するのだが、 書式は、
      control [[text,]] id, x, y, width, height[[, style[[, extended-style]]]][, helpId] [{ data-element-1 [, data-element-2 [, ... ]]}]
      となるらしい…分からない…。極一般的に使うのは、単純に,
      Control TEXT ID,x,y,width,height,style
      となるので、こちらの書式だけ覚えておけば良いと思う。
      この書式の行を、配置するコントロールの数分配置する。

    • ENDの行
      END又は}(左中括弧)を記述する。

      例)前項までの電卓を、ダイヤログに置き換えた例(座標的には若干広めに設計した)


  • アイコンの指定方法
    ダイヤログにメニューを表示する為には、上記例の様にDIALOGのoptional-statementに定義出来たのだが、アイコンについては、定義出来ない様である。Windowsのアプリなんだから、アイコンを付けたい…と考えるのは当然なので、色々試してみたら、同じリソース定義ファイル内に、アイコンファイルの定義を付けただけで、生成されたEXEファイルにアイコンが付いた。ただ、タスクバーとか、開いたダイヤログの左上には、例のアイコンが表示されないので、もっと確実な指定方法が有るのだと思う。これについては現在調査中であるが、DIALOGEX等を使えば出来るのであろうか?この辺は気にしないという方は、さっさと次に進むのだった…。

  • ダイヤログの呼び出し方
    前項までの電卓をダイヤログに移植したサンプルを用意してみた。基本的には、DialogBox()関数で呼び出せば良いのである。WinMain関数を見て、『え???これだけ?』っと思った人も居るだろう。そう、ダイヤログボックスを使えば、これだけで済んでしまうのである。ただ、やはり動作させる限りは、ダイヤログプロシージャ(サンプルではDlgProc=メッセージハンドル:Windowを作成した時のWindowプロシージャの代わりと思えば良い)を書かなければいけないので、ここの手間だけは掛かるのだが…、WinMainに、あれだけごちゃごちゃ書かなければ出来なかったものが、これだけで済んでしまうだけでも、何やら儲けた気分では無いだろうか?
    もちろん、今まで通りWindowを開いて、そこに配置したボタン等の操作で、DialogBox()関数を使えば、そこでダイヤログボックスを表示させる…などという操作も可である。因みにDialogBox()の引数は、

    int DialogBox(
      HINSTANCE hInstance, … アプリのインスタンス
      LPCTSTR lpTemplate, … リソースに定義したダイヤログの名称
      HWND hWndParent, … 親ウインドウのハンドル
      DLGPROC lpDialogFunc  … ダイヤログプロシージャを指定
      );

    である。サンプルでは、別のWindowを開かないので、親ウインドウのハンドルは、HWND_DESKTOP(デスクトップのハンドル)としているが、別途開いたWindowから起動するのであれば、当然,そちらのハンドルを指定するのだ。

    さて、この結果はソースを見ていただこう。
    テストサンプル006


    今回のサンプルには、メニューが付いているが、例によって動作は殆どダミーである。もちろんメニュー無しでも動作するのだが…。

  • ソースの説明
    目新しい項目として、ダイヤログプロシージャ(サンプルでは、DlgProc)を説明するのだが、基本的には、ウインドウ・プロシージャ(前項までのWndProc)と同等と考えて良い。ただ、必ず制御しなければいけないメッセージは、WM_INITDIALOGと、WM_COMMAND/IDCANCELのみであるので、こちらの構造も若干簡素になるのである。また、ウインドウ・プロシージャでは、定義されたメッセージに該当しない場合、return DefWindowProc(…) ;を実施していたが、ダイヤログ・プロシージャでは必要無い様だ。
    • WM_INITDIALOG
      ダイヤログが生成された時に発生するイベントと思われる。この時点で、初期化すべき変数の初期化と、今後、制御すべきコントロールへのハンドルなどを取得しておく。
      サンプルでは、CTEXTへの数字出力を行うので、hrs=GetDlgItem(hWnd,IDM_DISP) ;を行って、CTEXTのハンドルを取得している。

    • WM_COMMAND/IDCANCEL ダイヤログのシステムメニューの[×]にて、終了した場合に発生するイベントである。ここで、EndDialog() ;を行わないと、ダイヤログは消えないので、必ず行うこと!システムメニューをスタイルに持たない場合は、何かのイベント発生にて、EndDialog() ;を必ず実行する様にする事が必要である。

  • モーダルダイヤログとモードレスダイヤログ
    今回のサンプルで扱ったのは、DialogBox()にて起動した、モーダルダイヤログというタイプにあたる。今回の例では、見えにくいと思うが、別途Windowを表示してからダイヤログを表示させる,若しくはダイヤログを2個以上使う…と言った処理を行うと分かる事だが、モーダルダイヤログは、ダイヤログを終了させるまで、次の処理に進まないタイプのダイヤログである。即ち、モーダルタイプのダイヤログを表示している時は、メインウインドウの処理は止まるのである。これは、シーケンシャルにプログラムを記述したい場合,例えばパスワード入力画面を表示して、入力が終わったら、その結果を参照して次に進むと言った場合には、非常に都合が良いのだが、並列に処理したい場合,例えばメールソフトで、SMTPのログウインドウを表示したい場合には使えないという事になってしまう訳だ。ここで、登場するのがモードレスダイヤログという事になる。CreateDialog()関数にて起動したダイヤログは、並列に処理が行えるのである。即ち、後から説明するスレッド処理みたいなものである。両者共、引数は同じである為、起動は簡単であるが、後者のモードレスダイヤログは、メインプログラムが終了を関知して、消去を行わなければならないので、少なくとも、メインのWindowが親ウインドウとなっており、メインプログラムが終了時には、ダイヤログを消す処理を行わなければならないと思われる。これについても、今後、分かった時点で説明する。
2002/04/19
HomeSweetHome2
Ozzy's Software