当光标移动到子窗口上时避免 WM_MOUSELEAVE

问题描述 投票:0回答:3

我正在使用

TrackMouseEvent
WM_MOUSEHOVER
WM_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;

类比

这样想:

  • 我想要当鼠标进入房子时收到通知
  • 我想要当鼠标离开房子时收到通知

老鼠并没有因为进入

Bathroom
而离开房子。我们知道(Windows 知道)House
Bathroom
hwndOwner;所以它可以保证:

  • 通过输入
    Bathroom
  • 它还没有离开
    House

或者,换句话说:

  • 鼠标没有 WM_MouseLeft
    hwndOnwer
    控制
  • 仅仅因为鼠标进入了子控件
c++ winapi
3个回答
5
投票

好吧,所以,基于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
标志就好了,但遗憾的是没有。


3
投票

您的消息处理程序会收到一条

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

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


1
投票

通过在鼠标离开事件时获取鼠标坐标来解决。解决方案并不像我想要的那么美丽。

© www.soinside.com 2019 - 2024. All rights reserved.