Windows 窗口重繪機制:**`WM_PAINT` 觸發時,系統默認擦除背景 (`WM_ERASEBKGND`),然後調用 `OnPaint`**。若在 `OnMouseMove`/`OnLButtonDown` 中直接 `GetDC` 畫線,**不保存座標**,下一次 `WM_PAINT` 到來時線條被擦除。
// 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 | 作用 | 關鍵參數 |
|---|---|---|
| `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 繪圖的**標準模式**,解決線條消失、閃爍、殘影等所有顯示問題。