EternalWindows
コンボボックス / 拡張コンボボックス

拡張コンボボックスとは、通常のコンボボックスの機能に加えて、 イメージリストをサポートしているコンボボックスのことです。 拡張コンボボックスを使用すれば、アイテムのテキストの左横にイメージを表示できるため、 今回はこの使い方を検討します。 次に使用するビットマップを示します。

次に、今回のコードを示します。

#include <windows.h>
#include <commctrl.h>

#pragma comment (lib, "comctl32.lib")

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)
{
	static HWND       hwndComboBox = NULL;
	static HIMAGELIST himl = NULL;

	switch (uMsg) {
		
	case WM_CREATE: {
		COMBOBOXEXITEM       item;
		INITCOMMONCONTROLSEX ic;
		
		ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
		ic.dwICC  = ICC_USEREX_CLASSES;
		InitCommonControlsEx(&ic);

		hwndComboBox = CreateWindowEx(0, WC_COMBOBOXEX, TEXT(""), WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST, 30, 30, 120, 140, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		
		himl = ImageList_LoadImage(NULL, TEXT("sample.bmp"), 16, CLR_NONE, RGB(255, 255, 255), IMAGE_BITMAP, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
		if (himl == NULL)
			return -1;
		
		SendMessage(hwndComboBox, CBEM_SETIMAGELIST, 0, (LPARAM)himl);
		
		item.mask           = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
		item.iItem          = 0;
		item.pszText        = TEXT("item1");
		item.iImage         = 0;
		item.iSelectedImage = 0;
		SendMessage(hwndComboBox, CBEM_INSERTITEM, 0, (LPARAM)&item);
		
		item.mask           = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
		item.iItem          = 1;
		item.pszText        = TEXT("item2");
		item.iImage         = 1;
		item.iSelectedImage = 1;
		SendMessage(hwndComboBox, CBEM_INSERTITEM, 0, (LPARAM)&item);

		return 0;
	}

	case WM_DESTROY:
		if (himl != NULL)
			ImageList_Destroy(himl);
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

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

拡張コンボボックスを作成する場合は、InitCommonControlsExにICC_USEREX_CLASSESを指定し、 ウインドウスタイルにWC_COMBOBOXEXを指定します。 ImageList_LoadImageはビットマップをイメージリストとしてロードする関数であり、 第2引数にビットマップのファイル名、第3引数にイメージの幅、第5引数に透過色を指定します。 取得したイメージリストは、CBEM_SETIMAGELISTで拡張コンボボックスに設定します。 アイテムの追加は、COMBOBOXEXITEM構造体を初期化し、これをCBEM_INSERTITEMに指定することで行います。 maskには、初期化するメンバを表す定数を指定し、 CBEIF_TEXTを指定した場合は、アイテムの名前を表すpszTextを初期化できます。 CBEIF_IMAGEとCBEIF_SELECTEDIMAGEを指定した場合は、 iImageとiSelectedImageを初期化できます。 これらは、イメージリスト上のどのイメージを使用するかを表すインデックスであり、 iImageは通常時、iSelectedImageはアイテムの選択時に参照されます。 iItemは、アイテムのインデックスを指定します。

Enterキーの検出

拡張コンボボックスは、通常のコンボボックスと比べてEnterキーの押下を検出しやすくなっています。 通常のコンボボックスの場合は、ウインドウをサブクラス化してWM_KEYDOWNを検出しなければなりませんが、 拡張コンボボックスの場合はWM_NOTIFYとして通知されます。 これを処理する例を次に示します。

#include <windows.h>
#include <commctrl.h>

#pragma comment (lib, "comctl32.lib")

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)
{
	static HWND hwndComboBox = NULL;

	switch (uMsg) {
		
	case WM_CREATE: {
		INITCOMMONCONTROLSEX ic;
		
		ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
		ic.dwICC  = ICC_USEREX_CLASSES;
		InitCommonControlsEx(&ic);

		hwndComboBox = CreateWindowEx(0, WC_COMBOBOXEX, TEXT(""), WS_CHILD | WS_VISIBLE | CBS_DROPDOWN, 30, 30, 120, 140, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);

		return 0;
	}

	case WM_NOTIFY: {
		if (((LPNMHDR)lParam)->code == CBEN_ENDEDIT && ((LPNMCBEENDEDIT)lParam)->iWhy == CBENF_RETURN) {
			COMBOBOXEXITEM item;

			item.mask    = CBEIF_TEXT;
			item.iItem   = 0;
			item.pszText = ((LPNMCBEENDEDIT)lParam)->szText;
			SendMessage(hwndComboBox, CBEM_INSERTITEM, 0, (LPARAM)&item);
		}
		return 0;
	}

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

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

ウインドウスタイルにCBS_DROPDOWNを指定しているため、拡張コンボボックスのエディットコントロールを編集することができます。 編集が終了した場合は通知コードがCBEN_ENDEDITであるWM_NOTIFYが送られ、 さらにNMCBEENDEDIT構造体のiWhyがCBENF_RETURNである場合は、Enterキーによる編集の終了であると分かります。 つまり、これらの条件がそろった場合にEnterキーが押されたとみなすことができます。 具体的に何を行うかは自由ですが、上記では入力された文字列を拡張コンボボックスに追加しています。

ところで、拡張コンボボックスは通常のコンボボックスと大きさが違いますが、 これは設定されているフォントの問題です。 次のコードを実行すれば、拡張コンボボックスの大きさが通常のコンボボックスと同一になります。

SendMessage(hwndComboBox, WM_SETFONT, (WPARAM)GetStockObject(SYSTEM_FONT), 0);

WM_SETFONTは、第3引数のフォントをコントロールに設定するメッセージです。 GetStockObject(SYSTEM_FONT)で取得できるフォントは、通常のコンボボックスなどで使用されています。



戻る