【问题标题】:Avoid WM_MOUSELEAVE when the cursor moves over a child window当光标在子窗口上移动时避免 WM_MOUSELEAVE
【发布时间】:2018-06-26 19:41:02
【问题描述】:

我正在使用 TrackMouseEventWM_MOUSEHOVERWM_MOUSELEAVE 处理鼠标悬停/离开事件。

唯一的问题是,当鼠标悬停在窗口的任何子窗口上时,它会向跟踪鼠标的窗口发送WM_MOUSELEAVE 消息。

我实际上理解为什么 Windows 会这样做,但不知道如何解决它。谷歌搜索对我没有帮助。我相信解决方案非常简单,我只是错过了一些东西。我正在开发 Visual C++ Win32 应用程序。 (无MFC等)

我的代码:

void TrackMouse(HWND hwnd)
{
    TRACKMOUSEEVENT tme;
    tme.cbSize = sizeof(TRACKMOUSEEVENT);
    tme.dwFlags = TME_HOVER | TME_LEAVE;
    tme.dwHoverTime = 1; //How long the mouse has to be in the window to trigger a hover event.
    tme.hwndTrack = hwnd;
    TrackMouseEvent(&tme);
}

WndProc:

case WM_MOUSEMOVE:
{
    if (!isTracking)
    {
        TrackMouse(hWnd);
        isTracking = true;
    }
    break;
}

case WM_MOUSEHOVER:
    ShowWindow(MouseIsOver, TRUE);
    break;

case WM_MOUSELEAVE:
    ShowWindow(MouseIsOver, FALSE);
    isTracking = false;
    break;

【问题讨论】:

标签: c++ winapi


【解决方案1】:

您的消息处理程序收到WM_MOUSELEAVE 消息,告诉它跟踪已完成。您必须再次致电TrackMouseEvent() 才能继续跟踪。没有什么可修复的。您的消息处理程序可以采取相应的行动。

如果没有该消息,您的程序将不了解情况。

【讨论】:

    【解决方案2】:

    好的,所以,基于 OP(可能)实际上想要忽略 WM_MOUSELEAVE 当光标经过跟踪鼠标的窗口的子窗口时,我认为他现在可能正在做类似的事情这个:

    BOOL DidMouseLeaveWindow (HWND hWnd)
    {
        DWORD msgpos = GetMessagePos ();
        POINT pt = { GET_X_LPARAM (msgpos), GET_Y_LPARAM (msgpos) };
        ScreenToClient (hWnd, &pt);
        RECT cr;
        GetClientRect (hWnd, &cr);
        return !PtInRect (&cr, pt);
    }
    

    这对我来说似乎很好。

    如果您出于某种原因不喜欢这样,您也可以这样做:

    BOOL DidMouseLeaveWindow (HWND hWnd)
    {
        DWORD msgpos = GetMessagePos ();
        POINT pt = { GET_X_LPARAM (msgpos), GET_Y_LPARAM (msgpos) };
        HWND hWndUnderCursor = WindowFromPoint (pt);
        return !IsChild (hWnd, hWndUnderCursor);
    }
    

    这也许更优雅一些。

    不过,这两种方法都存在一些问题。如果(例如)子窗口的右侧边缘与父窗口的右侧边缘完全对齐并且您通过该边缘退出,那么父窗口将永远不会得到它的WM_MOUSELEAVE。如果您将光标放在子窗口上然后很快将其移出父窗口,您也可能会错过一个,

    要解决这些问题,我建议设置一个计时器作为支持。所以,你会做这样的事情:

    void TrackMouse(HWND hwnd)
    {
        // ...
        SetTimer (hWnd, 1, 250, 0);
    }
    

    在 WndProc 中:

    case WM_TIMER:
    case WM_MOUSELEAVE:
        if (DidMouseLeaveWindow (hWnd))
        {
            // ...
            isTracking = false;
            KillTimer (hWnd, 1);
        }
        break;
    

    如果TrackMouseEvent 有一个TME_IGNORE_CHILDREN 标志会很好,但遗憾的是它没有。

    【讨论】:

    • 我希望像我不知道的秘密 TME_IGNORE_CHILDREN 标志这样的解决方案。我在上面建议的检查坐标的解决方案。无论如何,感谢您提供有关此解决方案的一些有用说明。
    • @Grigory,是的,也许 MS 错过了一个技巧。稍微修改了我的答案(更改了一个字)。
    【解决方案3】:

    通过在鼠标离开事件中获取鼠标坐标来解决。解决方案并不如我所愿。

    【讨论】:

    • 我认为 OP 指的是当鼠标经过子窗口时父级获得WM_MOUSELEAVE 的事实,而他不希望这样。 OP,请确认,我将编辑您的帖子。
    • 正是如此。我认为前三句话就很清楚了。
    • 是的,差不多,我只是想确定一下。为了清楚起见,我将进行编辑,希望您不要介意(在我这样做之前,我会给您几分钟的时间来回复)。
    • 问题和答案的清晰性是该网站的主要目的。我当然不介意。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-19
    相关资源
    最近更新 更多