为什么在鼠标抬起事件之后会触发MouseMove事件?

16

WindowsForms 中,我只需按照以下方式添加事件处理程序:

    private void Form1_MouseDown(object sender, MouseEventArgs e)
    {
        Debug.WriteLine($"=> Form1_MouseDown, Clicks: {e.Clicks}, Location: {e.Location}");
    }

    private void Form1_MouseUp(object sender, MouseEventArgs e)
    {
        Debug.WriteLine($"=> Form1_MouseUp, Clicks: {e.Clicks}, Location: {e.Location}");
    }

    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        Debug.WriteLine($"=> Form1_MouseMove, Clicks: {e.Clicks}, Location: {e.Location}");
    }

输出结果为:

=> Form1_MouseMove, Clicks: 0, Location: {X=17,Y=21}
=> Form1_MouseDown, Clicks: 1, Location: {X=17,Y=21}
=> Form1_MouseUp,   Clicks: 1, Location: {X=17,Y=21}
=> Form1_MouseMove, Clicks: 0, Location: {X=17,Y=21}

您可以看到所有事件都发生在同一位置,那么我的问题是为什么MouseUp事件之后还会有一个MouseMove事件?

我还尝试在WPF中使用类似的代码,但MouseMove事件没有发生。

我也尝试在C++中使用类似的代码,但MouseMove事件没有发生:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
     ...

    case WM_MOUSEMOVE:
        OutputDebugString(L"WM_MOUSEMOVE\n");
        break;

    case WM_LBUTTONDOWN:
        OutputDebugString(L"WM_LBUTTONDOWN\n");
        break;

    case WM_LBUTTONUP:
        OutputDebugString(L"WM_LBUTTONUP\n");
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

鼠标在移动吗?如果是,它将生成移动事件。 - tadman
错误的假设在Windows回调函数中消息到达的顺序与您的物理操作相匹配。它不是一个FIFO队列。 - Jeff D.
2
你必须始终假定操作系统可能会合成MouseMove通知,即使鼠标没有移动。这将触发一系列其他消息,例如影响光标形状等。在这种情况下,它是因为鼠标捕获状态已更改。将this.Capture = false;添加到MouseDown事件处理程序中以查看差异。 - Hans Passant
2
你在回复@Jeff的评论时发布的链接只是支持他所写的内容。至于你自己的问题,“为什么?”这类问题通常主要基于观点,这也不例外。你需要问微软为什么在WM_MOUSEUP后会生成WM_MOUSEMOVE。话虽如此,你的代码应该始终准备好处理鼠标移动,并且Windows或.NET可能会在这种情况下生成一个鼠标移动事件,以解决有缺陷的程序(大多数程序将在鼠标移动时检查光标状态)的问题,例如在鼠标抬起时重置其光标状态。 - Peter Duniho
请注意,这不是.NET独有的问题。例如,这里有一个Chromium的错误报告,抱怨同样的事情:https://bugs.chromium.org/p/chromium/issues/detail?id=161464。我不会感到惊讶,如果在Windows中有一些兼容性代码被触发用于Winforms程序以及基于Chromium的代码,以帮助这些程序更好地工作。再次强调,您的代码应该始终能够处理鼠标移动事件,因此获取额外的信息不应该成为问题。 - Peter Duniho
显示剩余2条评论
3个回答

10
如果你的鼠标之前已经聚焦在另一个窗口上,那么点击新窗口并移动鼠标的焦点将生成鼠标移动事件(即使鼠标在你单击之前或之后没有立即移动)。这里有一个类似的StackOverflow回答链接“Ghost”MouseMove Event

7
这是因为鼠标被 MouseDown 捕获后会在 MouseUp 时释放。而这个额外的 MouseMove 可能是为了确保光标位置。作为一种解决方法,你可以这样做。
        Point LastLocation = Point.Empty;

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            Debug.WriteLine("=> Form1_MouseDown, Clicks: " + e.Location + ", Location: " + e.Location + "");
        }

        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            Debug.WriteLine("=> Form1_MouseUp, Clicks: " + e.Location + ", Location: " + e.Location + "");

        }

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (LastLocation != e.Location)
            {
                LastLocation = e.Location;
                Debug.WriteLine("=> Form1_MouseMove, Clicks: " + e.Location + ", Location: " + e.Location + "");
            }
        }

2
这是预期行为,并且在应用程序切换时(例如:Alt+Tab)也会触发。您应该采用@VishnuBabu的解决方法,并忽略初始mousemove触发,可以在窗口加载后获取当前光标位置,而不是将LastLocation设置为空。"最初的回答"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接