ボタンコントロール
コントロール
ウィンドウアプリは、そのウィンドウ上で様々な操作が視覚的に可能です。
アプリを操作するために重要なのがコントロールです。
コントロールは「ボタン」「チェックボックス」「リストボックス」「エディットコントロール」などが代表的なものです。
まずは最もよく使用されるであろうボタンコントロールについて説明します。
ボタンコントロールの作成
コントロールはウィンドウの一種であり、親ウィンドウ内に配置される子ウィンドウとして扱います。
ウィンドウなのでCreateWindow
関数で作成します。
(→ウィンドウの作成)
#define IDC_BUTTON1 100
HWND hBtn = CreateWindow(
L"BUTTON", L"Click",
WS_CHILD | WS_VISIBLE,
10, 10, 60, 24,
hWnd, (HMENU)IDC_BUTTON1, hInst, NULL);
第一引数はクラス名の指定ですが、ここにはBUTTON
という文字列を指定します。
今までは自分で定義した文字列(クラス名)を指定していましたが、コントロールを作成する場合はシステムクラス(システム定義済みクラス)という、あらかじめ定義されたウィンドウクラスを使用します。
ボタンコントロールを作成する場合は「BUTTON」クラスを使用するというわけです。
第二引数はボタンのキャプションです。
つまりボタン上に表示する文字列です。
第三引数のウィンドウスタイルでは、WS_CHILD
とWS_VISIBLE
を指定します。
WS_CHILD
フラグは子ウィンドウを作成するという意味で、これは必須です。
WS_VISIBLE
フラグは表示状態で作成するという意味で、必須ではないですがデフォルトでは非表示で作成されるので、通常はこのフラグで表示状態にして作成します。
コントロールのウィンドウスタイルはコントロール独自の物もいろいろと用意されているので、必要に応じて追加します。
続く四つの引数はクライアント領域上のコントロールの位置とサイズです。
(X座標,Y座標,幅,高さ)
第八引数hWndParent
には親ウィンドウのウィンドウハンドルを指定します。
この親ウィンドウ上にコントロールが配置されます。
第九引数hMenu
には子ウィンドウのウィンドウID(コントロールID)を指定します。
これは親ウィンドウが各コントロールを識別するための識別子で、実体はただの整数値です。
通常はdefineで任意の値を定義しておいてそれを指定します。
なお、要求される引数の型はHMENU型なのでキャストしないと警告がでます。
残りの引数は通常のウィンドウ作成と同じです。
戻り値は作成したコントロールのウィンドウハンドルです。
コントロールに対する操作はウィンドウハンドルを使用する場合と子ウィンドウIDを使用する場合があります。
ウィンドウハンドルはHWND型で、CreateWindow関数によりシステムが自動的に決定する値です。
子ウィンドウIDはプログラマが決定する任意の整数値です。
この二つは似ているようで別物なので注意しましょう。
WM_COMMANDメッセージ
CreateWindow関数の引数に間違いがなければ、これでボタンが作成されます。
しかし作成しただけではクリックしても何も起こりません。
ボタンをクリックすると、親ウィンドウにWM_COMMAND
メッセージが送信されます。
このメッセージの処理でボタンクリック時の動作を実装します。
サンプルコード1
簡単なボタンを作成してみます。
#include <windows.h>
#define IDC_BUTTON1 100
//ウィンドウの生成等は省略
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE: //ウィンドウ作成
//ボタンの作成
CreateWindow(
L"BUTTON", L"Click",
WS_CHILD | WS_VISIBLE,
10, 10, 60, 24,
hWnd, (HMENU)IDC_BUTTON1, hInst, NULL);
break;
case WM_COMMAND: //コントロールの操作
MessageBox(hWnd, L"ボタンをクリックしました。", L"情報", MB_OK);
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
今回はWM_CREATEメッセージ内でボタンを作成しています。
ボタンをクリックするとメッセージボックスが表示されます。
今回はボタンのウィンドウハンドル(CreateWindow関数の戻り値)は使用しないので取得していません。
上記のサンプルコードにはWM_PAINT
メッセージの処理がないことに注目してください。
コントロール類の描画はWM_PAINTメッセージに関係なく行われます。
これはコントロール自体がウィンドウであり、それぞれのコントロールが独自の描画処理を行っているためです。
コントロールは親ウィンドウが破棄される時に自動的に破棄されるため、破棄の処理を記述する必要はありません。
複数のボタンの設置
ボタンを複数設置する場合、当然ですがそれぞれのボタンで異なる処理を実装する必要がありますが、上記コードではそれが出来ていません。
WM_COMMAND
メッセージのWPARAMの下位ワード(LOWORD
マクロで取得)に、クリックしたボタンの子ウィンドウIDが格納されています。
これでボタンを判別することができます。
#include
//ウィンドウの生成等は省略
#define IDC_BUTTON1 100
#define IDC_BUTTON2 101
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE: //ウィンドウ作成
//ボタンの作成
CreateWindow(
L"BUTTON", L"ボタン1",
WS_CHILD | WS_VISIBLE,
10, 10, 70, 24,
hWnd, (HMENU)IDC_BUTTON1, hInst, NULL);
CreateWindow(
L"BUTTON", L"ボタン2",
WS_CHILD | WS_VISIBLE,
85, 10, 70, 24,
hWnd, (HMENU)IDC_BUTTON2, hInst, NULL);
break;
case WM_COMMAND: //コントロールの操作
switch (LOWORD(wParam))
{
case IDC_BUTTON1:
MessageBox(hWnd, L"ボタン1をクリックしました。", L"情報", MB_OK);
break;
case IDC_BUTTON2:
MessageBox(hWnd, L"ボタン2をクリックしました。", L"情報", MB_OK);
break;
}
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
ボタン毎に異なるメッセージを表示するサンプルです。
ボタンに限らず、コントロール類は大体この方法で処理を振り分けます。
ちなみにLPARAMにはコントロールのウィンドウハンドルが格納されています。
コントロールIDもコントロールのウィンドウハンドルも、コントロールを識別するためのものですが使い方が異なるので注意してください。
通知コード
WM_COMMAND
メッセージの受信時、WPARAMの下位ワードはコントロールの子ウィンドウIDですが、上位ワード(HIWORD
マクロで取得)には通知コードというものが格納されています。
これはボタンなどのコントロールの状態が変化したときに送られてきます。
ボタンの場合は「クリックされた」ことを状態変化としてWM_COMMANDメッセージを通知します。
このとき、WPARAMの上位ワードに「クリックされた」という通知コードが格納されています。
ボタンの通知コードには以下のようなものがあります。
定数 | 説明 |
---|---|
BN_CLICKED | ボタンがクリックされたとき |
BN_PAINT | ボタンの再描画が必要なとき Windows3.0以前との互換性のために存在 |
BN_HILITE BN_PUSHED |
ボタンが選択されたとき Windows3.0以前との互換性のために存在 |
BN_UNHILITE BN_UNPUSHED |
ボタンの選択が解除されたとき Windows3.0以前との互換性のために存在 |
BN_DISABLE | ボタンが無効化されたとき Windows3.0以前との互換性のために存在 |
BN_DOUBLECLICKED BN_DBLCLK |
ボタンがダブルクリックされたときBS_NOTIFY フラグの追加が必要 |
BN_SETFOCUS | ボタンがフォーカスを受け取ったときBS_NOTIFY フラグの追加が必要 |
BN_KILLFOCUS | ボタンがフォーカスを失ったときBS_NOTIFY フラグの追加が必要 |
クリック以外の通知コードは、ウィンドウスタイルにBS_NOTIFY
フラグの追加が必要です。
BS_NOTIFYフラグを追加するとクリック以外の操作時にもWM_COMMANDメッセージが通知されるようになるので、通知コードを見て処理を分岐させる必要があります。
#include <windows.h>
//ウィンドウの生成等は省略
#define IDC_BUTTON1 100
#define IDC_BUTTON2 101
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static const WCHAR* txt[] = {
L"- - -",
L"クリック",
L"ダブルクリック",
L"フォーカス オン",
L"フォーカス オフ"
};
static int state;
HDC hdc;
PAINTSTRUCT ps;
switch (message)
{
case WM_CREATE: //ウィンドウ作成
CreateWindow(
L"BUTTON", L"Click",
WS_CHILD | WS_VISIBLE | BS_NOTIFY, //BS_NOTIFYを追加
10, 10, 60, 24,
hWnd, (HMENU)IDC_BUTTON1, hInst, NULL);
CreateWindow( //ダミーのボタン
L"BUTTON", L"Dummy",
WS_CHILD | WS_VISIBLE,
75, 10, 60, 24,
hWnd, (HMENU)IDC_BUTTON2, hInst, NULL);
break;
case WM_COMMAND: //コントロールの操作
switch (LOWORD(wParam))
{
case IDC_BUTTON1:
switch (HIWORD(wParam)) //通知コードを調べる
{
case BN_CLICKED:
state = 1;
break;
case BN_DBLCLK:
state = 2;
break;
case BN_SETFOCUS:
state = 3;
break;
case BN_KILLFOCUS:
state = 4;
break;
}
InvalidateRect(hWnd, NULL, TRUE);
break;
}
break;
case WM_PAINT: //ウィンドウの描画
hdc = BeginPaint(hWnd, &ps);
TextOut(hdc, 10, 34, txt[state], lstrlen(txt[state]));
EndPaint(hdc, &ps);
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
実行結果です。
左側のボタンを操作する度に、下のテキストが切り替わります。
フォーカスのオン/オフの操作を確認するためにダミーのボタンを横に配置しています。
ダミーボタンはコード上は何も処理を行いませんが、クリックすることで左側のボタンからフォーカスが外れるため、BN_KILLFOCUS
の処理が行われテキストが切り替わります。
フォーカスとは、キー入力の対象になっている要素を指す言葉です。
マウスで選択したコントロールはそのままキー入力の対象になり、これを「フォーカスを得る」とか「フォーカスを受け取る」などと言います。
キー入力の対象が別のコントロールに移ることを「フォーカスが外れる」とか「フォーカスを失う」などといいます。
ボタンコントロールはマウスで選択することでフォーカスを得ることができます。
(Enterキーやスペースキーでボタンをクリックできるようになる)
コントロールによってはフォーカスを得ることができないものもあります。
ボタンのスタイル
ボタンのウィンドウスタイルには以下の定数を任意で追加できます。
定数 | 説明 |
---|---|
BS_PUSHBUTTON | プッシュボタン (通常のボタン。デフォルト) |
BS_DEFPUSHBUTTON | 既定のボタンを表す、やや強調表示されたプッシュボタン |
BS_TEXT | ボタン上にテキストを表示 (デフォルト) |
BS_ICON | ボタンにアイコンを表示 |
BS_BITMAP | ボタンにビットマップを表示 |
BS_LEFT | テキストの左揃え |
BS_RIGHT | テキストの右揃え |
BS_CENTER | テキストの水平方向の中央揃え |
BS_TOP | テキストの上揃え |
BS_BOTTOM | テキストの下揃え |
BS_VCENTER | テキストの垂直方向の中央揃え |
BS_MULTILINE | テキストがボタン矩形内に収まらない場合に、テキストを折り返して表示 |
BS_NOTIFY | 親ウィンドウにフォーカスの状態変化を通知 ( BN_SETFOCUS 、BN_KILLFOCUS ) |
BS_FLAT | フラットスタイルボタン |
BS_OWNERDRAW | オーナードローボタン コントロール内の描画をプログラマ自身が行う |
特に何も追加しない場合はBS_PUSHBUTTON
とBS_TEXT
を指定した時と同じになります。
これは通常のボタン(プッシュボタン)です。
ボタンにはプッシュボタン以外にも種類がありますが、それは別のページで改めて説明します。