Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does MouseMove event fire when mouse is not moving

Tags:

c#

wpf

mousemove

I have an ItemsControl whose ItemsPresenter responds to the MouseMove event. Items are moved within the data source, and if the mouse is over the control when items are moved, this causes the MouseMove event to fire even though the mouse isn't moving.

Below is an example demonstrating the problem.

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>

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

If you debug/run this, move the mouse over the ItemsControl, and leave it there, you'll see in the Output window that the MouseMove event is firing as items in the control shift around.

Any reason for this? Or is there a way to filter these events out and only respond to "real" mouse move events?

like image 964
Sean Beanland Avatar asked Sep 09 '25 11:09

Sean Beanland


1 Answers

In your example those events are bubbling from child controls of your item presenter, that is from TextBlocks. If you do this:

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

You will see that each time original source of event is different text block (0 1 2 3 4 5), and is a textblock which is now under mouse. And from perspective of this textblock, mouse was moved indeed - it was not over it and then became over. I agree that it's arguable behavior, and maybe can be considered bug even. To work around this, easiest I think is remember last mouse move position and check if it has changed:

private Point _lastMove;
private void OnMouseMove(object sender, MouseEventArgs e)
{                        
    var p = e.GetPosition((IInputElement)sender);
    if (_lastMove != p) {
        // really moved
        _lastMove = p;
    }
}
like image 92
Evk Avatar answered Sep 11 '25 01:09

Evk