【问题标题】:Filtering a Touch.FrameReported IObservable using arbitrary boolean condition that changes over time使用随时间变化的任意布尔条件过滤 Touch.FrameReported IObservable
【发布时间】:2011-06-17 02:13:19
【问题描述】:

我一直在使用 Windows Phone 7 中的响应式扩展 (RX),并且非常接近于一个可行的解决方案,但我发现了一个小细节。我正在尝试使用Touch.FrameReported 和Observable.FromEvent 处理原始触摸事件(更好地学习Touch API 和RX 的教育任务),但我只想在某些条件下处理这些事件。例如,我可能只想在枢轴控件中选择特定页面时过滤对 touch down 和 touch up 事件的订阅,但它可以是任何在真假之间来回变化的任意条件。由于条件是一个随时间变化的值,因此感觉它应该是另一个与触摸事件流合并的可观察对象,但我一生都无法弄清楚如何做到这一点。

相反,我有一个使用 IObservable 的 TakeWhile 和 SkipUntil 扩展的半工作解决方案。我有一个接收整个应用程序的所有触摸事件的流(oTouchApp),第二个流仅在我的过滤条件为真时接收项目(oTouchPage),然后还有两个其他流将触摸过滤为触摸向下 (oTouchDown) 和向上 (oTouchDown) 操作。所有这些流都是IObservable<IEvent<TouchFrameEventArgs>> 类型,因此可以轻松地合并和比较它们以创建自定义手势。问题是一旦过滤条件从真变为假,我就无法重新启动 oTouchPage 流。我必须手动重新创建流,我希望它以某种方式在打开和关闭之间切换。

这是我到目前为止的代码。任何有关如何使用布尔值(如开/关开关)过滤流的帮助将不胜感激。

var oTouchApp = Observable.FromEvent<TouchFrameEventHandler, TouchFrameEventArgs>(x => new TouchFrameEventHandler(x), ev => Touch.FrameReported += ev, ev => Touch.FrameReported -= ev);
//This stops working after TestCondition goes from True to False
var oTouchPage = oTouchApp.SkipWhile((x) => TestCondition == False).TakeWhile((x) => TestCondition == True);
var oTouchDown = from t in oTouchPage
                 let primarypoint = t.EventArgs.GetPrimaryTouchPoint(this)
                 where primarypoint.Action == TouchAction.Up
                 select t;
var oTouchUp = from t in oTouchPage
               let primarypoint = t.EventArgs.GetPrimaryTouchPoint(this)
               where primarypoint.Action == TouchAction.Down
               select t;
//Code for testing
var sub1 = oTouchPage.Subscribe(x =>{Debug.WriteLine("TouchPage");});
var sub2 = oTouchDown.Subscribe(x =>{Debug.WriteLine("TouchDown");});
var sub3 = oTouchUp.Subscribe(x =>{Debug.WriteLine("TouchUp");});

更新: 结果我只需要在 oTouchPage 流中添加一个简单的 where 子句:var oTouchPage = oTouchApp.Where((x) =&gt; TestCondition == True); 它可能不是最好的解决方案,因为每次生产项目时都会评估 TestCondition,但它运行良好且易于阅读。如果测试条件基于事件或其他易于转换为 IObservable 的条件,那么我认为下面提到的 Window 或 SelectMany 方法可能会更好,但是您可能必须处理“流的流”。我现在正在related question 中与之抗争。

【问题讨论】:

    标签: system.reactive observable


    【解决方案1】:

    TakeWhile 在谓词返回 false 时触发完成,此时一切都被拆除。

    你有两个选择:

    最简单的解决方案是使用Repeat重新启动整个过程:

    var oTouchPage = oTouchApp
        .SkipWhile((x) => TestCondition == false)
        .TakeWhile((x) => TestCondition == true)
        .Repeat();
    

    另一种解决方案是使用SelectMany 跟踪情况以触发每个“轮”侦听的开始(这与“拖放”WPF Rx 示例非常相似):

    // Think of Subject like an observable variable
    // This can just be an IObservable<bool> if the triggers actually come from elsewhere
    Subject<bool> testConditionValues = new Subject<bool>();
    
    var startTrigger = testConditionValues
        .DistinctUntilChanged()
        .Where(v => v == true);
    
    var endTrigger = testConditionValues
        .Where(v => v == false);
    
    var values = from start in startTrigger
                 from touch in touchEvents.TakeUntil(endTrigger)
                 select touch;
    
    // Start listening
    testConditionValues.OnNext(true);
    
    // Stop listening
    testConditionValues.OnNext(false);
    

    【讨论】:

    • 谢谢! Subject 非常适合测试(我使用的是 Observable.Timer :-P)并添加 .Repeat() 就像一个魅力。使用 RX 的整个想法是使代码易于阅读,所以我最喜欢你的第一个建议。不过仍然认为有更好的方法......也许使用新的 Window 运算符。
    【解决方案2】:

    除了 Richard Szalays 的回答之外,您还可以查看 Window 运算符(尽管我认为他的第二个解决方案可能是正确的)。您有希望接收触摸事件的时间窗口。

    private void SetupStream()
    {
        Subject<bool> testConditionValues = new Subject<bool>();
    
        var startTrigger = testConditionValues.DistinctUntilChanged()
             .Where(v => v == true);
    
        var endTrigger = testConditionValues
             .Where(v => v == false);
    
        touchEvents.Window(startTrigger, _ => endTrigger)
            .Subscribe(HandleNewWindow);
    
        // Open window
        testConditionValues.OnNext(true);
    
        // Close window
        testConditionValues.OnNext(false);
    
    }
    
    public void HandleNewWindow(IObservable<IEvent<EventArgs>> events)
    {
        events.Subscribe(mousEvent => Trace.WriteLine(mousEvent));
    }
    

    【讨论】:

    • Window 运算符看起来正是我所需要的,但我对它的用法有点困惑。它返回一个 IObservable 的 IObservable,我只想要一个仅在 TestCondition == True 时产生的流。有没有办法将“顺序流的流”合并为一个可以在 oTouchDown/oTouchUp 中使用的流?还有什么地方可以找到更多关于 Window 运算符的信息或示例?我只能找到blogs.msdn.com/b/rxteam/archive/2010/12/25/…
    • 从第 9 频道找到了一段很棒的视频,其中介绍了 Window 运算符。没有任何真实的例子,但有助于概念化它。大约 30 分钟开始,但整个视频都很好看:channel9.msdn.com/Shows/Going+Deep/…
    • @Greg Bray - 如果您只想一次打开一个窗口(最后一个),您可以使用 Switch 运算符,它将关闭前一个窗口并通过管道传输新窗口并返回给您只是一个 IObservable> (见编辑)。
    • @Greg Bray - 作为记录,我仍然推荐 Rich 的一个例子。您只需要在条件为真时开始侦听事件,并在条件为假时停止。使用 Window 运算符,您始终可以监听事件,然后将它们过滤掉。
    • @Greg Bray - 再次删除 Switch,因为它不适用于后续窗口,因为订阅将在第一个窗口关闭时完成
    猜你喜欢
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 2019-03-24
    • 2015-11-29
    • 2021-12-03
    • 1970-01-01
    • 1970-01-01
    • 2016-05-20
    相关资源
    最近更新 更多