MFC 默認繪圖流程:OnPaint → OnEraseBkgnd (填充背景刷) → OnDraw (業務繪圖)。背景色與前景圖形反差大、頻繁重繪 (調整大小、遮擋恢復) 導致視覺閃爍。單純設置背景刷為 NULL 雖消除閃爍,但會造成舊圖形殘留疊加混亂。
在內存創建兼容 DC 與位圖,所有繪圖操作導向內存 DC,最後一次性 BitBlt 到屏幕 DC。內存繪圖過程不可見,屏幕僅呈現最終幀,無閃爍。
void CMyView::OnDraw(CDC* pDC) {
CRect rect;
GetClientRect(&rect);
int nWidth = rect.Width(), nHeight = rect.Height();
CDC memDC;
CBitmap memBmp;
memDC.CreateCompatibleDC(pDC);
memBmp.CreateCompatibleBitmap(pDC, nWidth, nHeight);
CBitmap* pOldBmp = memDC.SelectObject(&memBmp);
// 1. 填充背景 (內存中無閃爍)
memDC.FillSolidRect(0, 0, nWidth, nHeight, RGB(255,255,255));
// 2. 業務繪圖 (全部針對 memDC)
memDC.MoveTo(0,0); memDC.LineTo(100,100);
// ... 復雜繪圖邏輯 ...
// 3. 一次性傳輸到屏幕
pDC->BitBlt(0, 0, nWidth, nHeight, &memDC, 0, 0, SRCCOPY);
// 4. 清理 (恢復舊位圖,刪除對象)
memDC.SelectObject(pOldBmp);
memBmp.DeleteObject();
memDC.DeleteDC();
}
pDC->GetClipBox(&rectClip) 獲取無效區矩形。繪圖前判斷圖形是否與 rectClip 相交,不相交則跳過,大幅減少復雜場景繪圖開銷。
BOOL CMyView::OnEraseBkgnd(CDC* pDC) {
CRect rect; GetClientRect(&rect);
int w = rect.Width(), h = rect.Height();
CDC memDC; CBitmap memBmp;
memDC.CreateCompatibleDC(pDC);
memBmp.CreateCompatibleBitmap(pDC, w, h);
CBitmap* pOld = memDC.SelectObject(&memBmp);
// 背景色
memDC.FillSolidRect(0, 0, w, h, RGB(255,255,255));
// 居中繪制背景圖片 m_bmp (CBitmap 成員,構造函數 LoadBitmap)
BITMAP bm; m_bmp.GetObject(sizeof(bm), &bm);
CDC imgDC; imgDC.CreateCompatibleDC(pDC);
CBitmap* pOldImg = imgDC.SelectObject(&m_bmp);
memDC.BitBlt((w - bm.bmWidth)/2, (h - bm.bmHeight)/2,
bm.bmWidth, bm.bmHeight, &imgDC, 0, 0, SRCCOPY);
imgDC.SelectObject(pOldImg);
// 輸出到屏幕
pDC->BitBlt(0, 0, w, h, &memDC, 0, 0, SRCCOPY);
memBmp.DeleteObject(); memDC.DeleteDC();
return TRUE; // 關鍵:返回 TRUE 告訴系統背景已處理,阻止默認 OnEraseBkgnd
}
SelectObject 返回舊對象指針,繪制結束前必須 SelectObject(pOld) 恢復,然後 DeleteObject 刪除新建對象,防止 GDI 泄漏。OnSize 變化時重建,避免每幀分銷毀開銷。