EternalWindows
コントロール基礎 / 子ウインドウ

多くのWindowsアプリケーションはユーザーの操作を支援するために、コントロールをウインドウに表示しています。 コントロールには、ボタンやスクロールバー、ツールバーやリストビューなど様々な種類があり、 多くのユーザーはこうしたコントールの使い方に慣れています。 このことから、アプリケーションは独自のユーザーインターフェースを表示するよりも、 標準のコントロールを表示した方がよいといえます。

コントロールの正体は、子ウインドウです。 ウインドウには親子関係というものがあり、 コントロールは既存のウインドウの上に配置されることから、 既存のウインドウの子ウインドウという位置づけになっています。 また、この既存のウインドウは、子ウインドウの親ウインドウになります。 つまり、親ウインドウが作成されていなければ、コントロールを作成することはできません。 また、親ウインドウが破棄された場合は、コントロールも同時に破棄されます。 コントロールの作成には親ウインドウと同じようにCreateWindowExを使用しますが、 いくつかの引数の指定は親ウインドウと異なるため、そこを重点的に見ていきます。

hwndButton = CreateWindowEx(0, TEXT("BUTTON"), TEXT("ボタン"), WS_CHILD | WS_VISIBLE, 30, 30, 80, 40, hwndParent, (HMENU)ID_BUTTON, hinst, NULL);

第2引数には、コントロールのウインドウクラスの名前を指定します。 ここでは、BUTTONという文字列を指定していますが、これがボタンのウインドウクラスの名前です。 つまり、このCreateWindowExでボタンというコントロールが作成されることになります。 第4引数は、コントロールのウインドウスタイルを指定します。 WS_CHILDはウインドウが子ウインドウであることを意味し、 WS_VISIBLEはウインドウを作成すると同時に表示することを意味します。 第9引数は、親ウインドウのハンドルを指定することになります。 第10引数は、コントロールのIDを指定します。 コントロールを複数作成する場合は、このIDでコントロールを識別することになります。

通常、アプリケーションがウインドウを作成する場合は、 RegisterClassExを呼び出してウインドウクラスを登録しますが、 コントロールを作成する場合はこうした作業は必要ありません。 理由は、コントロールのウインドウクラスが予め登録されているからです。 次に、コントロールの種類とウインドウクラスの名前を示します。

コントロール ウインドウクラス 専用の定義
ボタン BUTTONWC_BUTTON
スタティックコントロール STATICWC_STATIC
エディットコントロール EDITWC_EDIT
リストボックス LISTBOXWC_LISTBOX
コンボボックス COMBOBOXWC_COMBOBOX
スクロールバー SCROLLBARWC_SCROLLBAR
ヘッダーコントロール SysHeader32WC_HEADER
ツールバー ToolbarWindow32TOOLBARCLASSNAME
レバー ReBarWindow32REBARCLASSNAME
ツールチップ tooltips_class32TOOLTIPS_CLASS
ステータスバー msctls_statusbar32STATUSCLASSNAME
トラックバー msctls_trackbar32TRACKBAR_CLASS
アップダウンコントロール msctls_updown32UPDOWN_CLASS
プログレスバー msctls_progress32PROGRESS_CLASS
ホットキー msctls_hotkey32HOTKEY_CLASS
SysLink SysLinkWC_LINK
リストビュー SysListView32WC_LISTVIEW
ツリービュー SysTreeView32WC_TREEVIEW
拡張コンボボックス ComboBoxEx32WC_COMBOBOXEX
タブコントロール SysTabControl32WC_TABCONTROL
アニメーション SysAnimate32ANIMATE_CLASS
カレンダー SysMonthCal32MONTHCAL_CLASS
DTP SysDateTimePick32DATETIMEPICK_CLASS
IPアドレスコントロ−ル SysIPAddress32WC_IPADDRESS
ページャー SysPagerWC_PAGESCROLLER

専用の定義というのは、単に文字列を定数化したものに過ぎません。 つまり、WC_BUTTONは"BUTTON"として定義されており、 "BUTTON"の代わりにWC_BUTTONを指定することもできます。 専用の定義を指定する場合は、commctrl.hのインクルードが必要になります。

ユーザーがコントロールに何らかの操作を行った場合は、 コントロールの親ウインドウにWM_COMMAND(またはWM_NOTIFY)が通知されます。 たとえば、ボタンの場合は、ボタンを押した際にWM_COMMANDが送られます。 次に、WM_COMMANDを処理する例を示します。

case WM_COMMAND:
	if (LOWORD(wParam) == ID_BUTTON && HIWORD(wParam) == BN_CLICKED)
		MessageBox(NULL, TEXT("ボタンが押されました。"), TEXT("OK"), MB_OK);
	return 0;

WM_COMMANDのwParamの下位ワードには、コントロールのIDが格納されます。 たとえば、これがボタンのIDと一致したならば、ボタンに関する通知であると分かります。 wParamの上位ワードには、通知コードが格納されます。 たとえば、ボタンが押された場合はBN_CLICKEDが格納されます。 WM_COMMANDのlParamには、コントロールのウインドウハンドルが格納されます。

今回のプログラムは、ボタンが選択された場合にメッセージボックスを表示します。

#include <windows.h>

#define ID_BUTTON 100

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg) {
		
	case WM_CREATE:
		CreateWindowEx(0, TEXT("BUTTON"), TEXT("ボタン"), WS_CHILD | WS_VISIBLE, 30, 30, 80, 40, hwnd, (HMENU)ID_BUTTON, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		return 0;

	case WM_COMMAND:
		MessageBox(NULL, TEXT("ボタンが押されました。"), TEXT("OK"), MB_OK);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

ボタンの作成は、WM_CREATEで行っています。 ここで作成しておけば、親ウインドウが表示される際にはボタンも表示されることになります。 CreateWindowExの第11引数にはインスタンスハンドルを指定しますが、 これはWM_CREATEのlParamに格納されているCREATESTRUCT構造体から参照できます。 CreateWindowExでボタンのウインドウハンドルを受け取っていないのは、 今回のプログラムで使用する機会がないからです。

WM_COMMANDでは、コントロールや通知コードの確認をせずにMessageBoxを呼び出していますが、 今回の場合はこれでも問題ありません。 今回は作成しているコントロールが1つだけですし、 ボタンの通知コードは主としてBN_CLICKEDになるため、 WM_COMMANDが送られた時点でボタンが押下されたものと判断することができます。


戻る