为什么当鼠标没有移动时仍会触发MouseMove事件?

4

我有一个 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;
    }
}

如果你在调试/运行时将鼠标移到 ItemsControl 上并停留一段时间,你会在输出窗口中看到控件中的项移动时触发了 MouseMove 事件。
这是为什么呢?是否有办法过滤这些事件,只响应“真正”的鼠标移动事件?

1
我猜从鼠标的角度来看,它移动了。 - LarsTech
但是事件不是从鼠标的角度,而是从控件的角度吗? - Sean Beanland
你能同时输出鼠标的位置吗?也许有一些非常微小的鼠标移动是肉眼无法看到的,但计算机可以捕捉到吗? - Fraser G
1个回答

7

在你的例子中,这些事件是从你的项呈现器的子控件(即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;
    }
}

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