2015.12. 9.
石立 喬

Visual Studio Community 2015の使い方(4)

―― Win32 アプリケーションで、ボタン類を使用する ――

 Windowsプログラムは、GUIが重要である。Windowsフォームアプリケーションに比べて、Win32でGUIを作成するのは、やや面倒であるが、細かい設定をするには向いている。GUIはコントロールと呼ばれ、いろいろなコントロールが用意されているが、ここでは、ボタン類(プッシュボタン、チェックボックス、ラジオボタン)について紹介する。

コントロールのいろいろ
 コントロールは、すべてウィンドウの一種とみなされ、いろいろなウィンドウクラスがある。具体的には下記が主要なものである。識別子を定義する場合には、IDC_BUTTONなどと、Controlであることを示す。CreateWindow関数を用いて作成し、L"BUTTON"のようにしてクラスを指定する。
     BUTTON ------- プッシュボタン、チェックボックス、ラジオボタンなど
     EDIT ----------- テキスト入力
     COMBOBOX ---- 選択リスト、テキスト入力可能リスト、ドロップダウンリスト
     LISTBOX ------- リストボックス
     SCROLLBAR ---- スクロールバー
     STATIC -------- テキスト出力、ラベル

いろいろなスタイルのボタンの見本を作ってみる
 ここでは、コントロールの内、特にボタンについて紹介する。ボタンを作成するだけであれば、CreateWindow関数の戻り値を各コントロールのハンドルとして設定したり、識別子を対応付けたりする必要がないので、非常に簡単である。図1は、InitInstance関数内に、必要最低限で記述した例を示す。
 図1で、上から、CreateWindow関数で作成したプッシュボタン、ラジオボタン、チェックボタン、3状態チェックボタン、グループボックスの順になっている。図2は、得られたボタンコントロール類で、「チェックボタン(3-State)」にフォーカスが当たっている。キャプションのテキストの背景色が、白でないのが気になる。

使用した関数とスタイルの説明
◎CreateWindow関数
 コントロールは、親ウィンドウに対する子ウィンドウの一種と見なされるので、コントロールの作成には、CreateWindow関数を使用する。基本機能のCreateWindow関数の他に、拡張したスタイルを設定できるCreateWindowEx関数があるが、特別なデザイン以外には、使用しなくても良い。これらの関数は、InitInstance関数内で使用するのが一般的であるが、case WM_CREATE: に記述する例もある。
 この関数の引数(一番上の()で包んだ引数はCreateWindowExの場合に追加)は、
   (dwExStyle --------- WS_EX_CLIENTEDGEなど拡張ウィンドウスタイルを指定)
   lpClassName -------- L"BUTTON"などのシステムクラス名(前記の「コントロールのいろいろ」を参照)
   lpWindowName ------ L"ボタン1"などのキャプション、ラベル
   dwStyle ------------ WS_CLIENTやBS_PUSHBUTTONなど(後述の「ウィンドウのスタイル」、「ボタンのスタイル」を参照)
   x ----------------- 配置する親ウィンドウ上のX座標
   y ----------------- 配置する親ウィンドウ上のY座標
   nWidth ------------ コントロール領域の幅
   nHeight ----------- コントロール領域の高さ
   hWndParent ------- 親ウィンドウのハンドルで、一般的にはhWnd
   hMenu ------------ (HMENU)IDC_BUTTONなど、コントロール識別子
   hInstance --------- hInstなど、アプリケーションインスタンスのハンドル
   lpParam ---------- 一般的にNULLで良い
で、戻り値は、作成されたコントロールに対するハンドルである。
◎ウィンドウのスタイル
 コントロールは、ウィンドウの一種であるので、そのスタイルを選ぶには、CreateWindow関数で、ウィンドウスタイルWS(Window Style)を用いる。良く使われるのは下記である。
  WS_CHILD -------- 子ウィンドウであることを示す
  WS_VISIBLE ------- 画面上に表示する
  WS_BORDER ------ 周辺に枠を付ける(EDITクラスの場合など)
  WS_GROUP ------- ラジオボタンの場合、グループ化する
◎ボタンのスタイル
 ボタンの種類を選ぶには、CreateWindow関数で、ボタンスタイルBS(Button Style)を用いる。多く使われるのは下記である。頭にAUTOが付いているものは、あらかじめ、最低限の設定がされていて便利である。
  BS_DEFPUSHBUTTON ----- プッシュボタンと呼ばれる普通のボタンで、ダイアログボックスでは「Enter」が使える。
  BS_AUTOCHECKBOX ------ 通常のチェックボックスで、「なし」、「濃いチェック」を繰り返す。
  BS_AUTO3STATE --------- 3状態のチェックボックスで、「なし」、「濃いチェック」、「薄いチェック」を繰り返す。
  BS_AUTORADIOBUTTON --- 排他的なラジオボタンで、最初のボタンにはWS_GROUPを設定する。
  BS_GROUPBOX ----------- 入力機能やグループ化機能はなく、名称を付けて枠で囲むだけである。


図1 各種ボタンを作成するのみの簡単なプログラム



図2 得られたボタンコントロール類


ボタンを使った簡単なプログラム
 プログラムは、下記から成っている。すべて、<プロジェクト名>.cppに記述すれば良い。
 1)最初の部分で、作成する各ボタンコントロールの識別子と、金額を表すnAmountを定義しておく(図3)。
 2)MyRegisterClassで、ウィンドウの背景色を変更して、コントロールのテキストの背景に合わせる(図4、88行)。
          wcex.hbrBackGround = (HBRUSH)(COLOR_WINDOW + 1) を WINDOW_COLORに変更する。
 3)InitInstanceで、ウィンドウタイトルとウィンドウサイズを設定する(図5の110〜111行)。
 4 )InitInstanceで、各種ボタンをCreateWindow関数を用いて作成する(図5後半)。
 5)CheckRadioButton関数で、IDC_RADIO1をCHECKED状態にしておく(図5、138行)
 6)WinProcのcase WM_COMMANDの、さらにcase IDC_BUTTON1とcase IDC_BUTTON2を作成し、動作を記述する(図6)。
    IDC_BUTTON1の「所要金額」ボタンに対しては、各チェックボタンがCHECKED状態であれば、それぞれの金額を
    nAmountに加算し、IDC_RADIO2がCHECKED状態であれば、加算結果を0.8倍する。
    IDC_BUTTON2の「クリヤー」ボタンに対しては、すべてのチェックボタンをUNCHECKED状態に設定し、ラジオボタンは、
    IDC_RADIO1をCHECKED状態にし、nAmountを0にする。
 7)WinProcのcase WM_PAINTで、出力テキストの背景色を透明にし(そうしないと、白色になる)、TextOut関数を記述する(図7)。

使用した関数の説明
◎CheckRadioButton関数
 ラジオボタンの状態を設定する専用の関数で、複数のボタンの中から特定のボタンを排他的に設定できるので、便利である。引数は、
   hDlg ------------- ラジオボタンの属するダイアログボックス(この場合はメインウィンドウ)のハンドル
   nIDFirstButton ---- 最初のラジオボタンの識別子
   nIDLastButton ---- 最後のラジオボタンの識別子
   nIDCheckButton --- チェックを付けたいラジオボタンの識別子
である。
◎IsDlgItemChecked関数
 一般的なボタンコントロールの状態を知る関数で、ラジオボタンにも使用する(IsRadioButtonChecked関数は存在しないので)。引数は、
   hDlg ------------ ボタンが属するダイアログボックスのハンドル
   nIDButton ------- 状態を知りたいボタンの識別子
で、戻り値は、 Button STateで、
   BST_CHECKED ---------- チェックが付いている
   BST_UNCHECKED -------- チェックが付いていない
   BST_INDETERMINATE ----- 中間状態である(3状態ボタンの場合のみ)
のいずれかである。
◎CheckDlgItemButton関数
  一般的なボタンコントロールの状態を設定する関数で、ラジオボタンにも使用できる(CheckRadioButton関数の方が便利であるが)。引数は、
   hDlg ------------ ボタンが属するダイアログボックスのハンドル
   nIDButton ------- 設定したいボタンの識別子
   uCheck --------- 設定したい内容で、Button STateの
         BST_CHECKED ----------- チェックを付ける
        BST_UNCHECKED -------- チェックを外す
        BST_INDETERMINATE ----- 中間状態にする(3状態ボタンの場合のみ)
のいずれかを使用する。


図3 Win32Project1.cppの頭の部分で各ボタンの識別子を設定し、グローバル変数nAmountを追加する



図4 ウィンドウの背景色を、コントロールのテキストの背景色と同じにする



図5 親ウィンドウのタイトルとサイズを設定し、各種ボタンを作成する



図6 押されたボタンによって、計算したり、クリヤーしたりする



図7 ウィンドウに結果を出力する


得られた結果
 作成したプログラムを実行すると、一例として、図8の結果が得られた。


図8 チェックボックスとラジオボタンを設定し、「所要金額」ボタンを選択すると金額が表示される


付録:コントロールとのメッセージ交換に、より一般的な関数を使用する方法
 本文の図6では、コントロールから状態を取り出すのに、IsDlgButtonChecked関数を、コントロールの状態を設定するのに、CheckDlgButton関数を用いた。これらは、文字通り、DlgButton専用で、分かり易く、使い勝手が良い。
 しかし、一般的なコントロールとのメッセージのやり取りには、SendMessage関数とSendDlgItemMessage関数の二種類があり、
   SendMessage ---------- コントロールと直接にメッセージをやり取りするので、コントロールのハンドルを使う
   SendDlgItemMessage ---- コントロールの属する親ウィンドウのハンドルと、コントロールの識別子を使う
の違いがある。各コントロールにハンドルが設定されている場合には、SendMessage関数が簡単で、ハンドルが設定されていない場合には、SendDlgItemMessage関数を使用する。

◎SendMessage関数の場合
 SendMessage関数を使用する場合には、あらかじめhCheck1など、コントロールのハンドルを宣言しておき、CreateWindow関数の戻り値を代入しておく。
 関数の第一引数は対象とするコントロールのハンドル、第二引数は、メッセージである。コントロールの状態を知りたい場合(Button MessageがBM_GETCHECK)には、第三、第四の引数は不要なので、NULLまたは0にしておく。
 コントロールの状態を設定する場合(Button MessageがBM_SETCHECK)には、第三引数に、設定する状態を、Button STateのBST_UNCHECKEDまたはBST_CHECKEDで指定する。第四引数は不要なので、NULLまたは0にする。


図A SendMessage関数を使用した場合


◎SendDlgItemMessage関数の場合
 SendDlgItemMessage関数を使用する場合には、コントロール個別のハンドルは不要である。
 関数の第一引数は対象とするコントロールが属する親ウィンドウのハンドル、第二引数は、対象とするコントロールの識別子、第三引数はメッセージである。Button MessageがBM_GETCHECKの場合には、第三、第四の引数は不要である。
 Button MessageがBM_SETCHECKの場合には、第三引数に、設定する状態を、Button STateのBST_UNCHECKEDまたはBST_CHECKEDで指定する。第四引数は不要である。


図B SendDlgItemMessage関数を使用した場合







「Visual C++の勉強部屋」(目次)