Фильтрация IObservable Touch.FrameReported с использованием произвольного логического условия, которое изменяется со временем - PullRequest
1 голос
/ 28 января 2011

Я играл с Reactive Extensions (RX) в Windows Phone 7 и очень близок к рабочему решению, но поймал одну маленькую деталь. Я пытаюсь обработать необработанные сенсорные события, используя Touch.FrameReported и Observable.FromEvent (что-то вроде образовательного квеста, чтобы лучше изучить Touch API и RX), но я хочу обрабатывать события только при определенных условиях. условия. Например, я могу захотеть отфильтровать подписку по событиям касания и касания только тогда, когда определенная страница выбрана в сводном элементе управления, но это может быть любое произвольное условие, которое изменяется между истиной и ложью. Поскольку условие - это значение, которое меняется со временем, создается впечатление, что оно должно быть еще одной наблюдаемой, которая сливается с потоком событий касания, но я не могу понять, как это сделать.

Вместо этого у меня есть полу-рабочее решение, использующее расширения TakeWhile и SkipUntil для IObservable. У меня есть поток, который получает все события касания для всего приложения (oTouchApp), второй поток, который принимает элементы только при условии, что мое условие фильтрации выполнено (oTouchPage), а затем два других потока, которые фильтруют касания, касаются вниз (oTouchDown) и подправить (oTouchDown) действия. Все эти потоки имеют тип IObservable<IEvent<TouchFrameEventArgs>>, поэтому их легко объединять и сравнивать для создания пользовательских жестов. Проблема в том, что я не могу перезапустить поток oTouchPage, как только условие фильтра изменится с true на false. Мне пришлось бы вручную воссоздавать поток, где, как я бы предпочел, он как-то переключается между включением и выключением.

Вот код, который у меня есть. Будем весьма благодарны за любую помощь в том, как отфильтровать поток, используя логическое значение (например, включение / выключение).

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

UPDATE: Оказывается, все, что мне было нужно, это добавить простое предложение where в поток oTouchPage: var oTouchPage = oTouchApp.Where((x) => TestCondition == True); Возможно, это не лучшее решение, поскольку тестовое условие оценивается каждый раз, когда создается элемент, но оно хорошо работает и легко читается. Если тестовое условие основывалось на событиях или каком-либо другом условии, которое было легко преобразовать в IObservable, то я думаю, что упомянутые ниже подходы Window или SelectMany могли бы быть лучше, но тогда вам, возможно, придется иметь дело с «потоком потоков». Я борюсь с этим прямо сейчас в связанном вопросе .

Ответы [ 2 ]

2 голосов
/ 28 января 2011

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);
2 голосов
/ 28 января 2011

В дополнение к ответу Ричарда Сзалайса вы также можете посмотреть на оператор 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));
}
...