EternalWindows
リストビュー / カスタムドロー

オーナードローを利用すればリストビューのアイテムを独自に描画することができますが、 アイテムの色のみを変更したいような場合では、カスタムドローを利用する方が効率的といえます。 カスタムドローでは、アイテムの色が決定されるタイミングで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 hwndListView = NULL;
	
	switch (uMsg) {
		
	case WM_CREATE: {
		LVITEM               item;
		LVCOLUMN             column;
		INITCOMMONCONTROLSEX ic;
		
		ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
		ic.dwICC  = ICC_LISTVIEW_CLASSES;
		InitCommonControlsEx(&ic);
		
		hwndListView = CreateWindowEx(0, WC_LISTVIEW, TEXT(""), WS_CHILD | WS_VISIBLE | LVS_REPORT, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);

		ListView_SetExtendedListViewStyleEx(hwndListView, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);

		column.mask    = LVCF_WIDTH | LVCF_TEXT;
		column.cx      = 100;
		column.pszText = TEXT("col1");
		ListView_InsertColumn(hwndListView, 0, &column);
		
		column.mask    = LVCF_WIDTH | LVCF_TEXT | LVCF_FMT;
		column.fmt     = LVCFMT_CENTER;
		column.cx      = 100;
		column.pszText = TEXT("col2");
		ListView_InsertColumn(hwndListView, 1, &column);

		item.mask     = LVIF_TEXT;
		item.iItem    = 0;
		item.iSubItem = 0;
		item.pszText  = TEXT("item1");
		ListView_InsertItem(hwndListView, &item);
		
		item.mask     = LVIF_TEXT;
		item.iItem    = 0;
		item.iSubItem = 1;
		item.pszText  = TEXT("subitem");
		ListView_SetItem(hwndListView, &item);
		
		item.mask     = LVIF_TEXT;
		item.iItem    = 1;
		item.iSubItem = 0;
		item.pszText  = TEXT("item2");
		ListView_InsertItem(hwndListView, &item);
		
		item.mask     = LVIF_TEXT;
		item.iItem    = 1;
		item.iSubItem = 1;
		item.pszText  = TEXT("subitem");
		ListView_SetItem(hwndListView, &item);

		return 0;
	}

	case WM_NOTIFY:
		if (((LPNMHDR)lParam)->code == NM_CUSTOMDRAW) {
			LPNMLVCUSTOMDRAW lpCustomDraw = (LPNMLVCUSTOMDRAW)lParam;

			if (lpCustomDraw->nmcd.dwDrawStage == CDDS_PREPAINT)
				return CDRF_NOTIFYITEMDRAW;
			else if (lpCustomDraw->nmcd.dwDrawStage == CDDS_ITEMPREPAINT) {
				if (lpCustomDraw->nmcd.dwItemSpec == 1) {
					lpCustomDraw->clrText = RGB(255, 255, 255);
					lpCustomDraw->clrTextBk = RGB(255, 0, 0);
				}
				return CDRF_NEWFONT;
			}
			else
				;
			
			return 0;
		}
		break;
	
	case WM_SIZE:
		MoveWindow(hwndListView, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

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

今回は特定の行の色を変更するということで、リストビューの一行選択を有効にしています。 具体的には、ListView_SetExtendedListViewStyleにLVS_EX_FULLROWSELECTを指定しています。 WM_NOTIFYでNM_CUSTOMDRAWを検出した場合は、カスタムドローに関する通知が送られたこと意味します。 このときには、lParamにNMLVCUSTOMDRAW構造体のアドレスが格納されています。 カスタムドローには描画ステージというものがあり、いわばこれは描画段階です。 dwDrawStageがCDDS_PREPAINTということは、これから描画が開始されることを意味し、 ここでCDRF_NOTIFYITEMDRAWを返した場合は、引き続き描画の通知を受け取ることができます。 そして、dwDrawStageがCDDS_ITEMPREPAINTの場合は、 アイテムの描画が行われる段階であることを意味しているので、 ここでclrTextにテキスト色を、clrTextBkに背景色を指定することになります。 この処理は、インデックスが1のアイテムに対してだけ行っているため、 2行目のアイテムの色が変更されることになります。


戻る