MFC 繪圖核心在於裝置內容(Device Context, DC)操作與滑鼠訊息處理。本文從基本直線繪製切入,逐步展示連續繪圖、筆刷設定、繪圖模式(ROP2)等進階技巧,並比較四種 DC 取得方式的差異。
WM_LBUTTONDOWNvoid CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
m_ptOrigin = point; // 成員變數儲存起始點
CView::OnLButtonDown(nFlags, point);
}
WM_LBUTTONUP - 四種 DC 取得方式比較| 方法 | 程式碼 | 特性 |
|---|---|---|
| 1. SDK 風格 |
| 需手動配對 Get/ReleaseDC,適用於非 MFC 專案 |
2. CDC::GetDC() |
| MFC 封裝,記得呼叫 ReleaseDC |
3. CClientDC (推薦) |
| RAII 自動管理,僅能存取客戶區,最安全 |
4. CWindowDC |
| 可繪製整個視窗區域,含標題列/邊框 |
特別示範:CWindowDC dc(GetDesktopWindow()) 可直接在桌面繪圖(慎用)。
WM_MOUSEMOVE 處理// 成員變數
BOOL m_bDraw = FALSE;
CPoint m_ptOrigin;
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
m_bDraw = TRUE;
m_ptOrigin = point;
SetCapture(); // 捕捉滑鼠,確保移動/釋放訊息送達本視窗
}
void CDrawView::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_bDraw)
{
CClientDC dc(this);
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
m_ptOrigin = point; // 更新起點為當前點
}
CView::OnMouseMove(nFlags, point);
}
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
if (m_bDraw)
{
ReleaseCapture();
m_bDraw = FALSE;
}
CView::OnLButtonUp(nFlags, point);
}
void CDrawView::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_bDraw)
{
CClientDC dc(this);
CPen pen(PS_SOLID, 1, RGB(255, 128, 128)); // 粉紅色實線
CPen* pOldPen = dc.SelectObject(&pen); // 選入新筆刷,保存舊筆刷
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
m_ptOrigin = point;
dc.SelectObject(pOldPen); // 恢復原筆刷,避免資源洩漏
}
CView::OnMouseMove(nFlags, point);
}
關鍵:SelectObject 回傳舊物件指標,繪圖結束必須恢復,否則 GDI 物件洩漏。
固定原點 m_ptOrigin,以滑鼠當前位置與上一位置 m_ptOld 形成三角形:
void CDrawView::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_bDraw)
{
CClientDC dc(this);
CPen pen(PS_SOLID, 1, RGB(255, 128, 128));
CPen* pOldPen = dc.SelectObject(&pen);
dc.MoveTo(m_ptOrigin);
dc.LineTo(m_ptOld); // 上一條邊
dc.MoveTo(m_ptOrigin);
dc.LineTo(point); // 新邊
dc.MoveTo(m_ptOld);
dc.LineTo(point); // 底邊,形成封閉三角形
m_ptOld = point;
dc.SelectObject(pOldPen);
}
CView::OnMouseMove(nFlags, point);
}
SetROP2 - XOR 反相繪圖設定 R2_NOTXORPEN 或 R2_XORPEN 可實現「橡皮筋」效果(重疊區域反相,再畫一次即還原):
dc.SetROP2(R2_NOTXORPEN); // 或 R2_XORPEN
// 後續 MoveTo/LineTo 會產生反相效果
// 同樣路徑畫兩次即可還原背景,適合拖曳預覽
常用 ROP2 模式:
R2_COPYPEN:預設,直接覆蓋R2_NOT:反相背景色R2_XORPEN:筆色與背景 XORR2_NOTXORPEN:XOR 後再反相| DC 類別 | 適用場景 | 自動清理 |
|---|---|---|
CClientDC | 視窗客戶區一般繪圖 | 是 (RAII) |
CWindowDC | 需繪製非客戶區(標題、邊框) | 是 |
CPaintDC | OnPaint/WM_PAINT 專用 | 是,含 Begin/EndPaint |
CMetaFileDC | 繪圖記錄至 metafile | 是 |
GetDC()/ReleaseDC() | 非 RAII 場景、跨函數共用 DC | 手動 |
MFC 繪圖基礎在於:正確處理滑鼠捕捉、選擇適當 DC 類別、善用 RAII 管理 GDI 資源、理解 ROP2 模式實現橡皮筋效果。掌握這些核心概念,即可建立流暢的互動式繪圖應用。