响应式扩展和鼠标事件参数来源

3

我在我的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
两者源不同的原因是什么?

这种行为是一致的吗?还是你只是偶然看到了这个?是不是你碰巧悬停在按钮边框上了?毕竟,你正在订阅Grid上的MouseMove事件,因此占据屏幕空间的任何Grid中的元素都有可能成为事件源。 - Timothy Shields
这是一种一致的行为。如果使用OriginalSource而不是source,它也不会改变。 - Postlagerkarte
".Sample(TimeSpan.FromSeconds(1))" 是什么意思? - eran otzap
@eranotzap http://www.introtorx.com/Content/v1.0.10621.0/13_TimeShiftedSequences.html#Sample - Lucas Trzesniewski
将您的示例代码更改为在采样之前调用 DoFromEventPattern(...).Do(mouseMoveRxSampleDo).Sample(...).Subscribe(...); 看看是否我们还会遇到同样的问题。这听起来像是 EventArgs 是可变的,并且在 Sample 缓存它并报告给观察者之前被某些东西修改了。同时请记住,Sample 会将您从 Dispatcher 线程跳转到线程池线程,这可能会引起某些混乱(虽然不应该导致此特定问题)。 - Brandon
显示剩余2条评论
1个回答

0

我对WPF没有经验,所以请带着怀疑的态度看待这个答案...

但是,根据你使用Do进行的实验,似乎发生的情况是,在事件触发并且Sample缓存了该值之后,事件对象被WPF修改了。

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
}

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