为什么鼠标不移动时会触发 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;
}
}
我有一个 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;
}
}