鼠标移动事件会触发两次预览

3
我是一名有用的助手,可以为您翻译文本。
我有一个简单代码的问题。我寻找了几个小时的解决方案,但没有效果。我有一个画布和矩形。如果光标在外面,我会移动矩形,委托pMouseMove只会触发每个像素一次。相反,如果光标在矩形上,委托将每个像素触发两次。我想让它只运行一次,就像在矩形外面一样,怎么做?
XAML:
<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
  <Canvas x:Name="Can" Height="257" Width="503" Background="Gray">
    <TextBox Name="tb" Width="77" Height="20" Canvas.Left="0" Canvas.Top="-21"/>
  </Canvas>
</Window>

代码后台:

public partial class MainWindow : Window
{
    Rectangle rect = new Rectangle();
    private static int i;
    private static string s;

    public MainWindow()
    {
        InitializeComponent();

        rect.Height = 50;
        rect.Width = 50;
        rect.Fill = Brushes.Black;
        Can.Children.Add(rect);
        Can.PreviewMouseMove += pMouseMove;
    }

    private void pMouseMove(object sender, MouseEventArgs e)
    {
        //cursor over Rectangle
        Canvas.SetTop(rect, e.GetPosition(Can).Y + 10);
        Canvas.SetLeft(rect, e.GetPosition(Can).X + 10);

        //cursor outside Rectangle
        //Canvas.SetTop(rect, e.GetPosition(Can).Y - 10);
        //Canvas.SetLeft(rect, e.GetPosition(Can).X - 10);

        //Counter
        i++;
        tb.Text = i.ToString();

        //e.Handled = true;
    }
}

非常抱歉我的英语不太好


你应该意识到,鼠标移动事件有时可能不会每个像素都发送一次,有时甚至更少。任何依赖于鼠标移动事件数量的逻辑都是错误的,尽管关注性能是合理的。 - Ben Voigt
1个回答

3

WPF中的事件是路由事件, 这意味着您的Canvas将从画布本身和画布内的所有元素接收事件。正如您所注意到的,CanvasPreviewMouseMove事件会同时接收来自CanvasRectangle的事件。

[更新] 我运行了您的代码并添加了一行代码来检查e.OriginalSource的值,以确定最初引发事件的是什么。像这样:

private void pMouseMove(object sender, MouseEventArgs e)
{
    // print out e.OriginalSource just for learning purposes
    Console.WriteLine("OriginalSource:" + e.OriginalSource.ToString());
}

我的原始答案是检查e.OriginalSource的类型,因为我认为您收到了相同的事件两次。但是现在我明白了您的意思:如果e.OriginalSource是Rectangle,则与e.OriginalSource是Canvas时相比,PreviewMouseMove事件会频繁地触发两次。这是由于Rectangle实现内部的某些东西导致的(唯一找出的方法是使用像Reflector这样的工具查看内部逻辑)。但是,有一种解决方法可以使事件的频率保持一致。
您可以设置rect.IsHitTestVisible = false; 这将消除Rectangle发送事件并成为e.OriginalSource的情况,这意味着所有PreviewMouseMove事件都将来自Canvas。然后,您可以使用VisualTreeHelper.HitTest来检查鼠标位置是否在Rectangle内。
我刚刚运行了下面的代码,我认为这是一种确保一致触发事件但仍具有命中测试功能的方法。
在构造函数中:
rect.Fill = Brushes.Black;
rect.IsHitTestVisible = false;
Can.Children.Add(rect);

PreviewMouseMove 处理程序中:
private void pMouseMove(object sender, MouseEventArgs e)
{
    // Debug.WriteLine(e.OriginalSource.ToString());

    HitTestResult result = VisualTreeHelper.HitTest(rect, e.GetPosition(sender as UIElement));

    if (result != null) {
        Debug.WriteLine("Mouse inside rect")
    }
    else {
        Debug.WriteLine("Mouse outside rect");
    }
}

1
我检查了 e.OriginalSource 的值。如果光标位于矩形上,则 pMouseMove 会触发两次,且在这两种情况下,e.OriginalSource 的值均为 System.Windows.Shapes.Rectangle。因此,条件 if (e.OriginalSource is Canvas) 总是不成立。 - Cinio

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