/// <summary> /// Given a fully filled-out IObservedChange object, SetValueToProperty /// will apply it to the specified object (i.e. it will ensure that /// target.property == This.GetValue() and "replay" the observed change /// onto another object) /// </summary> /// <param name="target">The target object to apply the change to.</param> /// <param name="property">The target property to apply the change to.</param> internal static void SetValueToProperty <TSender, TValue, TTarget>( this IObservedChange <TSender, TValue> This, TTarget target, Expression <Func <TTarget, TValue> > property) { Reflection.TrySetValueToPropertyChain(target, Reflection.Rewrite(property.Body).GetExpressionChain(), This.GetValue()); }
private IReactiveBinding <TView, TViewModel, Tuple <object, bool> > BindImpl <TViewModel, TView, TVMProp, TVProp, TDontCare>( TViewModel viewModel, TView view, Expression <Func <TViewModel, TVMProp> > vmProperty, Expression <Func <TView, TVProp> > viewProperty, IObservable <TDontCare> signalViewUpdate, OutFunc <TVMProp, TVProp> vmToViewConverter, OutFunc <TVProp, TVMProp> viewToVmConverter) where TView : class, IViewFor where TViewModel : class { var signalInitialUpdate = new Subject <bool>(); var vmExpression = Reflection.Rewrite(vmProperty.Body); var viewExpression = Reflection.Rewrite(viewProperty.Body); var signalObservable = signalViewUpdate != null ? signalViewUpdate.Select(_ => false) : view.WhenAnyDynamic(viewExpression, x => (TVProp)x.Value).Select(_ => false); var somethingChanged = Observable.Merge( Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Select(_ => true), signalInitialUpdate.Select(_ => true), signalObservable); var changeWithValues = somethingChanged.Select(isVm => { TVMProp vmValue; TVProp vValue; if (!Reflection.TryGetValueForPropertyChain(out vmValue, view.ViewModel, vmExpression.GetExpressionChain()) || !Reflection.TryGetValueForPropertyChain(out vValue, view, viewExpression.GetExpressionChain())) { return(null); } if (isVm) { TVProp vmAsView; if (!vmToViewConverter(vmValue, out vmAsView) || EqualityComparer <TVProp> .Default.Equals(vValue, vmAsView)) { return(null); } return(Tuple.Create((object)vmAsView, isVm)); } TVMProp vAsViewModel; if (!viewToVmConverter(vValue, out vAsViewModel) || EqualityComparer <TVMProp> .Default.Equals(vmValue, vAsViewModel)) { return(null); } return(Tuple.Create((object)vAsViewModel, isVm)); }); var ret = EvalBindingHooks(viewModel, view, vmExpression, viewExpression, BindingDirection.TwoWay); if (!ret) { return(null); } IObservable <Tuple <object, bool> > changes = changeWithValues.Where(tuple => tuple != null).Publish().RefCount(); IDisposable disp = changes.Subscribe(isVmWithLatestValue => { if (isVmWithLatestValue.Item2) { Reflection.TrySetValueToPropertyChain(view, viewExpression.GetExpressionChain(), isVmWithLatestValue.Item1, false); } else { Reflection.TrySetValueToPropertyChain(view.ViewModel, vmExpression.GetExpressionChain(), isVmWithLatestValue.Item1, false); } }); // NB: Even though it's technically a two-way bind, most people // want the ViewModel to win at first. signalInitialUpdate.OnNext(true); return(new ReactiveBinding <TView, TViewModel, Tuple <object, bool> >( view, viewModel, viewExpression, vmExpression, changes, BindingDirection.TwoWay, disp)); }