在 MFC 視圖類(CView)中,當滑鼠移動時,在滑鼠附近即時顯示當前座標。傳統做法是在 OnDraw 中繪製座標字串,但會導致視圖頻繁重繪、閃爍,且可能覆蓋原有繪圖內容。本文介紹一種「備份-繪製-還原」技巧,實現無閃爍、不觸發 WM_PAINT、不破壞視圖原有內容的座標跟隨顯示。
CBitmap)中保存。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; // 是否為第一次移動
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();
}
}
}
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);
}
}
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();
}
}
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 專案。掌握「備份-繪製-還原」模式,可輕鬆實現各類即時標註、十字光標、測量工具等互動功能。