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

在視圖中實現滑鼠座標即時跟隨顯示(無閃爍、不刷新)

     編輯:VC++

需求背景

在 MFC 視圖類(CView)中,當滑鼠移動時,在滑鼠附近即時顯示當前座標。傳統做法是在 OnDraw 中繪製座標字串,但會導致視圖頻繁重繪、閃爍,且可能覆蓋原有繪圖內容。本文介紹一種「備份-繪製-還原」技巧,實現無閃爍、不觸發 WM_PAINT、不破壞視圖原有內容的座標跟隨顯示。

核心思路

  1. 計算顯示區域:根據滑鼠位置與偏移量,算出座標字串將佔據的矩形區域。
  2. 備份背景位圖:將該矩形區域的當前畫面內容複製到一個記憶體位圖(CBitmap)中保存。
  3. 繪製座標文字:在視圖 DC 上直接輸出座標字串(透明背景、自定義顏色)。
  4. 滑鼠再次移動時還原:下一次 OnMouseMove 觸發前,先把上一次保存的位圖貼回原位,抹除舊座標;然後重複上述步驟顯示新座標。

關鍵常量與成員變數

#define WORD_HEIGHT 18   // 字符高度,依系統字體調整
#define WORD_WIDTH  7    // 字符寬度
#define OFF_X       15   // 座標顯示位置相對滑鼠的 X 偏移
#define OFF_Y       10   // Y 偏移(通常在右下方)
#define TEXT_COLOR  RGB(255,0,0) // 座標文字顏色(紅)

// 視圖類成員
CBitmap m_StoreBmp;      // 備份位圖
CDC     m_StoreDC;       // 備份 DC
int     m_nCoordStrLen;  // 座標字串像素寬度
CPoint  m_OldPt;         // 上一次滑鼠位置
BOOL    m_bStart;        // 是否為第一次移動

核心函數實現

1. CopyBitmap — 備份背景

void CDrawMouseView::CopyBitmap(CDC* pDC, CPoint copyPt) {
    if (m_StoreBmp.GetSafeHandle()) { // 已有位圖先清理
        m_StoreDC.DeleteDC();
        m_StoreBmp.DeleteObject();
        m_StoreBmp.m_hObject = 0;
    }
    if (m_StoreDC.CreateCompatibleDC(pDC)) {
        if (m_StoreBmp.CreateCompatibleBitmap(pDC, m_nCoordStrLen, WORD_HEIGHT)) {
            m_StoreDC.SelectObject(&m_StoreBmp);
            // 從視圖 DC 複製指定矩形到備份 DC
            m_StoreDC.BitBlt(0, 0, m_nCoordStrLen, WORD_HEIGHT,
                             pDC, copyPt.x + OFF_X, copyPt.y + OFF_Y, SRCCOPY);
        } else {
            if (m_StoreBmp.GetSafeHandle()) m_StoreBmp.DeleteObject();
            m_StoreDC.DeleteDC();
        }
    }
}

2. DrawCoord — 繪製座標文字

void CDrawMouseView::DrawCoord(CDC* pDC, CString& coordString, CPoint drawPt) {
    if (m_StoreBmp.GetSafeHandle()) { // 僅當備份成功時才繪製
        drawPt.Offset(OFF_X, OFF_Y);
        CRect rect(drawPt, CSize(m_nCoordStrLen, WORD_HEIGHT));
        pDC->SetBkMode(TRANSPARENT);
        COLORREF oldColor = pDC->SetTextColor(TEXT_COLOR);
        pDC->DrawText(coordString, rect, DT_CENTER);
        pDC->SetTextColor(oldColor);
    }
}

3. DoRubberCoord — 還原背景(抹除舊座標)

void CDrawMouseView::DoRubberCoord(CDC* pDC, CPoint showPt) {
    if (m_StoreBmp.GetSafeHandle()) {
        m_StoreDC.SelectObject(&m_StoreBmp);
        // 將備份位圖貼回視圖原位置
        pDC->BitBlt(showPt.x + OFF_X, showPt.y + OFF_Y,
                    m_nCoordStrLen, WORD_HEIGHT, &m_StoreDC, 0, 0, SRCCOPY);
        m_StoreDC.DeleteDC();
        m_StoreBmp.DeleteObject();
    }
}

4. OnMouseMove — 滑鼠移動事件處理

void CDrawMouseView::OnMouseMove(UINT nFlags, CPoint point) {
    CDC* pDC = GetDC();
    if (!m_bStart) m_bStart = TRUE;
    else DoRubberCoord(pDC, m_OldPt); // 非第一次先還原

    CString str;
    str.Format(_T("[%d,%d]"), point.x, point.y);
    m_nCoordStrLen = str.GetLength() * WORD_WIDTH;
    m_OldPt = point;

    CopyBitmap(pDC, point);  // 備份新位置背景
    DrawCoord(pDC, str, point); // 繪製新座標
    ReleaseDC(pDC);
}

優缺點分析

優點缺點
完全避免視圖重繪,無閃爍頻繁創建/銷毀 GDI 物件,高頻移動下有輕微效能開銷
不覆蓋視圖原有繪圖內容若視圖內容在座標顯示期間發生變化(如動畫),還原時會出現「殘影」
邏輯簡單,易於移植到其他視圖類僅適用於純 GDI 繪圖場景,Direct2D/OpenGL 需另行處理

擴展建議

  • 將備份位圖改為成員變數持久化,避免頻繁 Create/Delete,僅在字串長度變化時重新分配。
  • 使用 CDC::GetClipBox 限制繪製範圍,進一步減少像素操作量。
  • 若需顯示更多資訊(如邏輯座標、物理座標),只需修改 str.Format 部分。

本技巧在 VC6.0 驗證通過,同樣適用於 VS2019+ 的 MFC 專案。掌握「備份-繪製-還原」模式,可輕鬆實現各類即時標註、十字光標、測量工具等互動功能。

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