Win32 C++:如何实现无边界无分隔且抓手着色的扁平状态栏
Win32 C++ 实现无间隙扁平彩色状态栏
需求
实现类似Visual Studio/VS Code风格的扁平彩色状态栏,要求:
- 无分隔线、无区域边框
- 包含「Selected」和「Total」两个自定义区域
已尝试的方案及问题
方案1:自绘区域(SBT_OWNERDRAW | SBT_NOBORDERS)
给每个区域设置自绘标记,在WM_DRAWITEM中自定义绘制背景和文本:
SendMessage(hWndStatusBar, SB_SETTEXT, (WPARAM)itemIndex | SBT_OWNERDRAW | SBT_NOBORDERS, (LPARAM)L""); // WndProc中处理WM_DRAWITEM case WM_DRAWITEM: { LPDRAWITEMSTRUCT pDis = (LPDRAWITEMSTRUCT)lParam; // 验证是状态栏控件 RECT textRect = pDis->rcItem; SetBkMode(pDis->hDC, OPAQUE); SetBkColor(pDis->hDC, RGB(255,0,0)); // 绘制区域背景 HBRUSH hBrush = CreateSolidBrush(RGB(255,0,0)); FillRect(pDis->hDC, &pDis->rcItem, hBrush); DeleteObject(hBrush); // 绘制文本 DrawText(pDis->hDC, partText.c_str(), -1, &textRect, DT_SINGLELINE | DT_VCENTER | DT_LEFT); } break;
问题:自定义区域已着色,但状态栏仍存在未填充的边框间隙,FillRect无法覆盖间隙区域。
方案2:全局背景色+禁用主题
通过SB_SETBKCOLOR设置状态栏全局背景色,同时禁用系统主题:
SetWindowTheme(hWndStatusBar, L"", L""); SendMessage(hWndStatusBar, SB_SETBKCOLOR, 0, (LPARAM)RGB(255, 0, 0));
问题:边框间隙消除,但自定义区域(抓手区域)未被着色,无法区分不同区域。
解决方案
结合两种方案的优势,同时处理全局背景和自定义区域绘制,消除间隙并实现区域着色:
步骤1:初始化状态栏基础样式
禁用系统主题,避免默认边框干扰,同时设置全局背景色填充间隙:
// 禁用状态栏的Windows主题 SetWindowTheme(hWndStatusBar, L"", L""); // 设置状态栏全局背景色(填充间隙区域) SendMessage(hWndStatusBar, SB_SETBKCOLOR, 0, (LPARAM)RGB(240, 240, 240)); // 设置状态栏分区(示例:Selected占150px,Total占剩余空间) int parts[] = {150, -1}; SendMessage(hWndStatusBar, SB_SETPARTS, _countof(parts), (LPARAM)parts);
步骤2:设置区域为自绘模式
给「Selected」和「Total」区域标记为自绘+无边框:
// 假设itemIndex0是Selected区域,itemIndex1是Total区域 SendMessage(hWndStatusBar, SB_SETTEXT, (WPARAM)0 | SBT_OWNERDRAW | SBT_NOBORDERS, (LPARAM)L""); SendMessage(hWndStatusBar, SB_SETTEXT, (WPARAM)1 | SBT_OWNERDRAW | SBT_NOBORDERS, (LPARAM)L"");
步骤3:自定义绘制区域(WM_DRAWITEM)
调整绘制矩形覆盖间隙,同时自定义区域背景和文本样式:
case WM_DRAWITEM: { LPDRAWITEMSTRUCT pDis = (LPDRAWITEMSTRUCT)lParam; // 过滤非状态栏的绘制请求 if (pDis->hwndItem != hWndStatusBar) break; // 扩大绘制矩形,覆盖默认间隙(偏移量可根据实际情况调整) RECT fullRect = pDis->rcItem; fullRect.left -= 2; fullRect.right += 2; fullRect.top -= 1; fullRect.bottom += 1; // 区分区域设置背景色 COLORREF bgColor; COLORREF textColor; CString displayText; if (pDis->itemID == 0) { // Selected区域:蓝色背景+白色文本 bgColor = RGB(0, 120, 215); textColor = RGB(255, 255, 255); displayText = L"Selected: 5"; } else { // Total区域:浅灰背景+黑色文本 bgColor = RGB(220, 220, 220); textColor = RGB(0, 0, 0); displayText = L"Total: 20"; } // 绘制区域背景 HBRUSH hBrush = CreateSolidBrush(bgColor); FillRect(pDis->hDC, &fullRect, hBrush); DeleteObject(hBrush); // 设置文本样式并绘制 SetTextColor(pDis->hDC, textColor); SetBkMode(pDis->hDC, TRANSPARENT); RECT textRect = pDis->rcItem; textRect.left += 4; // 增加文本内边距 DrawText(pDis->hDC, displayText, -1, &textRect, DT_SINGLELINE | DT_VCENTER | DT_LEFT); } break;
原理说明
SetWindowTheme禁用系统主题后,状态栏不会渲染默认的边框和阴影;SB_SETBKCOLOR设置全局背景色,填充了方案1中出现的间隙区域;- 自绘时扩大绘制矩形,确保自定义背景完全覆盖区域及周边间隙,实现真正的无边框效果;
- 区分区域设置不同的背景色,满足「Selected」和「Total」的视觉区分需求。
内容的提问来源于stack exchange,提问作者gene b.
相关产品推荐
相关产品推荐

