如何修改Win32 ListView控件的滚动条颜色?
解决Win32 ListView滚动条在深色主题下显示异常的问题
问题背景
自定义Win32 ListView控件时,已将背景改为深色,但滚动条仍显示为白色,不符合需求。尝试子类化ListView并处理WM_CTLCOLORSCROLLBAR消息无效——子类化本身有效(已通过WM_NCPAINT验证),但该消息无作用,新建干净项目测试问题依旧。
原因分析
- 消息接收对象错误:
WM_CTLCOLORSCROLLBAR消息是发送给控件的父窗口,而非控件自身。因此子类化ListView无法捕获该消息,必须在主窗口的窗口过程中处理。 - 主题样式干扰:若通过manifest启用了ComCtl32 v6主题,系统会优先使用主题样式绘制滚动条,可能覆盖
WM_CTLCOLORSCROLLBAR的颜色设置。
解决方案
方案1:主窗口处理WM_CTLCOLORSCROLLBAR
在主窗口的WndProc中捕获消息,判断滚动条所属控件为目标ListView后,设置滚动条颜色。
方案2:禁用ListView主题(可选)
通过SetWindowTheme禁用ListView的主题样式,避免主题覆盖自定义颜色设置。
方案3:子类化手动绘制滚动条(灵活定制)
子类化ListView后处理WM_NCPAINT消息,完全手动绘制滚动条外观,实现高度自定义。
修改后的示例代码
#include <Windows.h> #include <CommCtrl.h> #include <string> #include <iostream> #pragma comment(lib, "Comctl32.lib") #pragma comment(linker,"\"/manifestdependency:type='win32' \ name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") bool isRunning = false; int exitCode = 0; HFONT listViewFont; HWND g_hListView = NULL; // 全局变量保存ListView句柄,用于判断滚动条归属 LRESULT CALLBACK ListViewProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR idSubclass, DWORD_PTR dwRefData) { switch (msg) { // 可选:验证子类化生效的代码(绘制红色边框) case WM_NCPAINT: { LRESULT result = DefSubclassProc(hwnd, msg, wParam, lParam); HDC hdc = GetWindowDC(hwnd); RECT rc; GetWindowRect(hwnd, &rc); HBRUSH brush = CreateSolidBrush(RGB(255,0,0)); FrameRect(hdc, &rc, brush); DeleteObject(brush); ReleaseDC(hwnd, hdc); return result; } break; default: return DefSubclassProc(hwnd, msg, wParam, lParam); } return 0; } LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_CREATE: { g_hListView = CreateWindowA("SysListView32", "", WS_CHILD | LVS_REPORT | LVS_NOCOLUMNHEADER, 50, 50, 250, 20 * 20, hWnd, NULL, NULL, NULL); SetWindowSubclass(g_hListView, ListViewProc, 0, 0); ListView_SetBkColor(g_hListView, RGB(51, 51, 51)); ListView_SetTextColor(g_hListView, RGB(255,255,255)); // 适配深色背景的文字颜色 ListView_SetExtendedListViewStyle(g_hListView, LVS_EX_FULLROWSELECT); SendMessageA(g_hListView, WM_SETFONT, (WPARAM)listViewFont, NULL); // 禁用ListView主题,避免样式覆盖自定义设置 SetWindowTheme(g_hListView, L"", L""); ShowWindow(g_hListView, true); UpdateWindow(g_hListView); LVCOLUMN column; column.mask = LVCF_WIDTH | LVCF_TEXT; column.pszText = (LPWSTR)L"Column"; column.cx = 250; ListView_InsertColumn(g_hListView, 0, &column); for (size_t i = 0; i < 50; i++) { LVITEM item; item.pszText = (LPWSTR)L"item"; item.mask = LVIF_TEXT; item.iItem = i; item.iSubItem = 0; ListView_InsertItem(g_hListView, &item); }; } break; case WM_CTLCOLORSCROLLBAR: { HWND hCtrl = (HWND)lParam; // 判断滚动条属于目标ListView if (hCtrl == g_hListView) { HDC hdc = (HDC)wParam; SetBkColor(hdc, RGB(51,51,51)); // 返回用于绘制滚动条背景的画刷 return (LRESULT)CreateSolidBrush(RGB(51,51,51)); } return DefWindowProcA(hWnd, msg, wParam, lParam); } break; case WM_CLOSE: { isRunning = false; PostQuitMessage(exitCode); } break; default: { return DefWindowProcA(hWnd, msg, wParam, lParam); } break; }; return DefWindowProcA(hWnd, msg, wParam, lParam); }; void InitFonts() { listViewFont = CreateFontA( 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Georgia" ); }; int main() { InitFonts(); WNDCLASSEXA wc = { 0 }; wc.cbSize = sizeof(WNDCLASSEXA); wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255)); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.lpszClassName = "_MainWindow"; wc.lpfnWndProc = WndProc; RegisterClassExA(&wc); HWND hWnd = CreateWindowExA(WS_EX_OVERLAPPEDWINDOW, "_MainWindow", "", WS_OVERLAPPEDWINDOW, 500, 200, 500, 500, NULL, NULL, NULL, NULL); ShowWindow(hWnd, true); UpdateWindow(hWnd); isRunning = true; MSG msg; while (isRunning) { if (GetMessageA(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessageA(&msg); }; }; // 清理资源 DeleteObject(listViewFont); RemoveWindowSubclass(g_hListView, ListViewProc, 0); return exitCode; }
关键修改点
- 添加全局变量
g_hListView,用于在主窗口判断滚动条所属控件。 - 在主窗口
WndProc中处理WM_CTLCOLORSCROLLBAR消息,针对目标ListView设置滚动条背景色。 - 调用
SetWindowTheme禁用ListView主题,避免样式冲突。 - 添加文字颜色设置,适配深色背景。
- 补充资源清理代码,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Erkan Demirci
相关产品推荐
相关产品推荐

