public IDisposable OneWayBind <TViewModel, TView, TVMProp, TVProp>( TViewModel viewModel, TView view, Expression <Func <TViewModel, TVMProp> > vmProperty, Expression <Func <TView, TVProp> > viewProperty, Func <TVMProp> fallbackValue = null, object conversionHint = null) where TViewModel : class where TView : IViewFor { if (viewProperty == null) { var viewPropChain = Reflection.getDefaultViewPropChain(view, Reflection.ExpressionToPropertyNames(vmProperty)); var viewType = Reflection.GetTypesForPropChain(typeof(TView), viewPropChain).Last(); var converter = getConverterForTypes(typeof(TVMProp), viewType); if (converter == null) { throw new ArgumentException(String.Format("Can't convert {0} to {1}. To fix this, register a IBindingTypeConverter", typeof(TVMProp), viewType)); } return(Reflection.ViewModelWhenAnyValue(viewModel, view, vmProperty) .SelectMany(x => { object tmp; if (!converter.TryConvert(x, viewType, conversionHint, out tmp)) { return Observable.Empty <object>(); } return Observable.Return(tmp); }) .Subscribe(x => Reflection.SetValueToPropertyChain(view, viewPropChain, x, false))); } else { var converter = getConverterForTypes(typeof(TVMProp), typeof(TVProp)); if (converter == null) { throw new ArgumentException(String.Format("Can't convert {0} to {1}. To fix this, register a IBindingTypeConverter", typeof(TVMProp), typeof(TVProp))); } return(Reflection.ViewModelWhenAnyValue(viewModel, view, vmProperty) .SelectMany(x => { object tmp; if (!converter.TryConvert(x, typeof(TVProp), conversionHint, out tmp)) { return Observable.Empty <TVProp>(); } return Observable.Return(tmp == null ? default(TVProp) : (TVProp)tmp); }) .BindTo(view, viewProperty, () => { object tmp; return converter.TryConvert(fallbackValue(), typeof(TVProp), conversionHint, out tmp) ? (TVProp)tmp : default(TVProp); })); } }
public IDisposable AsyncOneWayBind <TViewModel, TView, TProp, TOut>( TViewModel viewModel, TView view, Expression <Func <TViewModel, TProp> > vmProperty, Expression <Func <TView, TOut> > viewProperty, Func <TProp, IObservable <TOut> > selector, Func <TOut> fallbackValue = null) where TViewModel : class where TView : IViewFor { if (viewProperty == null) { var viewPropChain = Reflection.getDefaultViewPropChain(view, Reflection.ExpressionToPropertyNames(vmProperty)); return(Reflection.ViewModelWhenAnyValue(viewModel, view, vmProperty) .SelectMany(selector) .Subscribe(x => Reflection.SetValueToPropertyChain(view, viewPropChain, x, false))); } return(Reflection.ViewModelWhenAnyValue(viewModel, view, vmProperty) .SelectMany(selector) .BindTo(view, viewProperty, fallbackValue)); }
public IDisposable Bind <TViewModel, TView, TVMProp, TVProp, TDontCare>( TViewModel viewModel, TView view, Expression <Func <TViewModel, TVMProp> > vmProperty, Expression <Func <TView, TVProp> > viewProperty, IObservable <TDontCare> signalViewUpdate, object conversionHint) where TViewModel : class where TView : IViewFor { var signalInitialUpdate = new Subject <bool>(); var vmPropChain = Reflection.ExpressionToPropertyNames(vmProperty); string[] viewPropChain; if (viewProperty == null) { viewPropChain = Reflection.getDefaultViewPropChain(view, vmPropChain); } else { viewPropChain = Reflection.ExpressionToPropertyNames(viewProperty); } var vmToViewConverter = getConverterForTypes(typeof(TVMProp), typeof(TVProp)); var viewToVMConverter = getConverterForTypes(typeof(TVProp), typeof(TVMProp)); if (vmToViewConverter == null || viewToVMConverter == null) { throw new ArgumentException( String.Format("Can't two-way convert between {0} and {1}. To fix this, register a IBindingTypeConverter", typeof(TVMProp), typeof(TVProp))); } var somethingChanged = Observable.Merge( Reflection.ViewModelWhenAnyValue(viewModel, view, vmProperty).Select(_ => true), signalInitialUpdate, signalViewUpdate != null ? signalViewUpdate.Select(_ => false) : view.WhenAnyDynamic(viewPropChain, x => (TVProp)x.Value).Select(_ => false)); string vmChangedString = String.Format("Setting {0}.{1} => {2}.{3}: ", typeof(TViewModel).Name, String.Join(".", vmPropChain), typeof(TView).Name, String.Join(".", viewPropChain)); string viewChangedString = String.Format("Setting {0}.{1} => {2}.{3}: ", typeof(TView).Name, String.Join(".", viewPropChain), typeof(TViewModel).Name, String.Join(".", vmPropChain)); var changeWithValues = somethingChanged.Select(isVm => { TVMProp vmValue; TVProp vValue; if (!Reflection.TryGetValueForPropertyChain(out vmValue, view.ViewModel, vmPropChain) || !Reflection.TryGetValueForPropertyChain(out vValue, view, viewPropChain)) { return(null); } if (isVm) { var vmAsView = (TVProp)vmToViewConverter.Convert(vmValue, typeof(TVProp), conversionHint); var changed = EqualityComparer <TVProp> .Default.Equals(vValue, vmAsView) != true; if (!changed) { return(null); } this.Log().Info(vmChangedString + (vmAsView != null ? vmAsView.ToString() : "(null)")); return(Tuple.Create((object)vmAsView, isVm)); } else { var vAsViewModel = (TVMProp)viewToVMConverter.Convert(vValue, typeof(TVMProp), conversionHint); var changed = EqualityComparer <TVMProp> .Default.Equals(vmValue, vAsViewModel) != true; if (!changed) { return(null); } this.Log().Info(viewChangedString + (vAsViewModel != null ? vAsViewModel.ToString() : "(null)")); return(Tuple.Create((object)vAsViewModel, isVm)); } }); var ret = changeWithValues.Subscribe(isVmWithLatestValue => { if (isVmWithLatestValue == null) { return; } if (isVmWithLatestValue.Item2) { Reflection.SetValueToPropertyChain(view, viewPropChain, isVmWithLatestValue.Item1, false); } else { Reflection.SetValueToPropertyChain(view.ViewModel, vmPropChain, 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(ret); }