反应性扩展和MouseEventArgs源

时间:2014-09-08 17:45:08

标签: c# .net wpf system.reactive

我在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为源。

不同来源的原因是什么?

1 个答案:

答案 0 :(得分:0)

我没有使用WPF的经验,所以请耐心等待这个答案......

但是,根据您对Do的实验,看来发生的事情是,在事件触发并Sample缓存该值后,事件对象可能会被W​​PF突变。

MouseEventArgs documentationRemarks部分说明了这一点:

  

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

这似乎表明,事实上,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
}