首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

MFC 繪圖程式入門:滑鼠事件處理與 DC 使用技巧

     編輯:關於VC++

前言

MFC 繪圖核心在於裝置內容(Device Context, DC)操作與滑鼠訊息處理。本文從基本直線繪製切入,逐步展示連續繪圖、筆刷設定、繪圖模式(ROP2)等進階技巧,並比較四種 DC 取得方式的差異。

一、基本直線繪製:滑鼠按下/釋放事件

1.1 記錄起點 WM_LBUTTONDOWN

void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
    m_ptOrigin = point; // 成員變數儲存起始點
    CView::OnLButtonDown(nFlags, point);
}

1.2 終點繪製 WM_LBUTTONUP - 四種 DC 取得方式比較

方法程式碼特性
1. SDK 風格
HDC hdc = ::GetDC(m_hWnd);
MoveToEx(hdc, m_ptOrigin.x, m_ptOrigin.y, NULL);
LineTo(hdc, point.x, point.y);
::ReleaseDC(m_hWnd, hdc);
需手動配對 Get/ReleaseDC,適用於非 MFC 專案
2. CDC::GetDC()
CDC* pDC = GetDC();
pDC->MoveTo(m_ptOrigin);
pDC->LineTo(point);
ReleaseDC(pDC);
MFC 封裝,記得呼叫 ReleaseDC
3. CClientDC (推薦)
CClientDC dc(this); // 建構時 GetDC,解構時 ReleaseDC
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
RAII 自動管理,僅能存取客戶區,最安全
4. CWindowDC
CWindowDC dc(this); // 含非客戶區(標題列、邊框)
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
可繪製整個視窗區域,含標題列/邊框

特別示範:CWindowDC dc(GetDesktopWindow()) 可直接在桌面繪圖(慎用)。

二、連續線條繪製:WM_MOUSEMOVE 處理

2.1 基本邏輯

// 成員變數
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);
}

2.2 變更筆刷顏色/樣式

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 物件洩漏。

2.3 扇形/多邊形繪製技巧

固定原點 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);
}

2.4 繪圖模式 SetROP2 - XOR 反相繪圖

設定 R2_NOTXORPEN 或 R2_XORPEN 可實現「橡皮筋」效果(重疊區域反相,再畫一次即還原):

dc.SetROP2(R2_NOTXORPEN); // 或 R2_XORPEN
// 後續 MoveTo/LineTo 會產生反相效果
// 同樣路徑畫兩次即可還原背景,適合拖曳預覽

常用 ROP2 模式:

  • R2_COPYPEN:預設,直接覆蓋
  • R2_NOT:反相背景色
  • R2_XORPEN:筆色與背景 XOR
  • R2_NOTXORPEN:XOR 後再反相

三、DC 選擇指南

DC 類別適用場景自動清理
CClientDC視窗客戶區一般繪圖是 (RAII)
CWindowDC需繪製非客戶區(標題、邊框)是
CPaintDCOnPaint/WM_PAINT 專用是,含 Begin/EndPaint
CMetaFileDC繪圖記錄至 metafile是
GetDC()/ReleaseDC()非 RAII 場景、跨函數共用 DC手動

結語

MFC 繪圖基礎在於:正確處理滑鼠捕捉、選擇適當 DC 類別、善用 RAII 管理 GDI 資源、理解 ROP2 模式實現橡皮筋效果。掌握這些核心概念,即可建立流暢的互動式繪圖應用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved