为什么按下鼠标按钮时 MouseMove 不会触发?

Why would MouseMove not fire when a mouse button is pressed?

有一些关于 WPF 鼠标处理的内容我显然不明白,所以我将不胜感激任何能得到的帮助。很抱歉让这个冗长 - 我被告知要提供足够的上下文信息,以便其他人可以弄清楚我正在努力完成什么。

我有一系列按钮,每个按钮都包含一张图片的预览。当放大我也显示的完整图像时,我想使用相应的预览按钮作为我实际放大完整图像的哪一部分的指示器。为此,我使用 Adorner 在预览图像的顶部绘制一个简单的矩形框,以反映缩放区域。请查看屏幕截图进行说明 - 左下角的按钮以橙色矩形显示缩放区域。

Screenshot

所有这些都运行良好,如果我通过单击完整图像并移动它来四处移动缩放区域,橙色框会相应地移动。

现在我的问题是:我还希望能够移动主缩放区域,方法是将鼠标放在预览按钮上的橙色框内,然后四处移动该框。我尝试实现这一点的方式是,我为 PreviewMouseDownPreviewMouseUpMouseMove 事件提供预览按钮事件处理程序,如下所示:

Button prevBut = new Button(); 
prevBut.PreviewMouseDown += PrevBut_PreviewMouseDown; 
prevBut.PreviewMouseUp += PrevBut_PreviewMouseUp; 
prevBut.MouseMove += PrevBut_MouseMove;

PreviewMouseDown 检查鼠标位置是否确实在橙色框内。如果是,它会记录开始位置并捕获按钮的鼠标。 PreviewMouseUp再次释放鼠标捕获。 MouseMove 现在应该被调用并做一些事情来根据鼠标位置分别更新缩放从开始位置的变化。

private void PrevBut_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    MouseStartPosRelativeToPreviewButton = e.GetPosition((Button)sender);

    if ( [some calculation to verify that mouse position is within orange box] )
        ((Button)sender).CaptureMouse();
}


private void PrevBut_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
    ((Button)sender).ReleaseMouseCapture();
}


private void PrevBut_MouseMove(object sender, MouseEventArgs e)
{
    if (((Button)sender).IsMouseCaptured)
    {
        Point PosRelativeToPreviewImage = e.MouseDevice.GetPosition((Button)sender);

        ..  [Do some calculations and update zoom plus the position of the orange box]
    }
}

问题是,当鼠标悬停在按钮上时 PrevBut_MouseMove() 会定期调用(我已验证),但当按下鼠标按钮时调用会停止。换句话说,如果我记录发生的事情,我会在将鼠标移动到按钮上方时看到大量 PrevBut_MouseMove() 调用,然后当我单击按钮上的任意位置时,我会得到 PrevBut_PreviewMouseDown(),紧接着是 PrevBut_PreviewMouseUp()(一旦我松开鼠标)。即使我按住鼠标按钮几秒钟,也不会记录下任何一个动作。一旦我松开鼠标按钮,就会有更多 PrevBut_MouseMove() 次调用,直到鼠标离开按钮。正如我所说,我不明白这一点。为什么 MouseMove 在鼠标按钮处于活动状态时不再触发?

顺便说一句,Adorner(橙色框)设置为 IsHitTestVisible = false,据我了解,它不应该妨碍鼠标事件。 (或者可以吗?)我已经花了很多时间试图找到一个解决方法,但无法让它工作。感谢您能给我的任何帮助。谢谢。

这是正常行为。 MouseMove 事件仍然由输入管理器引发。但是由于事件及其注册的处理程序是 同步执行的 (WPF 不实现异步事件), MouseMove 处理程序只能在所有当前正在执行的处理程序之后执行,例如 MouseDown 已经回来了。这就是 MouseDown 中断 MouseMove 的原因(在所有已注册事件处理程序的持续时间内)。

如果您主要对 MouseMove 感兴趣并且也想对鼠标按钮做出反应(当鼠标移动时),您应该在 MouseMove 的处理程序中处理这些按钮。
您可以通过访问 MouseEventArgs:

MouseMove 事件序列期间轮询按钮的状态来执行此操作
// Track pressed mouse buttons to determine that the MouseButtonState.Released state
// immediately follows the MouseButtonState.Pressed button state
private bool IsMouseButtonPreviouslyPressed  { get; sete; }

private void OnMouseMove(object sender, MouseEventArgs e)
{
  Point currentMousePointerPosition = e.GetPosition(sender as IInputElement);
  var eventSource = sender as UIElement;

  if (e.LeftButton == MouseButtonState.Pressed)
  {
    IsMouseButtonPreviouslyPressed  = true;
    HandleLeftMouseButtonDown(currentMousePointerPosition, eventSource);
  }
  if (IsMouseButtonPreviouslyPressed  && e.LeftButton == MouseButtonState.Released)
  {
    IsMouseButtonPreviouslyPressed  = false;
    HandleLeftMouseButtonUp(currentMousePointerPosition, eventSource);
  }
  HandleMouseMove(currentMousePointerPosition, eventSource);
}

private void HandleLeftMouseButtonDown(Point mousePointerPosition, UIElement sourceElement)
{
    if ( [some calculation to verify that mouse position is within orange box] )
    {
      sourceElement.CaptureMouse();
    }
}

private void HandleLeftMouseButtonUp(Point mousePointerPosition, UIElement sourceElement)
{
    if ( [some calculation to verify that mouse position is within orange box] )
    {
      sourceElement.ReleaseMouseCapture();
    }
}

private void HandleMouseMove(Point mousePointerPosition, UIElement sourceElement)
{
    if (sourceElement.IsMouseCaptured)
    {
      ..  [Do some calculations and update zoom plus the position of the orange box]
    }
}