EternalWindows
コンボボックス / オーナードロー

今回のプログラムは、コンボボックスをオーナードローします。 これにより、コンボボックスのアイテムを独自に描画できることになります。

#include <windows.h>

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:
		hwndComboBox = CreateWindowEx(0, TEXT("COMBOBOX"), TEXT(""), WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED, 30, 30, 120, 140, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		SendMessage(hwndComboBox, CB_ADDSTRING, 0, (LPARAM)TEXT("item1"));
		SendMessage(hwndComboBox, CB_ADDSTRING, 0, (LPARAM)TEXT("item2"));
		
		SendMessage(hwndComboBox, CB_SETCURSEL, 0, 0);
		return 0;

	case WM_DRAWITEM: {
		LPDRAWITEMSTRUCT lpDraw = (LPDRAWITEMSTRUCT)lParam;

		if (lpDraw->itemData == -1)
			return 0;

		if (lpDraw->itemState & ODS_SELECTED) {
			FillRect(lpDraw->hDC, &lpDraw->rcItem, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
			SetTextColor(lpDraw->hDC, RGB(255, 0, 0));
		}
		else {
			FillRect(lpDraw->hDC, &lpDraw->rcItem, (HBRUSH)GetStockObject(WHITE_BRUSH));
			SetTextColor(lpDraw->hDC, RGB(0, 0, 0));
		}
		
		if (lpDraw->itemState & ODS_FOCUS) {
			InflateRect(&lpDraw->rcItem, -1, -1);
			DrawFocusRect(lpDraw->hDC, &lpDraw->rcItem);
		}
		
		SetBkMode(lpDraw->hDC, TRANSPARENT);
		DrawText(lpDraw->hDC, (LPTSTR)lpDraw->itemData, -1, &lpDraw->rcItem, DT_LEFT | DT_VCENTER | DT_SINGLELINE);

		return 0;
	}

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

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

コンボボックスをオーナードローするには、CBS_OWNERDRAWFIXEDを指定します。 これにより、アイテムが描画される段階でWM_DRAWITEMが送られるため、 そこでアイテムを独自に描画することになります。

case WM_DRAWITEM: {
	LPDRAWITEMSTRUCT lpDraw = (LPDRAWITEMSTRUCT)lParam;

	if (lpDraw->itemData == -1)
		return 0;

	if (lpDraw->itemState & ODS_SELECTED) {
		FillRect(lpDraw->hDC, &lpDraw->rcItem, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
		SetTextColor(lpDraw->hDC, RGB(255, 0, 0));
	}
	else {
		FillRect(lpDraw->hDC, &lpDraw->rcItem, (HBRUSH)GetStockObject(WHITE_BRUSH));
		SetTextColor(lpDraw->hDC, RGB(0, 0, 0));
	}
	
	if (lpDraw->itemState & ODS_FOCUS) {
		InflateRect(&lpDraw->rcItem, -1, -1);
		DrawFocusRect(lpDraw->hDC, &lpDraw->rcItem);
	}
	
	SetBkMode(lpDraw->hDC, TRANSPARENT);
	DrawText(lpDraw->hDC, (LPTSTR)lpDraw->itemData, -1, &lpDraw->rcItem, DT_LEFT | DT_VCENTER | DT_SINGLELINE);

	return 0;
}

itemStateにODS_SELECTEDが含まれている場合は、 アイテムが選択されていることを意味するため、 この場合は独自の色でアイテムの背景を塗りつぶします。 また、テキストの色も独自に変更しています。 一方、アイテムが選択されていない場合は、 塗りつぶしの色とテキストの色は既定通りにしています。 次に、アイテムにフォーカスが割り当てられているかを調べ、 割り当てられている場合はDrawFocusRectでフォーカスを表す長方形を描画します。 InflateRectに-1を指定して範囲を縮小しているのは、長方形を少し内側に描画するためです。 最後は、DrawTextでアイテムのテキストを描画することになります。 アイテムのテキストは通常、CB_GETLBTEXTで取得するものですが、 オーナードローの場合はitemDataから参照することができます。 itemDataが-1である場合(CB_SETCURSELを実行していない場合)は描画するものがないため、 WM_DRAWITEMを直ちに終了しています。


戻る