Win10下4像素红色边框自定义非客户区绘制及重绘问题解决
Windows 10自定义非客户区边框重绘问题解决指南
我之前做Windows自定义窗口边框需求时也踩过一模一样的坑——初次显示正常,但窗口调整大小、最小化恢复后,明明WM_NCCALCSIZE和WM_NCPAINT都触发了,自定义边框就是不出来。大概率是Windows 10的DWM(桌面窗口管理器)在窗口状态变化后,悄悄覆盖了你的自定义绘制,或者是消息处理的细节没做到位。下面给你几个针对性的解决方案:
一、先禁用DWM的非客户区渲染干扰
Windows 10默认会让DWM接管非客户区的绘制,哪怕你自己处理了相关消息,它也可能在窗口状态变化后“抢着”画默认边框。解决办法是在窗口创建时,告诉DWM别管我们的非客户区:
// 放在WM_CREATE消息的处理逻辑里 #include <dwmapi.h> #pragma comment(lib, "dwmapi.lib") case WM_CREATE: { DWMNCRENDERINGPOLICY renderPolicy = DWMNCRP_DISABLED; DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &renderPolicy, sizeof(renderPolicy)); return 0; }
二、确保WM_NCCALCSIZE处理所有场景
WM_NCCALCSIZE的wParam有TRUE和FALSE两种情况,很多人只处理了其中一种,导致窗口状态变化时非客户区的尺寸计算错误。正确的处理逻辑应该覆盖两种情况:
case WM_NCCALCSIZE: { if (wParam == TRUE) { // wParam为TRUE时,lParam指向NCCALCSIZE_PARAMS结构体 NCCALCSIZE_PARAMS* pParams = reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam); // 给四边各留4像素的非客户区 pParams->rgrc[0].left += 4; pParams->rgrc[0].top += 4; pParams->rgrc[0].right -= 4; pParams->rgrc[0].bottom -= 4; return 0; // 告诉系统我们已经调整了窗口矩形 } else { // wParam为FALSE时,lParam指向RECT结构体 RECT* pClientRect = reinterpret_cast<RECT*>(lParam); pClientRect->left += 4; pClientRect->top += 4; pClientRect->right -= 4; pClientRect->bottom -= 4; return 0; } }
三、在WM_NCPAINT中强制重绘整个非客户区
有时候系统只会重绘局部非客户区,导致我们的边框没画全。你可以在WM_NCPAINT里主动获取整个非客户区的区域,再进行绘制:
case WM_NCPAINT: { HDC hdc = GetWindowDC(hwnd); HRGN hNonClientRgn = (HRGN)wParam; // 如果系统没传入区域,手动创建整个非客户区的区域 if (hNonClientRgn == NULL) { RECT rcWindow, rcClient; GetWindowRect(hwnd, &rcWindow); GetClientRect(hwnd, &rcClient); ClientToScreen(hwnd, (POINT*)&rcClient.left); ClientToScreen(hwnd, (POINT*)&rcClient.right); hNonClientRgn = CreateRectRgn(rcWindow.left, rcWindow.top, rcWindow.right, rcWindow.bottom); HRGN hClientRgn = CreateRectRgn(rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); CombineRgn(hNonClientRgn, hNonClientRgn, hClientRgn, RGN_DIFF); DeleteObject(hClientRgn); } // 绘制4像素宽的红色边框 HPEN hRedPen = CreatePen(PS_SOLID, 4, RGB(255, 0, 0)); HPEN hOldPen = (HPEN)SelectObject(hdc, hRedPen); FrameRgn(hdc, hNonClientRgn, hRedPen, 4, 4); // 清理资源 SelectObject(hdc, hOldPen); DeleteObject(hRedPen); if ((HRGN)wParam == NULL) DeleteObject(hNonClientRgn); ReleaseDC(hwnd, hdc); return 0; }
四、处理DWM颜色变化的触发重绘
如果系统的DWM颜色发生变化,也可能导致自定义边框被覆盖,我们可以在这个消息里强制触发非客户区重绘:
case WM_DWMCOLORIZATIONCOLORCHANGED: { RedrawWindow(hwnd, NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW); return 0; }
把这几个步骤结合起来,基本就能解决窗口状态变化后自定义边框不重绘的问题了。
内容的提问来源于stack exchange,提问作者krsi
相关产品推荐
相关产品推荐

