コンボボックス


エディット + リスト

さて、今回はコンボボックスを紹介しましょう
コンボボックスは、リストボックスとエディットコントロールを合わせたようなコントロールで
その仕様は、これまでのコントロールの操作を把握していれば容易に理解できます

コンボボックスを作成するには CreateWindow() 関数で
定義済みウィンドウクラス COMBOBOX を指定します

デフォルトでコンボボックスはエディットとリストボックスを併せ持つ形です
これは、CBS_SIMPLE スタイルの状態です
エディット部分のテキストは SetWindowText() や GetWindowText() でテキスト操作でき
コンボボックスのリストもリストボックスの様に CBS_SORT スタイルで自動ソートすることができます

下部のリストに項目を追加するには CB_ADDSTRING を使います
WPARAM は 0 を、LPARAM は追加する項目の文字列へのポインタを指定します
戻り値は、追加された項目の位置です
失敗した時は CB_ERR、追加するメモリスペースがない場合は CB_ERRSPACE が返ります

追加する位置を指定する場合は CB_INSERTSTRING を用います
WPARAM にインデックス、LPARAM に追加する項目の文字悦へのポインタを指定します
#include <windows.h>

LPCTSTR strItem[] = {
	TEXT("Kitty on your lap") ,
	TEXT("Tokyo mew mew") ,
	TEXT("Magical nyan nyan TARUTO") ,
	TEXT("Di Gi Charat") ,
	TEXT("Nekoneko Zoo")
};

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HWND combo;
	int i;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		combo = CreateWindow(
			TEXT("COMBOBOX") , NULL , 
			WS_CHILD | WS_VISIBLE | CBS_SORT | CBS_SIMPLE , 
			0 , 0 , 300 , 300 , hwnd , (HMENU)1 ,
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		for (i = 0 ; i < 5 ; i++)
			SendMessage(combo , CB_ADDSTRING , 0 , (LPARAM)strItem[i]);
		return 0;		
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


リストを選択すると、エディットの選択文字が表示されるなど
コンボボックスは、リスト同様に標準で様々な動作がサポートされています
基本的に、その動作はリストボックスと同じなので扱いやすいでしょう

このほかに、ドロップダウン型のコンボボックスというものもあります
一般的に、コンボボックスといえばこのドロップダウン型が使われています
このタイプは、簡単に表現すればリストの縮小版という感じです

ドロップダウン型は CBS_DROPDOWN スタイルを指定します
これで生成されたコンボボックスは、エディット部分だけが表示され
横のアイコンを押さないと、リストが表示されません

また、完全にリスト扱いの CBS_DROPDOWNLIST もあります
これは、エディットの機能がなくユーザーはテキストの編集を行えません
#include <windows.h>

LPCTSTR strItem[] = {
	TEXT("Kitty on your lap") ,
	TEXT("Tokyo mew mew") ,
	TEXT("Magical nyan nyan TARUTO") ,
	TEXT("Di Gi Charat") ,
	TEXT("Nekoneko Zoo")
};

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HWND combo;
	int i;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		combo = CreateWindow(
			TEXT("COMBOBOX") , NULL , 
			WS_CHILD | WS_VISIBLE | CBS_SORT | CBS_DROPDOWNLIST , 
			0 , 0 , 300 , 300 , hwnd , (HMENU)1 ,
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		for (i = 0 ; i < 5 ; i++)
			SendMessage(combo , CB_ADDSTRING , 0 , (LPARAM)strItem[i]);
		return 0;		
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


エディット部分をクリックすると、リストが表示されます
リストボックスを表示するスペースがない場合、コンボボックスのドロップダウンを使えば
文字1行分の幅で、リスト項目を再現させることができます

コンボボックスに送信できるメッセージは、リストボックスのメッセージと対になっているものが多く
たとえば、LB_DIR と同じ効果のある CB_DIR メッセージを送信することができます
項目を削除する時は CB_DELETESTRING
項目数を得る時は CB_GETCOUNT、選択項目は CB_GETCURSEL というように
リストボックスが持っている機能は、そのままプリフィックスを CB にしてコンボボックスでも使えます

ドロップダウン型のリストボックスで、ドロップダウンの操作をプログラムでエミュレートできます
ドロップ動作を要求するには CB_SHOWDROPDOWN メッセージを送信します
//ドロップダウンではないコンボボックスには意味がありません

WPARAM に TRUE を指定するとリストを表示し
FALSE を指定するとリストを隠します
LPARAM には 0 を指定し、戻り値は常に TRUE です

現在リストボックスが表示されているかどうかを調べるには
CB_GETDROPPEDSTATE を用います
パラメータは両方とも 0 を指定します
表示されていれば TRUE、非表示ならば FALSE が返ります
#include <windows.h>

LPCTSTR strItem[] = {
	TEXT("Kitty on your lap") ,
	TEXT("Tokyo mew mew") ,
	TEXT("Magical nyan nyan TARUTO") ,
	TEXT("Di Gi Charat") ,
	TEXT("Nekoneko Zoo")
};

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HWND combo;
	int i;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		combo = CreateWindow(
			TEXT("COMBOBOX") , NULL , 
			WS_CHILD | WS_VISIBLE | CBS_SORT | CBS_DROPDOWNLIST , 
			0 , 0 , 300 , 300 , hwnd , (HMENU)1 ,
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		for (i = 0 ; i < 5 ; i++)
			SendMessage(combo , CB_ADDSTRING , 0 , (LPARAM)strItem[i]);

		SetTimer(hwnd , 1 , 1000 , NULL);
		return 0;
	case WM_TIMER:
		if (SendMessage(combo , CB_GETDROPPEDSTATE , 0 , 0))
			SendMessage(combo , CB_SHOWDROPDOWN , FALSE , 0);
		else SendMessage(combo , CB_SHOWDROPDOWN , TRUE , 0);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}
約1秒ごとにリストを開いたり閉じたりするプログラムです
メッセージで、リストの状態を調べてそれに合わせて開け閉じしています

このほかにも多くのメッセージを渡すことができます
他のメッセージについては、メッセージ一覧を参照してください
機能的には、エディットとリストボックスのメッセージと同じメッセージが多いです


コンボボックスの通知コード

コンボボックスもまた、何らかの選択入力などがあった場合
親ウィンドウに対して WM_COMMAND を発行します

リストボックスなどと同様に、通知コードを調べることによって
ユーザーにどのような操作が行われたのかを調べることができます

定数解説
CBN_CLOSEUP リストボックスが閉じられた
CBN_DBLCLK リストボックスの項目をダブルクリック
CBN_DROPDOWN リストボックスが表示されようとしている
CBN_EDITCHANGE エディットでテキストが変更された可能性がある
CBN_EDITUPDATE エディットのテキストが変更され
エディットを表示しようとしている
CBN_ERRSPACE 十分なメモリを割り当てられない
CBN_KILLFOCUS キーボードフォーカスを失った
CBN_SELCHANGE リストボックスの選択の変更
CBN_SELENDCANCEL アイテムを選択したが、その時に他のコントロールを選択
または、ダイアログボックスを閉じた
CBN_SELENDOK アイテムを選択し、リストを閉じる
CBN_SETFOCUS キーボードフォーカスを得た

CBN_SELENDCANSEL は、ドロップダウンコンボボックスのリストを開いた状態で
別のウィンドウなどカーソルを合わせてクリックすると、選択がキャンセルされて発生します
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HWND combo , label;
	static TCHAR strText[1024];

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		combo = CreateWindow(
			TEXT("COMBOBOX") , NULL , 
			WS_CHILD | WS_VISIBLE | WS_VSCROLL |
			CBS_SORT | CBS_DROPDOWNLIST ,
			0 , 0 , 300 , 100 , hwnd , (HMENU)1 ,
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		SendMessage(combo , CB_DIR , DDL_READWRITE , (LPARAM)TEXT("*.*"));

		label = CreateWindow(
			TEXT("STATIC") , NULL , 
			WS_CHILD | WS_VISIBLE , 
			300 , 0 , 200 , 100 , hwnd , (HMENU)2 ,
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		return 0;
	case WM_COMMAND:
		if (HIWORD(wp) == CBN_SELCHANGE) {
			wsprintf(strText , TEXT("アイテム数 = %d\n選択項目 = %d") ,
				SendMessage(combo , CB_GETCOUNT , 0 , 0) ,
				SendMessage(combo , CB_GETCURSEL , 0 , 0)
			);
			SetWindowText(label , strText);
		}
			
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


項目を選択すると、そのインデックスを表示するプログラムです
項目は、実行ファイルのディレクトリ内の通常のファイルが表示されます



前のページへ戻る次のページへ