Win32窗口缩放时控件闪烁且残留痕迹的解决方法咨询
Win32窗口调整大小:解决闪烁与背景残留问题
你遇到的问题是:返回FALSE给WM_ERASEBKGND虽然阻止了背景擦除导致的闪烁,但也让控件移动后留下的空白区域没有被正确填充,最终出现残留痕迹。以下是针对性的解决方法和修改后的代码:
核心解决方案
移除窗口类的
CS_HREDRAW | CS_VREDRAW样式
这个样式会在窗口宽高变化时强制整个客户区重绘,是引发频繁闪烁的主要原因之一,移除后仅重绘需要更新的区域。主窗口添加
WS_CLIPCHILDREN样式
创建窗口时添加该样式,让系统绘制主窗口背景时自动跳过子控件占据的区域,避免重复绘制导致的冲突。在
WM_PAINT中手动绘制背景
既然禁用了系统的背景擦除,就需要在WM_PAINT中主动绘制窗口背景,填充控件移动后留下的空白区域。这里使用双缓冲进一步避免闪烁。保持
WM_ERASEBKGND返回FALSE
继续阻止系统自动擦除背景,从根源避免闪烁。
修改后的完整代码
#include <vector> #include <windows.h> // Enabling Visual Styles #pragma comment(linker,"\"/manifestdependency:type='win32' \ name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") std::vector<HWND> hWndBtn; LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: { hWndBtn.push_back(CreateWindow(TEXT("BUTTON"), TEXT("Button1"), WS_VISIBLE | WS_CHILD, 100, 100, 300, 200, hWnd, NULL, NULL, NULL)); hWndBtn.push_back(CreateWindow(TEXT("BUTTON"), TEXT("Button2"), WS_VISIBLE | WS_CHILD, 100, 100, 300, 200, hWnd, NULL, NULL, NULL)); hWndBtn.push_back(CreateWindow(TEXT("BUTTON"), TEXT("Button3"), WS_VISIBLE | WS_CHILD, 100, 100, 300, 200, hWnd, NULL, NULL, NULL)); hWndBtn.push_back(CreateWindow(TEXT("BUTTON"), TEXT("Button4"), WS_VISIBLE | WS_CHILD, 100, 100, 300, 200, hWnd, NULL, NULL, NULL)); hWndBtn.push_back(CreateWindow(TEXT("BUTTON"), TEXT("Button5"), WS_VISIBLE | WS_CHILD, 100, 100, 300, 200, hWnd, NULL, NULL, NULL)); hWndBtn.push_back(CreateWindow(TEXT("BUTTON"), TEXT("Button6"), WS_VISIBLE | WS_CHILD, 100, 100, 300, 200, hWnd, NULL, NULL, NULL)); break; } case WM_ERASEBKGND: return FALSE; // 保持禁用系统背景擦除 case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); // 创建双缓冲DC HDC memDC = CreateCompatibleDC(hdc); HBITMAP memBmp = CreateCompatibleBitmap(hdc, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top); HBITMAP oldBmp = (HBITMAP)SelectObject(memDC, memBmp); // 填充背景到缓冲DC HBRUSH bgBrush = (HBRUSH)(COLOR_WINDOW + 1); FillRect(memDC, &ps.rcPaint, bgBrush); // 将缓冲内容绘制到窗口DC BitBlt(hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top, memDC, 0, 0, SRCCOPY); // 清理资源 SelectObject(memDC, oldBmp); DeleteObject(memBmp); DeleteDC(memDC); EndPaint(hWnd, &ps); break; } case WM_SIZE: { INT width = max(0, LOWORD(lParam)); INT height = max(0, HIWORD(lParam)); int y = 100; int spacing = 30; HDWP hDWP = BeginDeferWindowPos((int)hWndBtn.size()); for (int i = 0; i < hWndBtn.size(); ++i) { height = 50; DeferWindowPos( hDWP, hWndBtn[i], NULL, 100, y, width - 200, height, SWP_NOZORDER ); y = y + height + spacing; } EndDeferWindowPos(hDWP); break; } case WM_CLOSE: DestroyWindow(hWnd); break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hWnd, uMsg, wParam, lParam); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, BOOL) { WNDCLASS wc = { 0 }; wc.lpszClassName = TEXT("wndclass"); // 移除CS_HREDRAW | CS_VREDRAW,避免强制全窗口重绘 wc.style = 0; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.lpfnWndProc = WinProc; RegisterClass(&wc); // 添加WS_CLIPCHILDREN样式,绘制背景时跳过子控件区域 HWND hWnd = CreateWindow(wc.lpszClassName, TEXT("Window"), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, 0, 0, hInstance, 0); if (hWnd == NULL) return 1; ShowWindow(hWnd, SW_SHOWNORMAL); MSG msg = { 0 }; while (GetMessage(&msg, 0, 0, 0)) { DispatchMessage(&msg); } return 0; }
额外优化建议
如果界面更复杂,还可以处理WM_NCCALCSIZE消息,启用客户区的双缓冲:
case WM_NCCALCSIZE: if (wParam == TRUE) { NCCALCSIZE_PARAMS* params = (NCCALCSIZE_PARAMS*)lParam; params->rgrc[0].left += 0; params->rgrc[0].top += 0; params->rgrc[0].right -= 0; params->rgrc[0].bottom -= 0; return WVR_REDRAW; } break;
内容的提问来源于stack exchange,提问作者chacebai
相关产品推荐
相关产品推荐

