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

Windows 控制項滑鼠進入/離開追蹤:CM_MOUSEENTER 與 CM_MOUSELEAVE 訊息實作

     編輯:關於C語言

想要按鈕滑鼠移入變色、移出還原?Windows 無原生「滑鼠進入/離開」訊息。Borland VCL 定義了 `CM_MOUSEENTER`/`CM_MOUSELEAVE` 使用者訊息,MFC 可透過 `_TrackMouseEvent` (Win98/NT4+) 或子類別化實現相同效果。本文解析訊息來源、MFC 實作兩種方案,並提供可復用的 `CHoverButton` 範例。

訊息來源澄清

  • `CM_` 前綴 = Custom Message (Borland VCL 定義),非 Microsoft 官方訊息
  • Windows 核心提供 `_TrackMouseEvent` API (需 `COMCTL32.DLL` 4.71+),發送 `WM_MOUSELEAVE` 訊息
  • MFC 7.0+ 封裝為 `CWnd::TrackMouseLeave()` / `OnMouseLeave()`

方案一:_TrackMouseEvent (現代標準)


class CHoverButton : public CButton {
    BOOL m_bTracking = FALSE;
    COLORREF m_clrNormal, m_clrHover;

protected:
    afx_msg void OnMouseMove(UINT nFlags, CPoint point);
    afx_msg LRESULT OnMouseLeave(WPARAM, LPARAM);
    DECLARE_MESSAGE_MAP()
};

BEGIN_MESSAGE_MAP(CHoverButton, CButton)
    ON_WM_MOUSEMOVE()
    ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
END_MESSAGE_MAP()

void CHoverButton::OnMouseMove(UINT nFlags, CPoint point) {
    if (!m_bTracking) {
        TRACKMOUSEEVENT tme = { sizeof(tme), TME_LEAVE, m_hWnd, 0 };
        _TrackMouseEvent(&tme);  // 請求接收 WM_MOUSELEAVE
        m_bTracking = TRUE;
        Invalidate(); // 觸發重繪進入狀態
    }
    CButton::OnMouseMove(nFlags, point);
}

LRESULT CHoverButton::OnMouseLeave(WPARAM, LPARAM) {
    m_bTracking = FALSE;
    Invalidate(); // 觸發重繪離開狀態
    return 0;
}

void CHoverButton::DrawItem(LPDRAWITEMSTRUCT lpDIS) {
    CDC* pDC = CDC::FromHandle(lpDIS->hDC);
    CRect rc = lpDIS->rcItem;
    pDC->FillSolidRect(rc, m_bTracking ? m_clrHover : m_clrNormal);
    // 繪製文字...
}

方案二:子類別化 + SetCapture (相容舊版)


// 在 OnLButtonDown 或 OnMouseMove 首次進入時
SetCapture();  // 捕捉滑鼠,即使移出客戶區仍收到 WM_MOUSEMOVE
// 在 OnMouseMove 中檢測座標是否在客戶區內
CRect rc; GetClientRect(&rc);
if (!rc.PtInRect(point)) {
    ReleaseCapture();
    // 觸發離開邏輯
}

缺點:捕捉期間其他視窗無法收到滑鼠訊息,不適合複雜 UI。

方案三:Timer 輪詢 (最簡單但低效)


SetTimer(1, 50, NULL); // 50ms 檢查一次
void OnTimer(UINT nID) {
    CPoint pt; GetCursorPos(&pt); ScreenToClient(&pt);
    CRect rc; GetClientRect(&rc);
    bool inside = rc.PtInRect(pt);
    if (inside != m_bInside) { m_bInside = inside; Invalidate(); }
}

最佳實踐建議

  1. 優先使用 `_TrackMouseEvent` / `WM_MOUSELEAVE` (Win98+ 全支援)
  2. MFC 7.0+ 直接覆寫 `OnMouseLeave()` 虛函數,無需 `ON_MESSAGE`
  3. 自訂控制項將進入/離開邏輯封裝為虛函數 `OnMouseEnter`/`OnMouseLeave`,方便衍生類擴充
  4. 避免在 `OnMouseMove` 大量計算/重繪,僅在狀態切換時 `Invalidate()`
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved