其他鼠标操作

前面的部分讨论了鼠标单击和鼠标移动。 下面是一些可以使用鼠标执行的其他操作。

拖动 UI 元素

如果 UI 支持拖动 UI 元素,则应在鼠标按下消息处理程序中调用另一个函数: DragDetect。 如果用户启动应解释为拖动的鼠标手势, DragDetect 函数将返回 TRUE 。 以下代码演示如何使用此函数。

    case WM_LBUTTONDOWN: 
        {
            POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
            if (DragDetect(m_hwnd, pt))
            {
                // Start dragging.
            }
        }
        return 0;

想法如下:当某个程序支持拖放时,你不希望每次单击鼠标都会被解释为拖动。 否则,用户可能会意外拖动某些内容,例如,当用户只是想单击它 (以) 选择它。 但是,如果鼠标特别敏感,则很难在单击时保持鼠标完全静止。 因此,Windows 定义了几个像素的拖动阈值。 当用户按下鼠标按钮时,除非鼠标超过此阈值,否则不会将其视为拖动。 DragDetect 函数测试是否达到此阈值。 如果函数返回 TRUE,则可以将鼠标单击解释为拖动。 否则,请勿这样做。

注意

如果 DragDetect 返回 FALSE,则当用户松开鼠标按钮时,Windows 将禁止 显示WM_LBUTTONUP 消息。 因此,请勿调用 DragDetect ,除非程序当前处于支持拖动的模式。 (例如,如果已选择可拖动的 UI 元素。) 在本模块结束时,我们将看到使用 DragDetect 函数的较长代码示例。

 

限制光标

有时,你可能希望将光标限制在工作区或工作区的一部分。 ClipCursor 函数将光标的移动限制为指定的矩形。 此矩形以屏幕坐标而不是客户端坐标提供,因此点 (0, 0) 表示屏幕的左上角。 若要将客户端坐标转换为屏幕坐标,请调用函数 ClientToScreen

以下代码将光标限制在窗口的工作区。

    // Get the window client area.
    RECT rc;
    GetClientRect(m_hwnd, &rc);

    // Convert the client area to screen coordinates.
    POINT pt = { rc.left, rc.top };
    POINT pt2 = { rc.right, rc.bottom };
    ClientToScreen(m_hwnd, &pt);
    ClientToScreen(m_hwnd, &pt2);
    SetRect(&rc, pt.x, pt.y, pt2.x, pt2.y);

    // Confine the cursor.
    ClipCursor(&rc);

ClipCursor 采用 RECT 结构,但 ClientToScreen 采用 POINT 结构。 矩形由其左上角和右下角点定义。 可以将光标限制在任何矩形区域(包括窗口外部的区域),但将光标限制在工作区是使用 函数的典型方法。 将光标限制在窗口外的某个区域是不常见的,用户可能会将其视为 bug。

若要删除限制,请调用值为 NULLClipCursor

ClipCursor(NULL);

鼠标跟踪事件:悬停和离开

默认情况下,其他两条鼠标消息处于禁用状态,但对于某些应用程序可能很有用:

若要启用这些消息,请调用 TrackMouseEvent 函数。

    TRACKMOUSEEVENT tme;
    tme.cbSize = sizeof(tme);
    tme.hwndTrack = hwnd;
    tme.dwFlags = TME_HOVER | TME_LEAVE;
    tme.dwHoverTime = HOVER_DEFAULT;
    TrackMouseEvent(&tme);

TRACKMOUSEEVENT 结构包含函数的参数。 结构的 dwFlags 成员包含位标志,这些标志指定感兴趣的跟踪消息。 可以选择获取 WM_MOUSEHOVERWM_MOUSELEAVE,如此处所示,也可以只获取两者之一。 dwHoverTime 成员指定鼠标在系统生成悬停消息之前需要悬停的时间。 此值以毫秒为单位。 常 量HOVER_DEFAULT 表示使用系统默认值。

收到请求的消息之一后, TrackMouseEvent 函数将重置。 必须再次调用它才能获取另一条跟踪消息。 但是,在再次调用 TrackMouseEvent 之前,应等到下一个鼠标移动消息。 否则,你的窗口可能会充斥着跟踪消息。 例如,如果鼠标悬停,则当鼠标静止时,系统将继续生成 WM_MOUSEHOVER 消息流。 在鼠标移动到另一个位置并再次悬停之前,你实际上并不需要另一个 WM_MOUSEHOVER 消息。

下面是一个可用于管理鼠标跟踪事件的小型帮助程序类。

class MouseTrackEvents
{
    bool m_bMouseTracking;

public:
    MouseTrackEvents() : m_bMouseTracking(false)
    {
    }
    
    void OnMouseMove(HWND hwnd)
    {
        if (!m_bMouseTracking)
        {
            // Enable mouse tracking.
            TRACKMOUSEEVENT tme;
            tme.cbSize = sizeof(tme);
            tme.hwndTrack = hwnd;
            tme.dwFlags = TME_HOVER | TME_LEAVE;
            tme.dwHoverTime = HOVER_DEFAULT;
            TrackMouseEvent(&tme);
            m_bMouseTracking = true;
        }
    }
    void Reset(HWND hwnd)
    {
        m_bMouseTracking = false;
    }
};

下一个示例演示如何在窗口过程中使用此类。

LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_MOUSEMOVE:
        mouseTrack.OnMouseMove(m_hwnd);  // Start tracking.

        // TODO: Handle the mouse-move message.

        return 0;

    case WM_MOUSELEAVE:

        // TODO: Handle the mouse-leave message.

        mouseTrack.Reset(m_hwnd);
        return 0;

    case WM_MOUSEHOVER:

        // TODO: Handle the mouse-hover message.

        mouseTrack.Reset(m_hwnd);
        return 0;

    }
    return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}

鼠标跟踪事件需要系统进行额外的处理,因此,如果不需要它们,请将其禁用。

为了完整性,下面是一个函数,用于查询系统的默认悬停超时。

UINT GetMouseHoverTime()
{
    UINT msec; 
    if (SystemParametersInfo(SPI_GETMOUSEHOVERTIME, 0, &msec, 0))
    {   
        return msec;
    }
    else
    {
        return 0;
    }
}

鼠标滚轮

以下函数检查是否存在鼠标滚轮。

BOOL IsMouseWheelPresent()
{
    return (GetSystemMetrics(SM_MOUSEWHEELPRESENT) != 0);
}

如果用户旋转鼠标滚轮,则具有焦点的窗口将收到 WM_MOUSEWHEEL 消息。 此消息的 wParam 参数包含一个名为 delta 的整数值,该值测量滚轮旋转的距离。 增量使用任意单位,其中 120 个单位定义为执行一个“操作”所需的旋转。当然,操作的定义取决于程序。 例如,如果使用鼠标滚轮滚动文本,则每 120 个旋转单位将滚动一行文本。

增量的符号指示旋转方向:

  • 正:向前旋转,远离用户。
  • 负:向后旋转,朝用户旋转。

delta 的值连同一些其他标志一起放置在 wParam 中。 使用 GET_WHEEL_DELTA_WPARAM 宏获取增量的值。

int delta = GET_WHEEL_DELTA_WPARAM(wParam);

如果鼠标滚轮具有高分辨率,增量的绝对值可能小于 120。 在这种情况下,如果操作以较小的增量执行有意义,则可以执行此操作。 例如,文本可以按小于一行的增量滚动。 否则,累积总增量,直到滚轮旋转到足以执行操作为止。 将未使用的增量存储在变量中,当 120 个单位累积 (正数或负) 时,执行操作。

下一步

键盘输入