/// <summary> /// <para>Converts NotificationObject's property to ReactiveProperty. Value is two-way synchronized.</para> /// <para>PropertyChanged raise on selected scheduler.</para> /// </summary> /// <typeparam name="TSubject">The type of the subject.</typeparam> /// <typeparam name="TProperty">The type of the property.</typeparam> /// <typeparam name="TResult">The type of the result.</typeparam> /// <param name="subject">The subject.</param> /// <param name="propertySelector">Argument is self, Return is target property.</param> /// <param name="convert">Convert selector to ReactiveProperty.</param> /// <param name="convertBack">Convert selector to source.</param> /// <param name="mode">ReactiveProperty mode.</param> /// <returns></returns> public static ReactivePropertySlim <TResult> ToReactivePropertySlimAsSynchronized <TSubject, TProperty, TResult>( this TSubject subject, Expression <Func <TSubject, TProperty> > propertySelector, Func <IObservable <TProperty>, IObservable <TResult> > convert, Func <IObservable <TResult>, IObservable <TProperty> > convertBack, ReactivePropertyMode mode = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueOnSubscribe) where TSubject : INotifyPropertyChanged { if (ExpressionTreeUtils.IsNestedPropertyPath(propertySelector)) { var result = new ReactivePropertySlim <TResult>(mode: mode); var observer = PropertyObservable.CreateFromPropertySelector(subject, propertySelector); IDisposable disposable = null; disposable = convert(subject.ObserveProperty(propertySelector, isPushCurrentValueAtFirst: true)) .Subscribe(x => result.Value = x); convertBack(result) .Subscribe(x => observer.SetPropertyPathValue(x), _ => disposable.Dispose(), () => disposable.Dispose()); return(result); } else { var setter = AccessorCache <TSubject> .LookupSet(propertySelector, out _); var result = new ReactivePropertySlim <TResult>(mode: mode); IDisposable disposable = null; disposable = convert(subject.ObserveProperty(propertySelector, isPushCurrentValueAtFirst: true)) .Subscribe(x => result.Value = x); convertBack(result) .Subscribe(x => setter(subject, x), _ => disposable.Dispose(), () => disposable.Dispose()); return(result); } }
/// <summary> /// <para>Converts NotificationObject's property to ReactiveProperty. Value is two-way synchronized.</para> /// <para>PropertyChanged raise on selected scheduler.</para> /// </summary> /// <typeparam name="TSubject">The type of the subject.</typeparam> /// <typeparam name="TProperty">The type of the property.</typeparam> /// <typeparam name="TResult">The type of the result.</typeparam> /// <param name="subject">The subject.</param> /// <param name="propertySelector">Argument is self, Return is target property.</param> /// <param name="convert">Convert selector to ReactiveProperty.</param> /// <param name="convertBack">Convert selector to source.</param> /// <param name="raiseEventScheduler">The raise event scheduler.</param> /// <param name="mode">ReactiveProperty mode.</param> /// <param name="ignoreValidationErrorValue">Ignore validation error value.</param> /// <returns></returns> public static ReactiveProperty <TResult> ToReactivePropertyAsSynchronized <TSubject, TProperty, TResult>( this TSubject subject, Expression <Func <TSubject, TProperty> > propertySelector, Func <IObservable <TProperty>, IObservable <TResult> > convert, Func <IObservable <TResult>, IObservable <TProperty> > convertBack, IScheduler raiseEventScheduler, ReactivePropertyMode mode = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueOnSubscribe, bool ignoreValidationErrorValue = false) where TSubject : INotifyPropertyChanged { if (ExpressionTreeUtils.IsNestedPropertyPath(propertySelector)) { var observer = PropertyObservable.CreateFromPropertySelector(subject, propertySelector); var result = convert(Observable.Using(() => observer, x => x) .StartWith(observer.GetPropertyPathValue())) .ToReactiveProperty(raiseEventScheduler, mode: mode); convertBack(result.Where(_ => !ignoreValidationErrorValue || !result.HasErrors)) .Subscribe(x => observer.SetPropertyPathValue(x)); return(result); } else { var setter = AccessorCache <TSubject> .LookupSet(propertySelector, out _); var result = convert(subject.ObserveProperty(propertySelector, isPushCurrentValueAtFirst: true)) .ToReactiveProperty(raiseEventScheduler, mode: mode); convertBack(result.Where(_ => !ignoreValidationErrorValue || !result.HasErrors)) .Subscribe(x => setter(subject, x)); return(result); } }
public void ChangeObserveSourcePropertyType() { var item = new Item { Child = new Item { Value = 10, }, }; var testScheduler = new TestScheduler(); var testObserver = testScheduler.CreateObserver <int>(); var path = PropertyObservable.CreateFromPropertySelector(item, x => x.Child.Value); path.Subscribe(testObserver); path.GetPropertyPathValue().Is(10); item.Child.Value = 1; testObserver.Messages.Is(OnNext(0, 1)); item.Child = new AnotherItem { Value = 9999, }; path.GetPropertyPathValue().Is(9999); testObserver.Messages.Is(OnNext(0, 1), OnNext(0, 9999)); }
public void ObserveProperty() { var item = new Item { Child = new Item { Value = 10, }, }; var testScheduler = new TestScheduler(); var testObserver = testScheduler.CreateObserver <int>(); var path = PropertyObservable.CreateFromPropertySelector(item, x => x.Child.Value); path.Subscribe(testObserver); path.GetPropertyPathValue().Is(10); item.Child.Value = 1; testObserver.Messages.Is(OnNext(0, 1)); item.IsPropertyChangedEmpty.IsFalse(); path.Dispose(); item.IsPropertyChangedEmpty.IsTrue(); item.Child.Value = 100; testObserver.Messages.Is(OnNext(0, 1)); }
private static IObservable <TProperty> ObserveNestedProperty <TSubject, TProperty>( this TSubject subject, Expression <Func <TSubject, TProperty> > propertySelector, bool isPushCurrentValueAtFirst = true) where TSubject : INotifyPropertyChanged { var propertyObserver = PropertyObservable.CreateFromPropertySelector(subject, propertySelector); var ox = Observable.Using(() => propertyObserver, x => x); return(isPushCurrentValueAtFirst ? ox.StartWith(propertyObserver.GetPropertyPathValue()) : ox.AsObservable()); }
public static PropertyObservable <TProperty> CreateFromPropertySelector <TSubject, TProperty>(TSubject subject, Expression <Func <TSubject, TProperty> > propertySelector) where TSubject : INotifyPropertyChanged { if (!(propertySelector.Body is MemberExpression current)) { throw new ArgumentException(); } var result = new PropertyObservable <TProperty>(); result.SetRootNode(PropertyPathNode.CreateFromPropertySelector(propertySelector)); result.SetSource(subject); return(result); }
public void ObserveReactiveProperty() { var rp = new ReactiveProperty <ClassThatDoesNotImplementedINotifyPropertyChanged>(); var testScheduler = new TestScheduler(); var testObserver = testScheduler.CreateObserver <string>(); var path = PropertyObservable.CreateFromPropertySelector(rp, x => x.Value.Name); path.Subscribe(testObserver); path.GetPropertyPathValue().IsNull(); rp.Value = new ClassThatDoesNotImplementedINotifyPropertyChanged { Name = "name1" }; path.GetPropertyPathValue().Is("name1"); }