首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Windows GDI繪圖:如何在客戶區保留多條線段不被擦除?WM_PAINT與InvalidateRect正確用法

     編輯:程式設計綜合問答

問題根源

Windows 窗口重繪機制:**`WM_PAINT` 觸發時,系統默認擦除背景 (`WM_ERASEBKGND`),然後調用 `OnPaint`**。若在 `OnMouseMove`/`OnLButtonDown` 中直接 `GetDC` 畫線,**不保存座標**,下一次 `WM_PAINT` 到來時線條被擦除。

正確架構:數據驅動繪圖

  1. 模型層:容器保存所有線段端點 `std::vector lines;`。
  2. 交互層:鼠標消息中**只修改模型** (添加點、更新橡皮筋終點),**不直接畫圖**。
  3. 視圖層:`OnPaint` 中遍歷模型,**統一繪製所有線段**。

關鍵代碼實現


// 1. 定義線段結構
struct Line { CPoint start, end; };
std::vector m_lines;        // 所有確認的線段
CPoint m_dragStart, m_dragEnd;    // 正在拖動的橡皮筋線
bool m_isDrawing = false;

// 2. 鼠標按下:記錄起點
void CMyView::OnLButtonDown(UINT nFlags, CPoint point) {
    m_dragStart = m_dragEnd = point;
    m_isDrawing = true;
    SetCapture();
}

// 3. 鼠標移動:更新橡皮筋終點,觸發重繪
void CMyView::OnMouseMove(UINT nFlags, CPoint point) {
    if (m_isDrawing) {
        m_dragEnd = point;
        InvalidateRect(NULL, FALSE); // 關鍵:FALSE = 不擦除背景,僅重繪客戶區
    }
}

// 4. 鼠標釋放:確認線段,存入模型
void CMyView::OnLButtonUp(UINT nFlags, CPoint point) {
    if (m_isDrawing) {
        m_lines.push_back({m_dragStart, point}); // 保存
        m_isDrawing = false;
        ReleaseCapture();
        InvalidateRect(NULL, FALSE);
    }
}

// 5. 統一繪製 (核心)
void CMyView::OnPaint() {
    CPaintDC dc(this); // 自動 BeginPaint/EndPaint
    CPen pen(PS_SOLID, 1, RGB(0,0,0));
    CPen* pOldPen = dc.SelectObject(&pen);

    // 繪製所有已確認線段
    for (const auto& line : m_lines) {
        dc.MoveTo(line.start);
        dc.LineTo(line.end);
    }
    // 繪製正在拖動的橡皮筋線 (可選 XOR 模式避免殘影)
    if (m_isDrawing) {
        dc.MoveTo(m_dragStart);
        dc.LineTo(m_dragEnd);
    }
    dc.SelectObject(pOldPen);
}

關鍵 API 解析

API作用關鍵參數
`InvalidateRect(NULL, FALSE)`標記整個客戶區需重繪,**FALSE = 不發送 WM_ERASEBKGND**,保留背景,減少閃爍`NULL` = 整個客戶區;`FALSE` = 不擦除背景
`CPaintDC`封裝 `BeginPaint`/`EndPaint`,自動獲取更新區域剪裁,防止過度繪製構造時自動調用 `BeginPaint`,析構調用 `EndPaint`
`SetCapture`/`ReleaseCapture`鼠標捕獲,確保鼠標移出窗口仍收到 `OnMouseMove`拖動操作必須配對使用

進階:雙緩衝消除閃爍


void CMyView::OnPaint() {
    CPaintDC dc(this);
    CRect rect; GetClientRect(&rect);
    CMemDC memDC(&dc, &rect); // MFC 提供的記憶體 DC 類 (或手動 CreateCompatibleDC)
    // 在 memDC 上繪製所有線段...
    // 最後 BitBlt 到屏幕 dc
    dc.BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);
}

總結

**不在鼠標消息中直接畫圖**。保存幾何數據 → `InvalidateRect(NULL, FALSE)` → `OnPaint` 統一繪製。這是 Windows GDI/MFC 繪圖的**標準模式**,解決線條消失、閃爍、殘影等所有顯示問題。

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