Rx:忽略订阅者导致的更新

时间:2010-10-27 15:12:36

标签: c# silverlight silverlight-4.0 system.reactive

我在弄清楚如何做到这一点时遇到了问题。我有两个实施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通知,并且只有在外部源引发PropertyChanged事件时才停止获取值。是否有一种很好的方法可以实现这一目标?

3 个答案:

答案 0 :(得分:4)

没有内置的方式来做你正在谈论的事情。这是一个简单的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*/
    });

注意:我最近写了一篇关于a strongly typed IObservable wrapper around INotifyPropertyChanged events的博文;随意窃取该代码。

答案 1 :(得分:0)

没有内置方法,但您可以使用Where扩展方法过滤掉事件,以便观察。过滤条件将是事件的发送者。我认为target.PropertyChanged事件的发件人与其他来源引发的PropertyChanged事件的发件人不同。

我不完全确定这是否是您可以使用的方法。

答案 2 :(得分:0)

在Rx中使用锁这种方式很好。锁是短暂的,不会调用用户代码。