在 MFC 的 `OnDraw` 中使用雙緩沖繪製圍棋盤(19×19 線條),代碼邏輯看似正確但窗口一片空白。這通常是由於 **GDI+ 未初始化** 或 **BitBlt 坐標計算錯誤** 導致。本文逐步拆解問題並給出修正代碼。
關鍵片段:
CDC dcMemory;
dcMemory.CreateCompatibleDC(pDC);
CBitmap GoBoardMap;
CRect rect;
GetClientRect(&rect);
GoBoardMap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
// ... 計算 gostartx, goblenth 等 ...
Graphics graphics(dcMemory); // 直接用 HDC 構造 Graphics
Pen* myPen = new Pen(Color::Black, 1);
// 繪製線條迴圈 ...
pDC->BitBlt(gostartx, gostarty, goblenth, goblenth, &dcMemory, 0, 0, SRCCOPY);
dcMemory.DeleteDC();
MFC 應用若使用 GDI+ (`Graphics`, `Pen` 等類),**必須**在應用程序啟動時調用 `GdiplusStartup`,退出時調用 `GdiplusShutdown`。否則 `Graphics` 構造失敗,繪圖無效。
在 `CWinApp` 派生類中添加:
// 頭文件
#include
#pragma comment(lib, "gdiplus.lib")
ULONG_PTR m_gdiplusToken;
// CPP 文件 - InitInstance
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
// CPP 文件 - ExitInstance
Gdiplus::GdiplusShutdown(m_gdiplusToken);
CWinApp::ExitInstance();
代碼中 `BitBlt(gostartx, gostarty, goblenth, goblenth, ...)` 將內存 DC 的 `(0,0)` 區域複製到屏幕 DC 的 `(gostartx, gostarty)`。但繪圖時坐標已包含 `gostartx/gostarty` 偏移,導致內容繪在內存位圖的右下角區域,而複製時卻取左上角,結果為空。
void CMyView::OnDraw(CDC* pDC) {
CRect rect;
GetClientRect(&rect);
// 1. 創建兼容 DC 與位圖(覆蓋整個客戶區)
CDC dcMem;
dcMem.CreateCompatibleDC(pDC);
CBitmap bmp;
bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
CBitmap* pOldBmp = dcMem.SelectObject(&bmp);
// 2. 填充背景(可選)
dcMem.FillSolidRect(&rect, RGB(255, 255, 255));
// 3. GDI+ 繪圖:Graphics 綁定內存 DC,坐標從 (0,0) 開始
Graphics graphics(dcMem.GetSafeHdc());
Pen pen(Color::Black, 1);
float boardLen = min(rect.Width(), rect.Height()) * 0.9f;
float cellSize = boardLen / 18.0f;
float startX = (rect.Width() - boardLen) / 2.0f;
float startY = (rect.Height() - boardLen) / 2.0f;
for (int i = 0; i <= 18; ++i) {
// 橫線:坐標相對內存 DC 原點
graphics.DrawLine(&pen, startX, startY + i * cellSize, startX + boardLen, startY + i * cellSize);
// 豎線
graphics.DrawLine(&pen, startX + i * cellSize, startY, startX + i * cellSize, startY + boardLen);
}
// 4. 一次性 BitBlt 整個客戶區
pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &dcMem, 0, 0, SRCCOPY);
// 5. 清理
dcMem.SelectObject(pOldBmp);
// dcMem.DeleteDC() 會由析構自動調用
}
應用以上修正,您的圍棋盤將完美顯示。