MFC 開發中,InvalidateRect(&rc, FALSE) 觸發全客戶區重繪,若在 OnPaint 直接對 CPaintDC 繪製複雜圖形,必然產生嚴重閃爍。網路上常見的「雙緩衝」程式碼讓初學者望而卻步。本文提供三種由易到難、各有適用場景的解決方案,附完整可複製貼上的核心程式碼。
原理:在記憶體位圖上繪圖完成後,一次性 BitBlt 到螢幕 DC,避免使用者看到中間過程。
void CMyView::OnPaint() {
CPaintDC dc(this); // 螢幕 DC
CRect rect;
GetClientRect(&rect);
// 1. 建立相容記憶體 DC 與位圖
CDC memDC;
memDC.CreateCompatibleDC(&dc);
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);
// 2. 擦除背景(關鍵:填滿背景色,否則透明殘影)
memDC.FillSolidRect(rect, ::GetSysColor(COLOR_WINDOW));
// 3. 在 memDC 上進行所有繪圖操作
DrawAllGraphics(&memDC); // 你的繪圖邏輯
// 4. 一次性複製到螢幕
dc.BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);
// 5. 清理
memDC.SelectObject(pOldBitmap);
}
關鍵細節:FillSolidRect 必須在繪圖前呼叫,否則未繪製區域會顯示上一幀殘影(因位圖預設未初始化)。
WM_ERASEBKGND 抑制(簡單場景有效)若圖形更新區域固定(如只有某個矩形變動),可只刷新該區域並禁止系統擦除背景。
// 1. 僅刷新變動區域
CRect updateRect(100, 100, 200, 200); // 計算出的髒矩形
InvalidateRect(&updateRect, FALSE); // FALSE = 不擦除背景
// 2. 在視圖類覆寫 OnEraseBkgnd 返回 TRUE(告訴系統「我自己處理背景」)
BOOL CMyView::OnEraseBkgnd(CDC* pDC) {
return TRUE; // 禁止預設擦除,減少閃爍來源
}
// 3. OnPaint 中只繪製 updateRect 內容
void CMyView::OnPaint() {
CPaintDC dc(this);
CRect clipBox;
dc.GetClipBox(&clipBox); // 取得實際需重繪區域
DrawGraphicsInRect(&dc, clipBox);
}
適用場景:簡單動畫、滑鼠拖曳回饋、局部資料更新。**不適用**於全畫面複雜向量圖形重繪。
MFC 從 VS2010 (MFC 10) 起內建 CD2DRenderTarget 支援,利用 GPU 渲染徹底消除閃爍,且支援抗鋸齒、幾何變換。
// 1. 標頭檔宣告
#include
CD2DRenderTarget* m_pD2DRenderTarget;
// 2. OnInitialUpdate 或 OnCreate 建立
HRESULT CMyView::CreateD2DResources() {
if (m_pD2DRenderTarget) return S_OK;
CRect rect; GetClientRect(&rect);
m_pD2DRenderTarget = new CD2DRenderTarget();
HRESULT hr = m_pD2DRenderTarget->Create(this, rect.Size());
if (SUCCEEDED(hr)) {
// 建立筆刷、幾何體等資源
m_pD2DRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Blue), &m_pBrush);
}
return hr;
}
// 3. OnRender 繪圖(MFC 自動呼叫,雙緩衝內建)
void CMyView::OnRender(CD2DRenderTarget* pRenderTarget, const CRect& rect) {
pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
pRenderTarget->DrawRectangle(D2D1::RectF(10,10,100,100), m_pBrush, 2.0f);
// 繪製文字、圖片、幾何路徑...
}
// 4. 視窗大小改變時重建
void CMyView::OnSize(UINT nType, int cx, int cy) {
CView::OnSize(nType, cx, cy);
if (m_pD2DRenderTarget) {
m_pD2DRenderTarget->Resize(CD2DSizeF(cx, cy));
}
}
優勢:零閃爍、高效能、現代圖形 API、**自動雙緩衝**。
要求:目標機需安裝 Platform Update for Windows 7 或更新版本(Win8+ 內建)。
InvalidateRect(..., TRUE) + OnEraseBkgnd 返回 TRUE → 背景被擦兩次,閃爍加劇。CreateD2DResources 中建立,反覆在 OnRender new → 嚴重洩漏與效能下降。根據專案約束選擇合適方案,MFC 繪圖閃爍問題皆可徹底解決,提升應用專業觀感。