MFC 繪圖流程:OnPaint → 擦除背景 (WM_ERASEBKGND) → OnDraw 繪圖。背景色與繪圖內容反差大,短時間內交替出現造成閃爍。繪圖速度快慢非根本原因——簡單畫線也會閃,複雜圖形反而因繪圖時間長、閃爍頻率低而感覺不嚴重。動畫不閃是因連續幀差異小,非繪圖快。
核心概念:記憶體中完整繪製好一幀 → 一次性 BitBlt 貼到螢幕。記憶體繪圖過程不可見,貼圖過程極快且無反差,徹底消除閃爍。
void CMyView::OnDraw(CDC* pDC)
{
// 1. 取得繪圖區域大小
CRect rect;
GetClientRect(&rect);
int nWidth = rect.Width();
int nHeight = rect.Height();
// 2. 建立記憶體 DC 與相容位圖
CDC memDC;
CBitmap memBitmap;
memDC.CreateCompatibleDC(pDC);
memBitmap.CreateCompatibleBitmap(pDC, nWidth, nHeight);
CBitmap* pOldBitmap = memDC.SelectObject(&memBitmap);
// 3. 記憶體 DC 清背景
memDC.FillSolidRect(0, 0, nWidth, nHeight, RGB(255,255,255));
// 4. 在記憶體 DC 上繪圖 (所有繪圖操作改用 memDC)
memDC.MoveTo(0, 0);
memDC.LineTo(100, 100);
// ... 複雜繪圖邏輯 ...
// 5. 一次性貼到螢幕
pDC->BitBlt(0, 0, nWidth, nHeight, &memDC, 0, 0, SRCCOPY);
// 6. 清理
memDC.SelectObject(pOldBitmap);
memBitmap.DeleteObject();
memDC.DeleteDC();
}
雙重緩衝已包含清背景步驟,必須禁止 MFC 預設 WM_ERASEBKGND:
BOOL CMyView::OnEraseBkgnd(CDC* pDC)
{
return TRUE; // 告訴系統「我已處理背景」,不再執行預設擦除
}
BOOL CStrucView::OnEraseBkgnd(CDC* pDC)
{
CRect rect;
GetClientRect(&rect);
int nWidth = rect.Width(), nHeight = rect.Height();
CDC memDC;
CBitmap memBitmap;
memDC.CreateCompatibleDC(pDC);
memBitmap.CreateCompatibleBitmap(pDC, nWidth, nHeight);
CBitmap* pOldBit = memDC.SelectObject(&memBitmap);
// 1. 背景色
memDC.FillSolidRect(0, 0, nWidth, nHeight, RGB(255,255,255));
// 2. 置中繪製背景圖片 (m_bmp 為 CBitmap 成員,建構函數 LoadBitmap)
BITMAP bm;
m_bmp.GetObject(sizeof(bm), &bm);
CDC dcMem;
dcMem.CreateCompatibleDC(pDC);
CBitmap* pOldBmp = dcMem.SelectObject(&m_bmp);
memDC.BitBlt((nWidth - bm.bmWidth)/2, (nHeight - bm.bmHeight)/2,
bm.bmWidth, bm.bmHeight, &dcMem, 0, 0, SRCCOPY);
dcMem.SelectObject(pOldBmp);
// 3. 貼到螢幕
pDC->BitBlt(0, 0, nWidth, nHeight, &memDC, 0, 0, SRCCOPY);
memBitmap.DeleteObject();
memDC.DeleteDC();
return TRUE;
}
MFC 自動設定 pDC 的裁剪區,僅重繪受損區域。複雜繪圖時可主動利用:
CRect clipRect;
pDC->GetClipBox(&clipRect); // 取得裁剪矩形
// 繪圖前判斷圖元是否在裁剪區內
if (CRect(objLeft, objTop, objRight, objBottom).IntersectRect(&clipRect, ...)) {
// 僅繪製可見部分
memDC.DrawSomething(...);
}
簡單繪圖判斷開銷可能大於繪圖本身,視複雜度決定是否啟用。
| 技巧 | 作用 |
|---|---|
| OnEraseBkgnd return TRUE | 禁止預設背景擦除,避免雙重清背景 |
| CreateCompatibleDC + CreateCompatibleBitmap | 建立螢幕相容的記憶體繪圖表面 |
| BitBlt 一次性貼圖 | 極快的記憶體複製,無閃爍 |
| GetClipBox 裁剪區優化 | 跳過不可見區域繪圖,提升複雜場景效能 |
雙重緩衝是解決 MFC 繪圖閃爍的標準答案。掌握「記憶體 DC → 完整繪製 → BitBlt → 禁止預設擦除」標準流程,並善用裁剪區優化複雜場景,即可實現專業級無閃爍繪圖體驗。