反应式扩展和MouseEventArgs源

本文关键字:MouseEventArgs 扩展 反应式 | 更新日期: 2023-09-27 18:21:03

我在WPF应用程序中使用RX来跟踪鼠标移动。

当直接订阅鼠标移动事件时,与使用RX的示例方法相比,我在MouseEventArgs中获得了不同的源。

为了便于解释,这里有一个简单的例子:

我有一个包含网格和按钮的窗口:

<Window x:Class="WpfApplication4.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication4"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="grid">
        <Button></Button>
    </Grid>
</Window>

我订阅了带有RX:的鼠标移动事件

void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    Observable.FromEventPattern<MouseEventArgs>(grid, "MouseMove").Subscribe(mouseMoveRx);
    Observable.FromEventPattern<MouseEventArgs>(grid, "MouseMove").Sample(TimeSpan.FromSeconds(1)).Subscribe(mouseMoveRxSample);
}
private void mouseMoveRx(System.Reactive.EventPattern<MouseEventArgs> obj)
{
    var element = obj.EventArgs.Source as UIElement; 
    //element is System.Windows.Controls.Button
}
private void mouseMoveRxSample(System.Reactive.EventPattern<MouseEventArgs> obj)
{
    var element = obj.EventArgs.Source as UIElement;
    //element is Microsoft.Windows.Themes.ButtonChrome
}

第一个处理程序具有System.Windows.Controls.Button作为源,而第二个处理程序则具有Microsoft.Windows.Themes.ButtonChrome作为源。

来源不同的原因是什么?

反应式扩展和MouseEventArgs源

我没有WPF的经验,所以对这个答案持保留态度。。。

但是,根据您对Do的实验,似乎发生的情况是,在事件触发并且Sample缓存值之后,事件对象可能会被WPF突变。

MouseEventArgs文档的Remarks部分这样写道:

附加的事件和基本元素路由的事件共享它们的事件数据,以及路由的冒泡和隧道版本事件还共享事件数据。这会影响处理事件在事件路线上行进时的特性。对于详细信息,请参阅输入概述。

这似乎表明,WPF在元素层次结构中冒泡事件对象时,确实对其进行了变异。对象上的任何可设置属性都受此行为约束。在这种情况下,Source似乎是您唯一需要担心的事情。

要处理此问题,在应用任何引入异步性的Rx运算符之前,实际上需要缓存Source值,因为只要WPF事件处理程序正在运行,就只能信任Source属性。最简单的方法是使用Select子句捕获Source:

Observable.FromEventPattern<MouseEventArgs>(grid, "MouseMove")
    .Select(e => Tuple.Create(e.EventArgs.Source as UIElement, e.EventArgs))
    .Sample(TimeSpan.FromSeconds(1))
    .Subscribe(mouseMoveRxSample);
// ...
private void mouseMoveRxSample(Tuple<UIElement, MouseEventArgs> obj)
{
    var element = obj.Item1;
    //element is System.Windows.Controls.Button
}