1

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

TakeWhile相反,我有一个使用 IObservable和SkipUntil扩展的半工作解决方案。我有一个接收整个应用程序的所有触摸事件的oTouchAppoTouchPageoTouchDown( oTouchDown) 行动。所有这些流都是 type 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) => TestCondition == True); 这可能不是最佳解决方案,因为每次生成项目时都会评估 TestCondition,但它运行良好且易于阅读。如果测试条件基于事件或其他易于转换为 IObservable 的条件,那么我认为下面提到的 Window 或 SelectMany 方法可能会更好,但是您可能必须处理“流的流”。我现在正在一个相关问题中与之抗争。

4

2 回答 2

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));
}
于 2011-01-28T09:15:09.410 回答
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);
于 2011-01-28T09:28:52.157 回答