Rx: игнорирование обновлений, вызванных подписчиками - PullRequest
2 голосов
/ 27 октября 2010

У меня проблемы с выяснением, как это сделать.У меня есть два экземпляра (источник и цель), которые реализуют INotifyPropertyChanged, и я отслеживаю событие PropertyChanged для обоих.То, что я хочу сделать, - это запускать действие в любое время, когда значение source.PropertyChanged повышено, до значения target.PropertyChanged.Я могу сделать это просто так:

INotifyPropertyChanged source;
INotifyPropertyChanged target;

var sourcePropertyChanged = Observable
    .FromEvent<PropertyChangedEventArgs>(source, "PropertyChanged")
    .Where(x => x.EventArgs.PropertyName == sourcePropertyName);

var targetPropertyChanged = Observable
    .FromEvent<PropertyChangedEventArgs>(target, "PropertyChanged")
    .Where(x => x.EventArgs.PropertyName == targetPropertyName);

sourcePropertyChanged
    .TakeUntil(targetPropertyChanged)
    .ObserveOnDispatcher()
    .Subscribe(_ => /*Raises target.PropertyChanged for targetPropertyName*/);

Проблема, с которой я столкнулся, заключается в том, что я хочу игнорировать уведомления PropertyChanged, вызванные действиями, и прекращать принимать значения только тогда, когдаподнятый внешним источником.Есть ли хороший способ добиться этого?

Ответы [ 3 ]

4 голосов
/ 27 октября 2010

Нет встроенного способа делать то, о чем вы говорите. Вот простая реализация SkipWhen, которая пропускает следующее исходное значение каждый раз, когда значение принимается в «другой» последовательности:

public static IObservable<TSource> SkipWhen(this IObservable<TSource> source, 
    IObservable<TOther> other)
{
    return Observable.Defer<TSource>(() =>
    {
        object lockObject = new object();
        Stack<TOther> skipStack = new Stack<TOther>();

        other.Subscribe(x => { lock(lockObject) { skipStack.Push(x); });

        return source.Where(_ =>
        {
            lock(lockObject);
            {
                if (skipStack.Count > 0)
                {
                    skipStack.Pop();
                    return false;
                }
                else
                {
                    return true;
                }
            }
        });
    });
}

Ваш код будет обновлен следующим образом (см. Мое примечание ниже):

INotifyPropertyChanged source;
INotifyPropertyChanged target;

// See the link at the bottom of my answer
var sourcePropertyChanged = source.GetPropertyChangeValues(x => x.SourceProperty);

// Unit is Rx's "void"
var targetChangedLocally = new Subject<Unit>();

var targetPropertyChanged = target.GetPropertyChangeValues(x => x.TargetProperty)
    .SkipWhen(targetChangedLocally);

sourcePropertyChanged
    .TakeUntil(targetPropertyChanged)
    .ObserveOnDispatcher()
    .Subscribe(_ =>
    {
        targetChangedLocally.OnNext();
        /*Raises target.PropertyChanged for targetPropertyName*/
    });

NB. Недавно я писал в блоге о строго типизированной обертке IObservable для событий INotifyPropertyChanged ; не стесняйтесь украсть этот код.

0 голосов
/ 06 ноября 2010

Использование блокировок в Rx - это нормально. Блокировка недолговечна и не вызывает код пользователя.

0 голосов
/ 28 октября 2010

Нет встроенного способа, но вы, вероятно, могли бы отфильтровать события, используя метод расширения Where для наблюдаемого.Условие для фильтрации будет отправителем события.Я полагаю, что отправитель события target.PropertyChanged отличается от отправителя события PropertyChanged, вызванного другим источником.

Я не совсем уверен, что вы можете использовать такой подход.

...