在對話框 OnLButtonDown 呼叫 temp.TrackRubberBand(this, point, TRUE) 繪製橡皮筋框。但 OnLButtonUp 完全不觸發——TrackRubberBand 內部以滑鼠左鍵放開為結束條件,提前截獲了 WM_LBUTTONUP 訊息。
TrackRubberBand 返回後,主動 SendMessage(WM_LBUTTONUP, 0, 0) 讓自身訊息迴路再處理一次放開事件。
void CMyDialog::OnLButtonDown(UINT nFlags, CPoint point) {
Invalidate(TRUE);
if(m_tracker.HitTest(point) < 0) { // 新建選框
m_bLBtnDown = TRUE;
CRectTracker temp;
temp.TrackRubberBand(this, point, TRUE);
temp.m_rect.NormalizeRect();
m_ptStart = point;
GetCursorPos(&m_ptEnd); // 螢幕座標
SendMessage(WM_LBUTTONUP, 0, 0); // 關鍵:重發放開訊息
} else { // 調整既有框
m_tracker.Track(this, point, TRUE);
m_tracker.m_rect.NormalizeRect();
OnPaint();
}
CDialog::OnLButtonDown(nFlags, point);
}
void CMyDialog::OnLButtonUp(UINT nFlags, CPoint point) {
if(m_bLBtnDown) { // 僅新建模式處理
// 螢幕座標轉客戶區
CRect rect; rect.right = m_ptEnd.x; rect.bottom = m_ptEnd.y;
ScreenToClient(&rect);
m_ptEnd = rect.BottomRight();
m_tracker.m_rect.SetRect(m_ptStart.x, m_ptStart.y, m_ptEnd.x, m_ptEnd.y);
m_tracker.m_rect.NormalizeRect();
OnPaint();
m_bLBtnDown = FALSE;
}
CDialog::OnLButtonUp(nFlags, point);
}
void CMyDialog::OnLButtonDblClk(UINT nFlags, CPoint point) {
AfxMessageBox(_T("雙擊測試")); // 驗證雙擊不受影響
CDialog::OnLButtonDblClk(nFlags, point);
}
BOOL CMyDialog::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT msg) {
if(pWnd == this && m_tracker.SetCursor(this, nHitTest)) return TRUE;
return CDialog::OnSetCursor(pWnd, nHitTest, msg);
}
TrackRubberBand 內部跑自己的訊息迴圈,直接處理 WM_MOUSEMOVE/WM_LBUTTONUPWM_LBUTTONUP 已被消費,需手動補發m_bLBtnDown 區分「新建」與「調整/雙擊」模式,避免誤觸發此技巧同樣適用於 Track、DragRect 等 MFC 內建追蹤函式,掌握「訊息被內部迴圈截獲 → 手動重發」模式即可舉一反三。