在 OnPaint 中直接對螢幕 DC 繪圖,若繪圖指令多或區域大,會產生明顯閃爍。雙緩衝技術先在記憶體相容 DC 繪製完畢,再一次 BitBlt 複製到螢幕,可徹底消除閃爍。
void CMyView::OnDraw(CDC* pDC)
{
// 1. 建立記憶體相容 DC
CDC memDC;
memDC.CreateCompatibleDC(pDC);
// 2. 建立相容點陣圖(依客戶區大小)
CRect rectClient;
GetClientRect(&rectClient);
CBitmap bmp;
bmp.CreateCompatibleBitmap(pDC, rectClient.Width(), rectClient.Height());
// 3. 選入點陣圖並保存舊物件
CBitmap* pOldBmp = memDC.SelectObject(&bmp);
// 4. 在記憶體 DC 進行所有繪圖
memDC.FillSolidRect(rectClient, RGB(255,255,255)); // 清背景
for (int i = 0; i < 100; ++i) {
memDC.MoveTo(i*10, 0);
memDC.LineTo(i*10, rectClient.Height());
}
// 繪製其他複雜圖形...
// memDC.Rectangle(...);
// DrawMenuButton(memDC);
// 5. 一次性複製到螢幕 DC
pDC->BitBlt(0, 0, rectClient.Width(), rectClient.Height(),
&memDC, 0, 0, SRCCOPY);
// 6. 還原舊點陣圖並清理
memDC.SelectObject(pOldBmp);
// memDC、bmp 會在離開作用域自動釋放
}
CreateCompatibleDC 建立的 DC 初始為 1x1 單色點陣圖,**必須**先 SelectObject 一個適當大小的相容點陣圖才能正常繪圖。SelectObject 回傳的舊物件指標**必須**在結束前 SelectObject 回去,否則會導致 GDI 物件洩漏,甚至讓 DC 無法刪除。OnSize 中重建或動態調整。CBitmap 與 CDC 做為成員變數,僅在尺寸改變時重建,提升效能。class CDoubleBufferDC : public CDC {
public:
CDoubleBufferDC(CDC* pDC, const CRect& rect)
: m_pDC(pDC), m_rect(rect), m_pOldBmp(NULL)
{
CreateCompatibleDC(pDC);
m_bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
m_pOldBmp = SelectObject(&m_bmp);
SetBkMode(TRANSPARENT);
}
~CDoubleBufferDC() {
if (m_pDC->m_hDC) {
m_pDC->BitBlt(m_rect.left, m_rect.top,
m_rect.Width(), m_rect.Height(),
this, 0, 0, SRCCOPY);
}
SelectObject(m_pOldBmp);
}
private:
CDC* m_pDC;
CRect m_rect;
CBitmap m_bmp;
CBitmap* m_pOldBmp;
};
// 使用方式
void CMyView::OnDraw(CDC* pDC) {
CRect rc; GetClientRect(&rc);
CDoubleBufferDC dbDC(pDC, rc);
dbDC.FillSolidRect(rc, RGB(255,255,255));
// 所有繪圖操作改用 dbDC
for (int i=0; i<100; ++i) dbDC.LineTo(i*10, rc.Height());
}
WM_PAINT 處理超過 16ms 導致閃爍的情況。