wpf 如何在鼠标实际移动到元素上时获取 MouseMove 事件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21550982/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 10:43:00  来源:igfitidea点击:

How to get the MouseMove event when the mouse actually moves over an element

wpf

提问by Novitchi S

Basically what i want to do is to change a custom window's state from Maximized to Normal state and adjust the positions of the window, when user clicks and moves the mouse over TitleBar which is a simple border in my case.

基本上我想要做的是将自定义窗口的状态从最大化状态更改为正常状态并调整窗口的位置,当用户单击并将鼠标移到 TitleBar 上时,在我的情况下这是一个简单的边框。

The obvious thing to do is to attach an event-handler to MouseMoveand check if the mouse left button is pressed:

显而易见的事情是附加一个事件处理程序MouseMove并检查鼠标左键是否被按下:

    private void OnMouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed)
        {
            Console.WriteLine("Mouse moved" + Mouse.Captured);
        }
    }

The problem is that the MouseMoveoccurs when the mouse capture changes. So in my application the window will snap to a Normalstate after you open a popup and click the border, the snapping should happen after you start dragging.

问题是MouseMove当鼠标捕获更改时发生。因此,在我的应用程序中,Normal在您打开弹出窗口并单击边框后,窗口将捕捉到一个状态,捕捉应该在您开始拖动后发生。

Here is an XAML for the above code to prove the problem:

下面是上述代码的 XAML 来证明问题:

 <Grid MouseMove="OnMouseMove" Background="AliceBlue">
    <ComboBox Height="23">
        <ComboBoxItem>Test1</ComboBoxItem>
        <ComboBoxItem>Test1</ComboBoxItem>
        <ComboBoxItem>Test1</ComboBoxItem>
        <ComboBoxItem>Test1</ComboBoxItem>
        <ComboBoxItem>Test1</ComboBoxItem>
        <ComboBoxItem>Test1</ComboBoxItem>
    </ComboBox>
</Grid>

Issue Steps, run the above code in debug:

问题步骤,在调试中运行上面的代码:

  1. Click the ComboBoxto open its popup.
  2. Click on blue Grid (this will close the popup).
  1. 单击ComboBox打开其弹出窗口。
  2. 单击蓝色网格(这将关闭弹出窗口)。

Notice:The Output window shows the Message "Mouse moved"

注意:输出窗口显示消息“鼠标移动”

Expected behavior:Since only a click was made (there was no mouse move) i don't want the MouseMoveevent.

预期行为:由于只进行了一次点击(没有鼠标移动),我不想要该MouseMove事件。

I understand that the MouseMoveis fired when the Mouse.Capturedelement changes and this is normal, but i need a way to distinguish between a normal MouseMoveand a MouseMovecaused by a Mouse Capture.

我知道MouseMoveMouse.Captured元素发生变化时会触发,这是正常的,但我需要一种方法来区分正常MouseMoveMouseMove由鼠标捕获引起的 。

EDIT:

编辑:

A more complex example of this issue can be found in MahApps.Metro demo. The steps are the same as described above - open a popup and click on title bar when the window is in Maximized state. You will notice the window snapped from Maximized state to normal state. This shouldn't have happened, since you did not double-click, or drag the title bar.

可以在 MahApps.Metro 演示中找到此问题的更复杂示例。步骤与上述相同 - 当窗口处于最大化状态时,打开一个弹出窗口并单击标题栏。您会注意到窗口从最大化状态捕捉到正常状态。这不应该发生,因为您没有双击或拖动标题栏。

My solution so far:

到目前为止我的解决方案:

Since i know this is caused by mouse captured element changes, i handled the LostMouseCaptureevent and saved the mouse position:

由于我知道这是由鼠标捕获的元素更改引起的,因此我处理了该LostMouseCapture事件并保存了鼠标位置:

    private Point mousePositionAfterCapture;

    private void OnLostMouseCapture(object sender, MouseEventArgs e)
    {
        mousePositionAfterCapture = e.GetPosition(this);
    }   

And in MouseMovehandler i check if the position has changed after last lost mouse capture:

MouseMove处理程序中,我检查上次丢失鼠标捕获后位置是否已更改:

private void OnMouseMove(object sender, MouseEventArgs e)
{
    if (e.LeftButton == MouseButtonState.Pressed)
    {
        Point currentPossition = e.GetPosition(this);

        if (currentPossition == mousePositionAfterCapture)
            //means that the MouseMove event occurred as a result of 
            //Mouse Captured element change, we wait until an actual
            //mouse move will occure
            return;         

        Console.WriteLine("Mouse moved" + Mouse.Captured);
    }
}

采纳答案by Novitchi S

As i learned more about Dragging i will answer this question myself. The Drag is an operation that should start when the mouse is moved and the left mouse button is down, but it should not start right away, to prevent accidentally started Drags the SystemParameters.MinimumHorizontalDragDistanceand SystemParameters.MinimumVerticalDragDistancestatic members should be used.

随着我对拖动的了解更多,我将自己回答这个问题。Drag是一个操作,应该在鼠标移动和鼠标左键按下时开始,但不能马上开始,防止意外开始SystemParameters.MinimumHorizontalDragDistanceSystemParameters.MinimumVerticalDragDistance应该使用Drags和static成员。

private Point startPoint;

private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    startPoint = e.GetPosition(null);
}  

private void OnPreviewMouseMove(object sender, MouseEventArgs e)
{
    if (e.LeftButton == MouseButtonState.Pressed)
    {
        Point position = e.GetPosition(this);

        if (Math.Abs(position.X - startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
            Math.Abs(position.Y - startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
           {
               StartDrag(e);
           }
    }
}

This solved the issue asked in this question since the drag will never start if the mouse did not changed its position.

这解决了这个问题中提出的问题,因为如果鼠标没有改变其位置,拖动将永远不会开始。

回答by Anatolii Gabuza

Thing that you want to achieve is related to event routing. So to get your code working as expected you'll have to modify it just a bit:

您想要实现的事情与事件路由有关。因此,为了让您的代码按预期工作,您只需稍微修改一下:

<ComboBox Height="23" MouseMove="HandleMouseMove">
...

and

private void HandleMouseMove(object sender, MouseEventArgs e)
{
     e.Handled = true;
}

Why this will work?
By adding MouseMoveevent we're creating routed event which will be responsible for handling all mouse move events bubbled to current level not yet handled by anyone else. Other words any event raised on ComboBoxwill bubble up the element tree until reaches appropriate handler which will set Handled = trueor reaches top of the tree.

为什么这会起作用?
通过添加MouseMove事件,我们正在创建路由事件,该事件将负责处理所有冒泡到当前级别尚未由其他任何人处理的鼠标移动事件。换句话说,任何引发的事件ComboBox都会使元素树向上冒泡,直到到达适当的处理程序,该处理程序将设置Handled = true或到达树的顶部。

回答by Anatoliy Nikolaev

You should set for several controls one event handler OnMouseMove, as this bubble event arrives from the control and he goes up to the root element. In our case, MouseMoveevent triggered on ComboBoxand comes to Grid. Further, in event handler to check the type of the desired element and make the necessary action.

您应该为多个控件设置一个事件处理程序OnMouseMove,因为此气泡事件从控件到达并上升到根元素。在我们的例子中,MouseMove事件触发ComboBox并到达Grid。此外,在事件处理程序中检查所需元素的类型并进行必要的操作。

XAML

XAML

<Grid MouseMove="OnMouseMove" Background="AliceBlue">
    <ComboBox Height="30" MouseMove="OnMouseMove">
        <ComboBoxItem>1</ComboBoxItem>            
        <ComboBoxItem>2</ComboBoxItem>
        <ComboBoxItem>3</ComboBoxItem>
        <ComboBoxItem>4</ComboBoxItem>
    </ComboBox>
</Grid>

Code behind

Code behind

private void OnMouseMove(object sender, MouseEventArgs e)
{
    var target = sender;

    if (target is Grid)
    {
        if (e.LeftButton == MouseButtonState.Pressed)
        {
            MessageBox.Show("Mouse moved in " + target.GetType());
        }
    }

    e.Handled = true;
}