ExcludeClipRect 可從裁剪區域中排除一個矩形,導致該區域不被繪製。常用於「在背景上繪製前景圖片,避免重疊閃爍」。
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
RECT rc; GetClientRect(hWnd, &rc);
HDC hMemDC = CreateCompatibleDC(ps.hdc);
SelectObject(hMemDC, g_hBmp);
// ExcludeClipRect(ps.hdc, 0, 0, 200, 100); // 排除左上角 200x100
BitBlt(ps.hdc, 0, 0, 300, 200, hMemDC, 0, 0, SRCCOPY);
DeleteDC(hMemDC);
EndPaint(hWnd, &ps);
break;
啟用註解行後,左上角 200x100 區域將保持背景色,不被位圖覆蓋。
ExcludeClipRect 一旦調用,該排除區域在該 DC 上永久生效,即使視窗大小改變也不會恢復。必須手動「找回」該區域:
// 記錄上次排除區域
RECT g_rcExclud = {0};
BOOL g_bCancelExcludeRect = FALSE;
case WM_PAINT:
// ... 建立 DC、選擇位圖 ...
// 1. 先恢復上次被排除的區域
HRGN hrgn = NULL;
if (g_bCancelExcludeRect && RECT_WIDTH(g_rcExclud) && RECT_HEIGHT(g_rcExclud)) {
hrgn = CreateRectRgn(g_rcExclud.left, g_rcExclud.top,
g_rcExclud.right, g_rcExclud.bottom);
ExtSelectClipRgn(ps.hdc, hrgn, RGN_OR); // 合併區域
FillRect(ps.hdc, &g_rcExclud, (HBRUSH)GetStockObject(COLOR_WINDOW));
}
// 2. 計算新位圖位置(右下角)
g_rcExclud.left = rc.right - 300;
g_rcExclud.top = rc.bottom - 200;
g_rcExclud.right = rc.right;
g_rcExclud.bottom = rc.bottom;
// 3. 繪製位圖
BitBlt(ps.hdc, g_rcExclud.left, g_rcExclud.top,
RECT_WIDTH(g_rcExclud), RECT_HEIGHT(g_rcExclud),
hMemDC, 0, 0, SRCCOPY);
// 4. 排除剛繪製的圖片區域
ExcludeClipRect(ps.hdc, g_rcExclud.left, g_rcExclud.top,
RECT_WIDTH(g_rcExclud), RECT_HEIGHT(g_rcExclud));
// 5. 用背景色填滿其餘區域
FillRect(ps.hdc, &rc, (HBRUSH)GetStockObject(COLOR_WINDOW));
if (hrgn) DeleteObject(hrgn);
DeleteDC(hMemDC);
EndPaint(hWnd, &ps);
break;
關鍵原則:不擦除背景(WM_ERASEBKGND 返回 TRUE),在 WM_PAINT 中先畫前景圖片,排除該區域,再用背景色填滿其餘區域。配合 InvalidateRect(hWnd, NULL, FALSE)(最後參數 FALSE 表示不擦除背景)。
case WM_SIZE:
InvalidateRect(hWnd, NULL, FALSE); // 關鍵:不擦除背景
break;
本文整理自「零一小築」博客,原文出處:https://jetyi.blog.51cto.com/1460128/642401 | 參考:相關討論