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 hwndStatus = NULL;
	
	switch (uMsg) {
		
	case WM_CREATE: {
		int                  nWidth[] = {100, -1};
		INITCOMMONCONTROLSEX ic;
		
		ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
		ic.dwICC  = ICC_BAR_CLASSES;
		InitCommonControlsEx(&ic);
		
		hwndStatus = CreateWindowEx(0, STATUSCLASSNAME, TEXT(""), WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);		
		
		SendMessage(hwndStatus, SB_SETPARTS, (WPARAM)2, (LPARAM)nWidth);
		SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)0 | SBT_OWNERDRAW, 0);
		SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)1 | SBT_OWNERDRAW, 0);

		return 0;
	}
	
	case WM_DRAWITEM: {
		LPDRAWITEMSTRUCT lpDraw = (LPDRAWITEMSTRUCT)lParam;

		if (lpDraw->itemID == 0)
			DrawStatusText(lpDraw->hDC, &lpDraw->rcItem, TEXT("Part A"), SBT_POPOUT);
		else if (lpDraw->itemID == 1) {
			SetTextColor(lpDraw->hDC, RGB(255, 0, 0));
			SetBkMode(lpDraw->hDC, TRANSPARENT);
			DrawText(lpDraw->hDC, TEXT("Part B"), -1, &lpDraw->rcItem, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
		}
		else
			;

		return 0;
	}

	case WM_SIZE:
		SendMessage(hwndStatus, WM_SIZE, wParam, lParam);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

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

通常、コントロールをオーナードローする場合は、 オーナードローを示す定数をウインドウスタイルに指定しますが、 ステータスバーの場合はパート毎にSBT_OWNERDRAWという定数を指定します。 これは、SB_SETTEXTメッセージを送ることで可能です。 テキストを設定するわけでないないので、LPARAMは0で問題ありません。

オーナードローのタイミングは、WM_DRAWITEMとして通知されます。 このメッセージのLPARAMにはDRAWITEMSTRUCT構造体が格納されており、 デバイスコンテキストのハンドルを表すhDCメンバを基に描画を行います。

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

	if (lpDraw->itemID == 0) {
		DrawStatusText(lpDraw->hDC, &lpDraw->rcItem, TEXT("Part A"), SBT_POPOUT);
	}
	else if (lpDraw->itemID == 1)
		SetTextColor(lpDraw->hDC, RGB(255, 0, 0));
		SetBkMode(lpDraw->hDC, TRANSPARENT);
		DrawText(lpDraw->hDC, TEXT("Part B"), -1, &lpDraw->rcItem, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
	else
		;

	return 0;
}

itemIDは、パートのインデックスがゼロベースで格納されています。 つまり、0の場合は1番目のパートを描画すべきことを意味し、 1の場合は2番目のパートを描画すべきことを意味します。 今回はパート毎に描画方法を変える意味で、 1番目のパートはDrawStatusTextで描画し、 2番目のパートはSetTextColorで色を変更してからDrawTextで描画しています。


戻る