Win32 API下ListView子项颜色填充失效问题排查
解决Win32 ListView单元格根据值变色的问题
核心问题及修改点
WM_NOTIFY消息处理逻辑错误
- 原代码错误地将NM_CUSTOMDRAW消息的参数转换为
LPNMLISTVIEW,实际应该使用LPNMLVCUSTOMDRAW结构。 - 不能用
SetWindowLong存储绘制结果,必须直接返回ProcessCustomDraw的返回值,否则自定义绘制的通知不会被系统正确处理。
- 原代码错误地将NM_CUSTOMDRAW消息的参数转换为
自定义绘制函数缺少默认返回值
ProcessCustomDraw中如果没有匹配到任何绘制阶段,必须返回CDRF_DODEFAULT,确保系统执行默认绘制流程。
未根据单元格值动态设置颜色
- 需要通过
ListView_GetItem获取当前子项的文本,判断是"0"还是"1",分别设置红色/绿色背景。
- 需要通过
ListView项插入错误
- 原代码调用
LVM_INSERTITEM时一直传入0作为插入位置,导致所有行都覆盖到第一行,应传入循环变量i来插入多行。
- 原代码调用
修改后的完整代码
#pragma once #include <Windows.h> #include <string> #include <commctrl.h> #define WIN32_LEAN_AND_MEAN int wWrkPls = 655; int hWrkPls = 200; static HWND Matrix = NULL; static HINSTANCE hInst; LRESULT CALLBACK MainProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp); void CreateMatrix(HWND hWnd) { Matrix = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, L"", WS_CHILD | WS_VISIBLE | LVS_REPORT, 0, 0, wWrkPls - 18, hWrkPls - 38, hWnd, NULL, NULL, NULL); DWORD dwStyle = SendMessage(Matrix, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); dwStyle |= LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT; SendMessageW(Matrix, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwStyle); LVCOLUMN lvColumn; lvColumn.mask = LVCF_TEXT | LVCF_WIDTH; for (int i = 0; i < 5; i++) { lvColumn.cx = 30; lvColumn.pszText = (LPWSTR)L" "; SendMessage(Matrix, LVM_INSERTCOLUMN, i, (LPARAM)&lvColumn); } LVITEM lvItem; lvItem.mask = LVIF_TEXT; lvItem.stateMask = 0; for (int i = 0; i < 5; i++) { lvItem.iSubItem = 0; lvItem.iItem = i; lvItem.pszText = (i % 2 == 0) ? (LPWSTR)L"1" : (LPWSTR)L"0"; SendMessage(Matrix, LVM_INSERTITEM, i, (LPARAM)&lvItem); for (int j = 1; j < 5; j++) { lvItem.iSubItem = j; lvItem.pszText = (j % 2 == 0) ? (LPWSTR)L"1" : (LPWSTR)L"0"; SendMessage(Matrix, LVM_SETITEM, 0, (LPARAM)&lvItem); } } } int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPr, LPSTR args, int ncmdshow) { WNDCLASS WindowClass = { 0 }; WindowClass.hCursor = LoadCursor(NULL, IDC_ARROW); WindowClass.hInstance = hInst; WindowClass.lpszClassName = L"MainWindClass"; WindowClass.lpfnWndProc = MainProc; ::hInst = hInst; if (!RegisterClassW(&WindowClass)) { return -1; } int wCntr = GetSystemMetrics(SM_CXSCREEN) / 2; int hCntr = GetSystemMetrics(SM_CYSCREEN) / 2; HWND MainWindow = CreateWindowW(L"MainWindClass", L"ListView Color Test", WS_OVERLAPPEDWINDOW | WS_VISIBLE, wCntr - (wWrkPls / 2), hCntr - (hWrkPls / 2), wWrkPls, hWrkPls, NULL, NULL, NULL, NULL); MSG MainMsg = { 0 }; while (GetMessage(&MainMsg, NULL, NULL, NULL)) { TranslateMessage(&MainMsg); DispatchMessage(&MainMsg); } return 0; } LRESULT ProcessCustomDraw(LPARAM lp) { LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lp; switch (lplvcd->nmcd.dwDrawStage) { case CDDS_PREPAINT: return CDRF_NOTIFYITEMDRAW; case CDDS_ITEMPREPAINT: return CDRF_NOTIFYSUBITEMDRAW; case CDDS_SUBITEM | CDDS_ITEMPREPAINT: { WCHAR itemText[16] = {0}; LVITEM lvItem = {0}; lvItem.mask = LVIF_TEXT; lvItem.iItem = lplvcd->iItem; lvItem.iSubItem = lplvcd->iSubItem; lvItem.pszText = itemText; lvItem.cchTextMax = sizeof(itemText)/sizeof(WCHAR); ListView_GetItem(lplvcd->nmcd.hdr.hwndFrom, &lvItem); if (wcscmp(itemText, L"1") == 0) { lplvcd->clrTextBk = RGB(0, 255, 0); } else if (wcscmp(itemText, L"0") == 0) { lplvcd->clrTextBk = RGB(255, 0, 0); } lplvcd->clrText = RGB(255, 255, 255); return CDRF_NEWFONT; } } return CDRF_DODEFAULT; } LRESULT CALLBACK MainProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lp; if (pnmh->hwndFrom == Matrix && pnmh->code == NM_CUSTOMDRAW) { return ProcessCustomDraw(lp); } break; } case WM_CREATE: CreateMatrix(hWnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, msg, wp, lp); } return 0; }
额外说明
- 统一使用宽字符API替换原有的
CreateWindowA,避免编码不一致问题。 - 插入项时设置了交替的0和1值,方便直接测试颜色效果。
- 使用
ListView_GetItem读取单元格内容,确保能正确获取当前子项的文本值。
内容的提问来源于stack exchange,提问作者Orange Wagon
相关产品推荐
相关产品推荐

