为什么鼠标不移动时会触发 MouseMove 事件

Why does MouseMove event fire when mouse is not moving

我有一个 ItemsControl,其 ItemsPresenter 响应 MouseMove 事件。项目在数据源内移动,如果鼠标在项目移动时位于控件上,这会导致 MouseMove 事件触发,即使鼠标没有移动也是如此。

下面是一个演示问题的例子。

XAML:

<ItemsControl Name="ladder" ItemsSource="{Binding Rows}">
    <ItemsControl.Template>
        <ControlTemplate>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <TextBlock Text="Header" Grid.Column="0" />
                <ItemsPresenter Grid.Row="1" 
                                MouseMove="OnMouseMove"/>
            </Grid>                 
        </ControlTemplate>
    </ItemsControl.Template>
</ItemsControl>

C#:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        Rows.Add(new Row { Name = "0" });
        Rows.Add(new Row { Name = "1" });
        Rows.Add(new Row { Name = "2" });
        Rows.Add(new Row { Name = "3" });
        Rows.Add(new Row { Name = "4" });

        DispatcherTimer t = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(1000) };
        t.Tick += T_Tick;
        t.Start();
    }

    private void T_Tick(object sender, EventArgs e)
    {
        Rows.Move(4, 0);
    }

    private void OnMouseMove(object sender, MouseEventArgs e)
    {
        Debug.WriteLine(e.Timestamp);
    }

    public ObservableCollection<Row> Rows { get; set; } = new ObservableCollection<Row>();
}

public class Row
{
    public string Name { get; set; }

    public override string ToString()
    {
        return Name;
    }
}

如果你 debug/run 这个,将鼠标移到 ItemsControl 上,然后把它留在那里,你会在输出 window 中看到 MouseMove 事件随着控件中的项目四处移动而开火。

有什么原因吗?或者有没有办法过滤掉这些事件,只响应 "real" 鼠标移动事件?

在您的示例中,这些事件是从您的项目展示器的子控件中冒出的,即来自 TextBlocks。如果你这样做:

private void OnMouseMove(object sender, MouseEventArgs e)
{
    var tb=(TextBlock)e.OriginalSource;
    var lastMove = e.GetPosition((IInputElement)e.OriginalSource);
    Debug.WriteLine(tb.Text + ":" + lastMove);
}

你会看到每次事件的原始来源都是不同的文本块(0 1 2 3 4 5),并且是一个现在鼠标下的文本块。从这个文本块的角度来看,鼠标确实被移动了 - 它没有超过它然后变成了结束。我同意这是有争议的行为,甚至可以被认为是错误。要解决此问题,我认为最简单的方法是记住上次鼠标移动位置并检查它是否已更改:

private Point _lastMove;
private void OnMouseMove(object sender, MouseEventArgs e)
{                        
    var p = e.GetPosition((IInputElement)sender);
    if (_lastMove != p) {
        // really moved
        _lastMove = p;
    }
}