/// <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); var 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); var 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> /// Data binding method. /// </summary> /// <typeparam name="TView">View type</typeparam> /// <typeparam name="TProperty">Property type</typeparam> /// <param name="self">View</param> /// <param name="propertySelector">Target property selector</param> /// <param name="source">Source property</param> /// <param name="updateSourceTrigger">Update source trigger</param> /// <returns>Data binding token</returns> public static IDisposable SetBindingTableViewDataSource <TView, TProperty>( this TView self, Expression <Func <TView, TProperty> > propertySelector, ReactiveProperty <TProperty> source, Func <TView, IObservable <Unit> > updateSourceTrigger = null) where TView : IUITableViewDataSource { var d = new CompositeDisposable(); var isUpdating = false; var setter = AccessorCache <TView> .LookupSet(propertySelector, out var propertyName); source .Where(_ => !isUpdating) .Subscribe(x => setter(self, x)) .AddTo(d); if (updateSourceTrigger != null) { var getter = AccessorCache <TView> .LookupGet(propertySelector, out propertyName); updateSourceTrigger(self).Subscribe(_ => { isUpdating = true; try { source.Value = getter(self); } finally { isUpdating = false; } }).AddTo(d); } return(d); }
/// <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 <TResult>(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 <TResult>(raiseEventScheduler, mode: mode); convertBack(result.Where(_ => !ignoreValidationErrorValue || !result.HasErrors)) .Subscribe(x => setter(subject, x)); return(result); } }
private static IDisposable CreateTowWayBinding <T, TTarget, TProperty>(IReactiveProperty <T> self, TTarget target, Expression <Func <TTarget, TProperty> > propertySelector, Func <T, TProperty> convert, Func <TProperty, T> convertBack, IObservable <Unit> targetUpdateTrigger, TProperty propertyFallbackValue, T sourceFallbackValue) { if (targetUpdateTrigger == null) { throw new NotSupportedException("TwoWay binding required targetUpdateTrigger parameter."); } var propertyName = default(string); var d = new CompositeDisposable(); var targetUpdating = false; var sourceUpdating = false; targetUpdateTrigger .Subscribe(_ => { if (sourceUpdating) { return; } targetUpdating = true; try { self.Value = convertBack(AccessorCache <TTarget> .LookupGet(propertySelector, out propertyName)(target)); } catch (Exception ex) { Debug.WriteLine(ex); self.Value = sourceFallbackValue; } targetUpdating = false; }) .AddTo(d); self.Subscribe(value => { if (targetUpdating) { return; } var setter = AccessorCache <TTarget> .LookupSet(propertySelector, out propertyName); sourceUpdating = true; try { setter(target, convert(value)); } catch (Exception ex) { Debug.WriteLine(ex); setter(target, propertyFallbackValue); } sourceUpdating = false; }) .AddTo(d); return(d); }
/// <summary> /// Data binding method. /// </summary> /// <typeparam name="TView">View type</typeparam> /// <typeparam name="TProperty">Property type</typeparam> /// <param name="self">View</param> /// <param name="propertySelector">Target property selector</param> /// <param name="source">Source property</param> /// <returns>Data binding token</returns> public static IDisposable SetBindingTableViewDataSource <TView, TProperty>( this TView self, Expression <Func <TView, TProperty> > propertySelector, ReadOnlyReactiveProperty <TProperty> source) where TView : IUITableViewDataSource { var d = new CompositeDisposable(); var setter = AccessorCache <TView> .LookupSet(propertySelector, out var propertyName); source .Subscribe(x => setter(self, x)) .AddTo(d); return(d); }
/// <summary> /// Data binding method. /// </summary> /// <typeparam name="TView">View type</typeparam> /// <typeparam name="TProperty">Property type</typeparam> /// <param name="self">View</param> /// <param name="propertySelector">Target property selector</param> /// <param name="source">Source property</param> /// <returns>Data binding token</returns> public static IDisposable SetBindingGestureRecognizer <TView, TProperty>( this TView self, Expression <Func <TView, TProperty> > propertySelector, ReadOnlyReactiveProperty <TProperty> source) where TView : UIGestureRecognizer { var d = new CompositeDisposable(); string propertyName; var setter = AccessorCache <TView> .LookupSet(propertySelector, out propertyName); source .Subscribe(x => setter(self, x)) .AddTo(d); return(d); }
private static IDisposable CreateOneWayBinding <T, TTarget, TProperty>(IObservable <T> self, TTarget target, Expression <Func <TTarget, TProperty> > propertySelector, Func <T, TProperty> convert, TProperty propertyFallbackValue) { return(self .Subscribe(value => { var setter = AccessorCache <TTarget> .LookupSet(propertySelector, out var _); try { setter(target, convert(value)); } catch (Exception ex) { Debug.WriteLine(ex); setter(target, propertyFallbackValue); } })); }
/// <summary> /// Data binding method. /// </summary> /// <typeparam name="TView">View type</typeparam> /// <typeparam name="TProperty">Property type</typeparam> /// <param name="self">View</param> /// <param name="propertySelector">Target property selector</param> /// <param name="source">Source property</param> /// <returns>Data binding token</returns> public static IDisposable SetBinding <TView, TProperty>( this TView self, Expression <Func <TView, TProperty> > propertySelector, ReadOnlyReactiveProperty <TProperty> source) where TView : View { var d = new CompositeDisposable(); bool isUpdating = false; string propertyName; var setter = AccessorCache <TView> .LookupSet(propertySelector, out propertyName); source .Where(_ => !isUpdating) .Subscribe(x => setter(self, x)) .AddTo(d); return(d); }
public void LookupGetAndSet() { var get = AccessorCache <Person> .LookupGet(p => p.Name, out var prop); Assert.AreEqual("Name", prop); var set = AccessorCache <Person> .LookupSet(p => p.Name, out prop); Assert.AreEqual("Name", prop); var person = new Person { Name = "tanaka" }; get(person).Is("tanaka"); set(person, "kimura"); person.Name.Is("kimura"); }
public bool SetPropertyPathValue(object?value) { if (Source == null) { return(false); } if (Next != null) { return(Next.SetPropertyPathValue(value)); } else { var setter = _setAccessor ?? (_setAccessor = AccessorCache.LookupSet(Source.GetType(), PropertyName)); setter.DynamicInvoke(Source, value); return(true); } }
public static IDisposable OneWayBind <TType, TProperty>(this TType type, Expression <Func <TType, TProperty> > selector, IObservable <TProperty> observable) { return(observable.ObserveOnUIDispatcher() .Subscribe(x => { AccessorCache <TType> .LookupSet(selector).Invoke(type, x); })); }