【问题标题】:How to get the MouseMove event when the mouse actually moves over an element当鼠标实际移动到元素上时如何获取 MouseMove 事件
【发布时间】:2017-04-03 18:32:34
【问题描述】:

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

显而易见的做法是将事件处理程序附加到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>

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

  1. 单击ComboBox 打开其弹出窗口。
  2. 单击蓝色网格(这将关闭弹出窗口)。

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

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

我知道Mouse.Captured 元素更改时会触发MouseMove,这是正常的,但我需要一种方法来区分正常的MouseMove 和由鼠标捕获引起的MouseMove

编辑:

可以在 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);
    }
}

【问题讨论】:

  • 上述 XAML 和 EventHandler 应该足以重现该问题。问题是,当您通过单击蓝色网格内的某处关闭ComboBox 的弹出窗口时,您将收到我不想要的 MouseMove 事件。
  • 嗯,将 ComboBox ClickMode 更改为 Press 并告诉我们是否可以解决您的问题...
  • ComboBox 没有 ClickMode 属性。
  • 我很抱歉,这是在 ToggleButton 问题上发布的。按钮有点击模式。您是否尝试过使用 IsMouseDirectlyOver 属性?还有 IsMouseDirectlyOver 事件。
  • 我刚刚找到了您关于Focus的上一个问题的解决方案,您已经被删除了:(。找到解决方案了吗?:)

标签: wpf


【解决方案1】:

随着我对拖动的了解更多,我将自己回答这个问题。拖拽是当鼠标移动并按下鼠标左键时应该开始的操作,但它不应该立即开始,以防止意外启动拖拽应该使用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);
           }
    }
}

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

【讨论】:

    【解决方案2】:

    你想要达到的目标与event routing有关。因此,要让您的代码按预期工作,您只需稍作修改即可:

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

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

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

    【讨论】:

    • 对我来说,结果是一样的,UIElement 加到&lt;Grid MouseMove="" 的情况并没有改变。
    • @AnatoliyNikolaev 同意。没有正确理解问题。
    【解决方案3】:

    您应该为多个控件设置一个事件处理程序OnMouseMove,因为此气泡事件从控件到达并向上到达根元素。在我们的例子中,MouseMove 事件在ComboBox 上触发并到达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;
    }
    

    【讨论】:

    • 如果我使用你的例子,什么都不会改变。当通过单击网格关闭弹出窗口时,MouseMove 事件仍会在 target == Grid 时发生。
    • @Novitchi S:可能我没有完全理解这个问题,是不是需要在Grid鼠标事件中考虑,或者不?
    • 请看一下编辑后的问题,希望现在更清楚。
    • @Novitchi S:好的,我想我明白了。当您单击 Grid 触发的 MouseMove 事件时,您不希望出现这种情况,它仅由(e.LeftButton == MouseButtonState.Pressed).Right 触发?不幸的是,我无法在MahApps 演示中真正看到它是如何工作的。
    • @Novitchi S: 你能在问题编辑中提供ugly 解决方案吗,我也没有这样做:)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-18
    • 2014-06-27
    • 1970-01-01
    • 2017-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多