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

时间:2014-02-04 11:18:04

标签: wpf

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

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

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

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

以上是用于证明问题的上述代码的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>

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

  1. 点击ComboBox打开其弹出窗口。
  2. 点击蓝色网格(这将关闭弹出窗口)。
  3. 注意: “输出”窗口显示消息“鼠标移动”

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

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

    修改

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

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

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

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

    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);
        }
    }
    

3 个答案:

答案 0 :(得分:1)

你想要实现的事情与event routing有关。因此,为了让您的代码按预期工作,您将不得不稍微修改它:

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

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

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

答案 1 :(得分:0)

你应该为几个控件设置一个事件处理程序OnMouseMove,因为这个bubble事件从控件到达,然后他转到root元素。在我们的示例中,ComboBox上触发了 MouseMove 事件并转到Grid。此外,在事件处理程序中检查所需元素的类型并进行必要的操作。

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

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;
}

答案 2 :(得分:0)

随着我对Dragging的更多了解,我将自己回答这个问题。拖动是一个操作,应该在移动鼠标并且鼠标左键停止时启动,但它不应该立即启动,以防止意外启动拖动SystemParameters.MinimumHorizontalDragDistanceSystemParameters.MinimumVerticalDragDistance静态成员应该是使用

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);
           }
    }
}

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