2011-12-06 2 views
1

이것은 내가 전에 wbe에 대한 해결책을 알았 기 때문에 이것은 내 부분에서 인터넷 검색이 잘 안되는 경우 여야합니다. 메소드 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); 
    }); 
} 

추신 : 나는 다음과 같이 결국 기드온의 솔루션을 통합 필자는 현재 현재의 해결책을 찾았지만 결국 대답을 추가하거나 질문을 닫으면 어떤 에티켓을 위반하고 싶지 않습니다. (나중에 유용 할 수도 있으므로 삭제하지 않기를 바랍니다.) 여전히 이것이 최선의 방법이라고 확신하지 못합니다..

+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

관련 문제