2011-12-06 12 views
1

これは以前私がこの問題を解決する方法を知っていたので、私の一部ではグーグル・グーグルではないでしょうが、このメソッドは、INotifyPropertyChanged.PropertyChangedイベントをIObservable<Tuple<TProperty,TProperty>>に変換することができます。タプルの値は、プロパティのoldValueとnewValueを表します。Rx INotifyPropertyChanged to IObservable <Tuple <TProperty、TProperty >>

public static IObservable<Tuple<TProperty,TProperty>> ObservePropertyChanged<TNotifier, TProperty>(this TNotifier notifier, 
    Expression<Func<TNotifier, TProperty>> propertyAccessor, 
    bool startWithCurrent = false) 
    where TNotifier : INotifyPropertyChanged { 

    // Parse the expression to find the correct property name. 
    MemberExpression member = (MemberExpression)propertyAccessor.Body; 
    string name = member.Member.Name; 

    // Compile the expression so we can run it to read the property value. 
    var reader = propertyAccessor.Compile(); 

    var propertyChanged = Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
     handler => (sender, args) => handler(sender, args), 
     x => notifier.PropertyChanged += x, 
     x => notifier.PropertyChanged -= x); 

    // Filter the events to the correct property name, then select the value of the property from the notifier. 
    var newValues = from p in propertyChanged 
        where p.EventArgs.PropertyName == name 
        select reader(notifier); 

    throw new NotImplementedException(); 
} 

:この署名を合わせてcredit for below to

public static IObservable<TProperty> ObservePropertyChanged<TNotifier, TProperty>(this TNotifier notifier, 
    Expression<Func<TNotifier, TProperty>> propertyAccessor, 
    bool startWithCurrent = false) 
    where TNotifier : INotifyPropertyChanged { 

    // Parse the expression to find the correct property name. 
    MemberExpression member = (MemberExpression)propertyAccessor.Body; 
    string name = member.Member.Name; 

    // Compile the expression so we can run it to read the property value. 
    var reader = propertyAccessor.Compile(); 

    var propertyChanged = Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
     handler => (sender, args) => handler(sender, args), 
     x => notifier.PropertyChanged += x, 
     x => notifier.PropertyChanged -= x); 

    // Filter the events to the correct property name, then select the value of the property from the notifier. 
    var newValues = from p in propertyChanged 
        where p.EventArgs.PropertyName == name 
        select reader(notifier); 

    // If the caller wants the current value as well as future ones, use Defer() so that the current value is read when the subscription 
    // is added, rather than right now. Otherwise just return the above observable. 
    return startWithCurrent ? Observable.Defer(() => Observable.Return(reader(notifier)).Concat(newValues)) : newValues; 
} 

そして、それを変換します

は、だから私はこのような何かを取るための最良の方法であるかを知りたいです編集:私は多くの異なる演算子を試した後に動作するように見える何かを考え出した。これはこれを達成する正しい方法ですか?私が行方不明のものはありますか?

public static IObservable<Tuple<TProperty,TProperty>> ObserveValueChanged<TNotifier, TProperty>(this TNotifier notifier, 
    Expression<Func<TNotifier, TProperty>> propertyAccessor, 
    bool startWithCurrent = false) 
    where TNotifier : INotifyPropertyChanged { 
    var observable = ObservePropertyChanged(notifier, propertyAccessor, startWithCurrent); 

    return observable.Scan(new Tuple<TProperty, TProperty>(default(TProperty), default(TProperty)), 
        (acc, p) => new Tuple<TProperty, TProperty>(acc.Item2, p)); 

} 

編集:私は次で終わるためにギデオンのソリューションを組み込む:

public static IObservable<Tuple<TProperty, TProperty>> ObserveValueChanged2<TNotifier, TProperty>(this TNotifier notifier, 
    Expression<Func<TNotifier, TProperty>> propertyAccessor, 
    bool startWithCurrent = false) 
    where TNotifier : INotifyPropertyChanged { 

    // Compile the expression so we can run it to read the property value. 
    var reader = propertyAccessor.Compile(); 

    var newValues = ObservePropertyChanged(notifier, propertyAccessor, false); 
    if (startWithCurrent) { 
     var capturedNewValues = newValues; //To prevent warning about modified closure 
     newValues = Observable.Defer(() => Observable.Return(reader(notifier)) 
           .Concat(capturedNewValues)); 
    } 

    return Observable.Create<Tuple<TProperty, TProperty>>(obs => { 
     Tuple<TProperty, TProperty> oldNew = null; 
     return newValues.Subscribe(v => { 
       if (oldNew == null) { 
        oldNew = Tuple.Create(default(TProperty), v); 
       } else { 
        oldNew = Tuple.Create(oldNew.Item2, v); 
        obs.OnNext(oldNew); 
       } 
      }, 
      obs.OnError, 
      obs.OnCompleted); 
    }); 
} 

P.S. 私は結局私の現在の解決策に遭遇しましたが、私は答えを追加するか質問を閉じるべきでしょうか(これは後で役に立つと思われるので削除したくないですか?)私はまだこれが最良の方法であると確信していません

+1

INotifyPropertyChangedはあなたに古い値へのアクセスを与えません。私はこのイベントがあなたがしようとしているものに適しているとは思わない – cadrell0

+0

ReplaySubjectを使って値をキャプチャすることについての議論があることを覚えています。だから私は何とか初期値をキャプチャして、常に古い値と現在/新しい値のずらしたウィンドウを表示できると思った。それは理にかなっていますか?私はちょうどRxでそれをする方法を知らない。 – Damian

+0

私はカスタムイベントを作成して人生を楽にします。 – cadrell0

答えて

3

既存の演算子を使用する場合は、Skipと一緒にZipと一緒に使用するのがおそらく最も近いでしょう。私はおそらく、このような自分自身(NotImplementedを投げる場所を拾う)を書くでしょう:

if (startWithCurrent) 
{ 
    newValues = Observable.Defer(() => Observable.Return(reader(notifier)) 
          .Concat(newValues)); 
} 

return Observable.Create<Tuple<TProperty, TProperty>>(obs => 
    { 
     Tuple<TProperty, TProperty> oldNew = null; 
     return newValues.Subscribe(v => 
      { 
       if (oldNew == null) 
       { 
        oldNew = Tuple.Create(default(TProperty), v); 
       } 
       else 
       { 
        oldNew = Tuple.Create(oldNew.Item2, v); 
        obs.OnNext(oldNew); 
       } 
      }, 
      obs.OnError, 
      obs.OnCompleted); 
    }); 
+0

ありがとうございます。このようなものは私が探していたものです。 – Damian

関連する問題