使用随时间变化的任意布尔条件过滤Touch.FrameReported IObservable

时间:2011-01-28 07:43:09

标签: system.reactive observable

我一直在使用Windows Phone 7中的Reactive Extensions(RX),并且非常接近一个可行的解决方案,但却抓住了一个小细节。我正在尝试使用Touch.FrameReportedObservable.FromEvent处理原始触摸事件(这是一个教育任务,可以更好地学习Touch API和RX),但我只想在特定条件下处理事件。例如,我可能希望过滤掉触摸的订阅并仅在枢轴控件中选择特定页面时触摸事件,但它可以是在true和false之间来回切换的任意条件。由于条件是随时间变化的值,因此感觉它应该是与触摸事件流合并的另一个可观察对象,但我不能为我的生活找出如何做到这一点。

相反,我有一个使用IObservable的TakeWhileSkipUntil扩展的半工作解决方案。我有一个流,它接收整个应用程序的所有触摸事件(oTouchApp),第二个流只在我的过滤条件为真(oTouchPage)时才接收项目,然后是另外两个过滤的流接触触摸(oTouchDown)和修饰(oTouchDown)动作。所有这些流都是IObservable<IEvent<TouchFrameEventArgs>>类型,因此可以轻松地将它们合并并进行比较以创建自定义手势。问题是,一旦过滤条件从true变为false,我就无法重新启动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) => TestCondition == True);  它可能不是最好的解决方案,因为每次生成项目时都会评估TestCondition,但它运行良好且易于阅读。如果测试条件是基于事件或其他易于转换为IObservable的条件,那么我认为下面提到的Window或SelectMany方法可能会更好,但是你可能不得不处理“Stream of Stream”。我现在正在related question进行斗争。

2 个答案:

答案 0 :(得分: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));
}

答案 1 :(得分:2)

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);