static ObservableAsPropertyHelper <TRet> observableToProperty <TObj, TRet>(
            this TObj This,
            IObservable <TRet> observable,
            Expression <Func <TObj, TRet> > property,
            TRet initialValue    = default(TRet),
            IScheduler scheduler = null)
            where TObj : ReactiveObject
        {
            Contract.Requires(This != null);
            Contract.Requires(observable != null);
            Contract.Requires(property != null);

            Expression expression = Reflection.Rewrite(property.Body);

            if (expression.GetParent().NodeType != ExpressionType.Parameter)
            {
                throw new ArgumentException("Property expression must be of the form 'x => x.SomeProperty'");
            }

            var ret = new ObservableAsPropertyHelper <TRet>(observable,
                                                            _ => This.raisePropertyChanged(expression.GetMemberInfo().Name),
                                                            initialValue, scheduler);

            return(ret);
        }
        private static ObservableAsPropertyHelper <TRet> ObservableToProperty <TObj, TRet>(
            this TObj target,
            IObservable <TRet> observable,
            Expression <Func <TObj, TRet> > property,
            Func <TRet> getInitialValue,
            bool deferSubscription = false,
            IScheduler?scheduler   = null)
            where TObj : class, IReactiveObject
        {
            if (target is null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            if (observable is null)
            {
                throw new ArgumentNullException(nameof(observable));
            }

            if (property is null)
            {
                throw new ArgumentNullException(nameof(property));
            }

            Expression expression = Reflection.Rewrite(property.Body);

            var parent = expression.GetParent();

            if (parent is null)
            {
                throw new ArgumentException("The property expression does not have a valid parent.", nameof(property));
            }

            if (parent.NodeType != ExpressionType.Parameter)
            {
                throw new ArgumentException("Property expression must be of the form 'x => x.SomeProperty'");
            }

            var memberInfo = expression.GetMemberInfo();

            if (memberInfo is null)
            {
                throw new ArgumentException("The property expression does not point towards a valid member.", nameof(property));
            }

            string name = memberInfo.Name;

            if (expression is IndexExpression)
            {
                name += "[]";
            }

            return(new ObservableAsPropertyHelper <TRet>(
                       observable,
                       _ => target.RaisingPropertyChanged(name),
                       _ => target.RaisingPropertyChanging(name),
                       getInitialValue,
                       deferSubscription,
                       scheduler));
        }
Esempio n. 3
0
 /// <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>
 /// <typeparam name="TSender">The sender type.</typeparam>
 /// <typeparam name="TValue">The value type.</typeparam>
 /// <typeparam name="TTarget">The target type.</typeparam>
 /// <param name="item">
 /// The <see cref="IObservedChange{TSender, TValue}"/> instance to use as a
 /// value to apply.
 /// </param>
 /// <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> item,
     TTarget target,
     Expression <Func <TTarget, TValue> > property)
 {
     Reflection.TrySetValueToPropertyChain(target, Reflection.Rewrite(property.Body).GetExpressionChain(), item.GetValue());
 }
Esempio n. 4
0
        public static IObservable <IObservedChange <TSender, TValue> > SubscribeToExpressionChain <TSender, TValue> (
            this TSender source,
            Expression expression,
            bool beforeChange = false,
            bool skipInitial  = true)
        {
            IObservable <IObservedChange <object, object> > notifier =
                Observable.Return(new ObservedChange <object, object>(null, null, source));

            IEnumerable <Expression> chain = Reflection.Rewrite(expression).GetExpressionChain();

            notifier = chain.Aggregate(notifier, (n, expr) => n
                                       .Select(y => nestedObservedChanges(expr, y, beforeChange))
                                       .Switch());

            if (skipInitial)
            {
                notifier = notifier.Skip(1);
            }

            notifier = notifier.Where(x => x.Sender != null);

            var r = notifier.Select(x => {
                // ensure cast to TValue will succeed, throw useful exception otherwise
                var val = x.GetValue();
                if (val != null && !(val is TValue))
                {
                    throw new InvalidCastException(string.Format("Unable to cast from {0} to {1}.", val.GetType(), typeof(TValue)));
                }

                return(new ObservedChange <TSender, TValue>(source, expression, (TValue)val));
            });

            return(r.DistinctUntilChanged(x => x.Value));
        }
        /// <summary>
        /// Bind a command from the ViewModel to an explicitly specified control
        /// on the View.
        /// </summary>
        /// <typeparam name="TView">The view type.</typeparam>
        /// <typeparam name="TViewModel">The view model type.</typeparam>
        /// <typeparam name="TProp">The property type.</typeparam>
        /// <typeparam name="TControl">The control type.</typeparam>
        /// <typeparam name="TParam">The parameter type.</typeparam>
        /// <param name="viewModel">The View model.</param>
        /// <param name="view">The View.</param>
        /// <param name="vmProperty">The ViewModel command to bind.</param>
        /// <param name="controlProperty">The name of the control on the view.</param>
        /// <param name="withParameter">The ViewModel property to pass as the
        /// param of the ICommand.</param>
        /// <param name="toEvent">If specified, bind to the specific event
        /// instead of the default.
        /// NOTE: If this parameter is used inside WhenActivated, it's
        /// important to dispose the binding when the view is deactivated.</param>
        /// <returns>A class representing the binding. Dispose it to disconnect
        /// the binding.</returns>
        public IReactiveBinding <TView, TProp> BindCommand <TView, TViewModel, TProp, TControl, TParam>(
            TViewModel viewModel,
            TView view,
            Expression <Func <TViewModel, TProp> > vmProperty,
            Expression <Func <TView, TControl> > controlProperty,
            Func <TParam> withParameter,
            string?toEvent = null)
            where TViewModel : class
            where TView : class, IViewFor <TViewModel>
            where TProp : ICommand
        {
            if (vmProperty is null)
            {
                throw new ArgumentNullException(nameof(vmProperty));
            }

            if (controlProperty is null)
            {
                throw new ArgumentNullException(nameof(controlProperty));
            }

            var vmExpression      = Reflection.Rewrite(vmProperty.Body);
            var controlExpression = Reflection.Rewrite(controlProperty.Body);
            var source            = Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Cast <TProp>();

            IDisposable bindingDisposable = BindCommandInternal(source, view, controlExpression, Observable.Defer(() => Observable.Return(withParameter())), toEvent ?? string.Empty, cmd =>
            {
                if (!(cmd is IReactiveCommand rc))
                {
                    return(new RelayCommand(cmd.CanExecute, _ => cmd.Execute(withParameter())));
                }

                return(ReactiveCommand.Create(() => ((ICommand)rc).Execute(null), rc.CanExecute));
            });
Esempio n. 6
0
        /// <inheritdoc />
        public IDisposable BindInteraction <TViewModel, TView, TInput, TOutput, TDontCare>(
            TViewModel?viewModel,
            TView view,
            Expression <Func <TViewModel, Interaction <TInput, TOutput> > > propertyName,
            Func <InteractionContext <TInput, TOutput>, IObservable <TDontCare> > handler)
            where TViewModel : class
            where TView : class, IViewFor
        {
            if (propertyName is null)
            {
                throw new ArgumentNullException(nameof(propertyName));
            }

            if (handler is null)
            {
                throw new ArgumentNullException(nameof(handler));
            }

            var vmExpression = Reflection.Rewrite(propertyName.Body);

            var source = Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Cast <Interaction <TInput, TOutput> >();

            var interactionDisposable = new SerialDisposable();

            return(source
                   .Where(x => x is not null)
                   .Do(x => interactionDisposable.Disposable = x.RegisterHandler(handler))
                   .Finally(() => interactionDisposable.Dispose())
                   .Subscribe(_ => { }, ex => this.Log().Error(ex, $"{vmExpression} Interaction Binding received an Exception!")));
        }
        /// <summary>
        /// Bind a command from the ViewModel to an explicitly specified control
        /// on the View.
        /// </summary>
        /// <typeparam name="TView">The view type.</typeparam>
        /// <typeparam name="TViewModel">The view model type.</typeparam>
        /// <typeparam name="TProp">The property type.</typeparam>
        /// <typeparam name="TControl">The control type.</typeparam>
        /// <typeparam name="TParam">The parameter type.</typeparam>
        /// <returns>A class representing the binding. Dispose it to disconnect
        /// the binding.</returns>
        /// <param name="viewModel">The View model.</param>
        /// <param name="view">The View.</param>
        /// <param name="vmProperty">The ViewModel command to bind.</param>
        /// <param name="controlProperty">The name of the control on the view.</param>
        /// <param name="withParameter">The ViewModel property to pass as the
        /// param of the ICommand.</param>
        /// <param name="toEvent">If specified, bind to the specific event
        /// instead of the default.
        /// NOTE: If this parameter is used inside WhenActivated, it's
        /// important to dispose the binding when the view is deactivated.</param>
        public IReactiveBinding <TView, TViewModel, TProp> BindCommand <TView, TViewModel, TProp, TControl, TParam>(
            TViewModel viewModel,
            TView view,
            Expression <Func <TViewModel, TProp> > vmProperty,
            Expression <Func <TView, TControl> > controlProperty,
            IObservable <TParam> withParameter,
            string toEvent = null)
            where TViewModel : class
            where TView : class, IViewFor <TViewModel>
            where TProp : ICommand
        {
            var vmExpression      = Reflection.Rewrite(vmProperty.Body);
            var controlExpression = Reflection.Rewrite(controlProperty.Body);
            var source            = Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Cast <TProp>();

            IDisposable bindingDisposable = BindCommandInternal(source, view, controlExpression, withParameter, toEvent);

            return(new ReactiveBinding <TView, TViewModel, TProp>(
                       view,
                       viewModel,
                       controlExpression,
                       vmExpression,
                       source,
                       BindingDirection.OneWay,
                       bindingDisposable));
        }
        /// <summary>
        /// Bind a command from the ViewModel to an explicitly specified control
        /// on the View.
        /// </summary>
        /// <typeparam name="TView">The view type.</typeparam>
        /// <typeparam name="TViewModel">The view model type.</typeparam>
        /// <typeparam name="TProp">The property type.</typeparam>
        /// <typeparam name="TControl">The control type.</typeparam>
        /// <typeparam name="TParam">The parameter type.</typeparam>
        /// <param name="viewModel">The View model.</param>
        /// <param name="view">The View.</param>
        /// <param name="vmProperty">The ViewModel command to bind.</param>
        /// <param name="controlProperty">The name of the control on the view.</param>
        /// <param name="withParameter">The ViewModel property to pass as the
        /// param of the ICommand.</param>
        /// <param name="toEvent">If specified, bind to the specific event
        /// instead of the default.
        /// NOTE: If this parameter is used inside WhenActivated, it's
        /// important to dispose the binding when the view is deactivated.</param>
        /// <returns>A class representing the binding. Dispose it to disconnect
        /// the binding.</returns>
        public IReactiveBinding <TView, TViewModel, TProp> BindCommand <TView, TViewModel, TProp, TControl, TParam>(
            TViewModel viewModel,
            TView view,
            Expression <Func <TViewModel, TProp> > vmProperty,
            Expression <Func <TView, TControl> > controlProperty,
            Func <TParam> withParameter,
            string toEvent = null)
            where TViewModel : class
            where TView : class, IViewFor <TViewModel>
            where TProp : ICommand
        {
            var vmExpression      = Reflection.Rewrite(vmProperty.Body);
            var controlExpression = Reflection.Rewrite(controlProperty.Body);
            var source            = Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Cast <TProp>();

            IDisposable bindingDisposable = BindCommandInternal(source, view, controlExpression, Observable.Defer(() => Observable.Return(withParameter())), toEvent, cmd =>
            {
                var rc = cmd as IReactiveCommand;
                if (rc == null)
                {
                    return(new RelayCommand(cmd.CanExecute, _ => cmd.Execute(withParameter())));
                }

                return(ReactiveCommand.Create(() => ((ICommand)rc).Execute(null), rc.CanExecute));
            });

            return(new ReactiveBinding <TView, TViewModel, TProp>(
                       view,
                       viewModel,
                       controlExpression,
                       vmExpression,
                       source,
                       BindingDirection.OneWay,
                       bindingDisposable));
        }
        /// <summary>
        /// Converts to observable.
        /// </summary>
        /// <typeparam name="TSource">The type of the view model.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="expression">The expression.</param>
        /// <param name="source">The view model.</param>
        /// <param name="beforeChange">if set to <c>true</c> [before change].</param>
        /// <param name="skipInitial">if set to <c>true</c> [skip initial].</param>
        /// <returns>
        /// An observable Result.
        /// </returns>
        public static IObservable <TResult?> ToObservable <TSource, TResult>(
            this Expression <Func <TSource, TResult?> > expression,
            TSource?source,
            bool beforeChange = false,
            bool skipInitial  = false) // TODO: Create Test
        {
            var sParam = Reflection.Rewrite(expression.Body);

            return(source.SubscribeToExpressionChain <TSource, TResult?>(sParam, beforeChange, skipInitial, RxApp.SuppressViewCommandBindingMessage)
                   .Select(x => x.GetValue())
                   .Retry());
        }
Esempio n. 10
0
        public static IReactiveBinding <TView, TViewModel, TProp> BindCommand <TView, TViewModel, TProp, TControl, TParam>(
            this ICommandBinderImplementation This,
            TViewModel viewModel,
            TView view,
            Expression <Func <TViewModel, TProp> > propertyName,
            Expression <Func <TView, TControl> > controlName,
            Expression <Func <TViewModel, TParam> > withParameter,
            string toEvent = null)
            where TViewModel : class
            where TView : class, IViewFor <TViewModel>
            where TProp : ICommand
        {
            var paramExpression = Reflection.Rewrite(withParameter.Body);
            var param           = Reflection.ViewModelWhenAnyValue(viewModel, view, paramExpression);

            return(This.BindCommand(viewModel, view, propertyName, controlName, param, toEvent));
        }
Esempio n. 11
0
        private static ObservableAsPropertyHelper <TRet> ObservableToProperty <TObj, TRet>(
            this TObj target,
            IObservable <TRet> observable,
            Expression <Func <TObj, TRet> > property,
            TRet initialValue      = default(TRet),
            bool deferSubscription = false,
            IScheduler?scheduler   = null)
            where TObj : class, IReactiveObject
        {
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            if (observable == null)
            {
                throw new ArgumentNullException(nameof(observable));
            }

            if (property == null)
            {
                throw new ArgumentNullException(nameof(property));
            }

            Expression expression = Reflection.Rewrite(property.Body);

            if (expression.GetParent().NodeType != ExpressionType.Parameter)
            {
                throw new ArgumentException("Property expression must be of the form 'x => x.SomeProperty'");
            }

            var name = expression.GetMemberInfo().Name;

            if (expression is IndexExpression)
            {
                name += "[]";
            }

            return(new ObservableAsPropertyHelper <TRet>(
                       observable,
                       _ => target.RaisingPropertyChanged(name),
                       _ => target.RaisingPropertyChanging(name),
                       initialValue,
                       deferSubscription,
                       scheduler));
        }
Esempio n. 12
0
        /// <summary>
        /// Creates a one-way binding, i.e. a binding that flows from the
        /// <paramref name="viewModel"/> to the <paramref name="view"/> only. This binding will
        /// attempt to convert the value of the view model property to the view property if they
        /// are not of the same type.
        /// </summary>
        /// <typeparam name="TViewModel">The type of the view model that is bound.</typeparam>
        /// <typeparam name="TView">The type of the view that is bound.</typeparam>
        /// <typeparam name="TVMProp">The type of the property bound on the view model.</typeparam>
        /// <typeparam name="TVProp">The type of the property bound on the view.</typeparam>
        /// <param name="viewModel">The instance of the view model to bind to.</param>
        /// <param name="view">The instance of the view to bind to.</param>
        /// <param name="vmProperty">
        /// An expression representing the property to be bound to on the view model.
        /// This can be a child property, for example <c>x =&gt; x.Foo.Bar.Baz</c> in which case
        /// the binding will attempt to subscribe recursively to updates in order to
        /// always get the last value of the property chain.
        /// </param>
        /// <param name="viewProperty">
        /// An expression representing the property to be bound to on the view.
        /// This can be a child property, for example <c>x =&gt; x.Foo.Bar.Baz</c> in which case
        /// the binding will attempt to subscribe recursively to updates in order to
        /// always set the correct property.
        ///
        /// If it is left null, the framework will attempt to automagically figure out
        /// the control and property that is to be bound, by looking for a control of the
        /// same name as the <paramref name="vmProperty"/>, and its most natural property.
        /// </param>
        /// <param name="conversionHint">
        /// An object that can provide a hint for the converter.
        /// The semantics of this object is defined by the converter used.
        /// </param>
        /// <param name="vmToViewConverterOverride">
        /// Delegate to convert the value of the view model's property's type to a value of the
        /// view's property's type.
        /// </param>
        /// <returns>
        /// An instance of <see cref="IDisposable"/> that, when disposed,
        /// disconnects the binding.
        /// </returns>
        /// <exception cref="ArgumentException">
        /// There is no registered converter from <typeparamref name="TVMProp"/> to <typeparamref name="TVProp"/>.
        /// </exception>
        public IReactiveBinding <TView, TViewModel, TVProp> OneWayBind <TViewModel, TView, TVMProp, TVProp>(
            TViewModel viewModel,
            TView view,
            Expression <Func <TViewModel, TVMProp> > vmProperty,
            Expression <Func <TView, TVProp> > viewProperty,
            object conversionHint = null,
            IBindingTypeConverter vmToViewConverterOverride = null)
            where TViewModel : class
            where TView : class, IViewFor
        {
            var vmExpression   = Reflection.Rewrite(vmProperty.Body);
            var viewExpression = Reflection.Rewrite(viewProperty.Body);
            var viewType       = viewExpression.Type;
            var converter      = vmToViewConverterOverride ?? GetConverterForTypes(typeof(TVMProp), viewType);

            if (converter == null)
            {
                throw new ArgumentException($"Can't convert {typeof(TVMProp)} to {viewType}. To fix this, register a IBindingTypeConverter");
            }

            var ret = EvalBindingHooks(viewModel, view, vmExpression, viewExpression, BindingDirection.OneWay);

            if (!ret)
            {
                return(null);
            }

            var source = Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression)
                         .SelectMany(x =>
            {
                object tmp;
                if (!converter.TryConvert(x, viewType, conversionHint, out tmp))
                {
                    return(Observable <TVProp> .Empty);
                }

                return(Observable.Return(tmp == null ? default(TVProp) : (TVProp)tmp));
            });

            IDisposable disp = BindToDirect(source, view, viewExpression);

            return(new ReactiveBinding <TView, TViewModel, TVProp>(view, viewModel, viewExpression, vmExpression, source, BindingDirection.OneWay, disp));
        }
Esempio n. 13
0
        /// <summary>
        /// BindTo takes an Observable stream and applies it to a target
        /// property. Conceptually it is similar to <c>Subscribe(x =&gt;
        /// target.property = x)</c>, but allows you to use child properties
        /// without the null checks.
        /// </summary>
        /// <typeparam name="TValue">The source type.</typeparam>
        /// <typeparam name="TTarget">The target object type.</typeparam>
        /// <typeparam name="TTValue">The type of the property on the target object.</typeparam>
        /// <param name="observedChange">The observable to apply to the target property.</param>
        /// <param name="target">The target object whose property will be set.</param>
        /// <param name="propertyExpression">
        /// An expression representing the target property to set.
        /// This can be a child property (i.e. <c>x.Foo.Bar.Baz</c>).</param>
        /// <param name="conversionHint">
        /// An object that can provide a hint for the converter.
        /// The semantics of this object is defined by the converter used.
        /// </param>
        /// <param name="vmToViewConverterOverride">
        /// Delegate to convert the value of the view model's property's type to a value of the
        /// view's property's type.
        /// </param>
        /// <returns>An object that when disposed, disconnects the binding.</returns>
        public IDisposable BindTo <TValue, TTarget, TTValue>(
            IObservable <TValue> observedChange,
            TTarget target,
            Expression <Func <TTarget, TTValue> > propertyExpression,
            object conversionHint = null,
            IBindingTypeConverter vmToViewConverterOverride = null)
            where TTarget : class
        {
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            var viewExpression = Reflection.Rewrite(propertyExpression.Body);

            var ret = EvalBindingHooks(observedChange, target, null, viewExpression, BindingDirection.OneWay);

            if (!ret)
            {
                return(Disposable.Empty);
            }

            var converter = vmToViewConverterOverride ?? GetConverterForTypes(typeof(TValue), typeof(TTValue));

            if (converter == null)
            {
                throw new ArgumentException($"Can't convert {typeof(TValue)} to {typeof(TTValue)}. To fix this, register a IBindingTypeConverter");
            }

            var source = observedChange.SelectMany(x =>
            {
                object tmp;
                if (!converter.TryConvert(x, typeof(TTValue), conversionHint, out tmp))
                {
                    return(Observable <TTValue> .Empty);
                }

                return(Observable.Return(tmp == null ? default(TTValue) : (TTValue)tmp));
            });

            return(BindToDirect(source, target, viewExpression));
        }
Esempio n. 14
0
        public IReactiveBinding <TView, TViewModel, TProp> BindCommand <TView, TViewModel, TProp, TControl, TParam>(
            TViewModel viewModel,
            TView view,
            Expression <Func <TViewModel, TProp> > vmProperty,
            Expression <Func <TView, TControl> > controlProperty,
            Func <TParam> withParameter,
            string toEvent = null)
            where TViewModel : class
            where TView : class, IViewFor <TViewModel>
            where TProp : ICommand
        {
            var vmExpression      = Reflection.Rewrite(vmProperty.Body);
            var controlExpression = default(Expression);

            if (controlProperty == null)
            {
                controlExpression = Reflection.getViewExpression(view, vmExpression);
            }
            else
            {
                controlExpression = Reflection.Rewrite(controlProperty.Body);
            }

            var source = Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Cast <TProp>();

            IDisposable bindingDisposable = bindCommandInternal(source, view, controlExpression, Observable.Empty <object>(), toEvent, cmd => {
                var rc = cmd as IReactiveCommand;
                if (rc == null)
                {
                    return(new RelayCommand(cmd.CanExecute, _ => cmd.Execute(withParameter())));
                }

                var ret = ReactiveCommand.Create(rc.CanExecuteObservable);
                ret.Subscribe(_ => rc.Execute(withParameter()));
                return(ret);
            });

            return(new ReactiveBinding <TView, TViewModel, TProp>(view, viewModel, controlExpression, vmExpression,
                                                                  source, BindingDirection.OneWay, bindingDisposable));
        }
Esempio n. 15
0
        /// <summary>
        /// Creates a one way binding with a selector, i.e. a binding that flows from the
        /// <paramref name="viewModel"/> to the <paramref name="view"/> only, and where the value of the view model
        /// property is mapped through the <paramref name="selector"/> before being set to the view.
        /// </summary>
        /// <typeparam name="TViewModel">The type of the view model that is bound.</typeparam>
        /// <typeparam name="TView">The type of the view that is bound.</typeparam>
        /// <typeparam name="TProp">The type of the property bound on the view model.</typeparam>
        /// <typeparam name="TOut">The return type of the <paramref name="selector"/>.</typeparam>
        /// <param name="viewModel">The instance of the view model to bind to.</param>
        /// <param name="view">The instance of the view to bind to.</param>
        /// <param name="vmProperty">
        /// An expression representing the property to be bound to on the view model.
        /// This can be a child property, for example <c>x =&gt; x.Foo.Bar.Baz</c> in which case
        /// the binding will attempt to subscribe recursively to updates in order to
        /// always get the last value of the property chain.
        /// </param>
        /// <param name="viewProperty">
        /// An expression representing the property to be bound to on the view.
        /// This can be a child property, for example <c>x =&gt; x.Foo.Bar.Baz</c> in which case
        /// the binding will attempt to subscribe recursively to updates in order to
        /// always set the correct property.
        ///
        /// If it is left null, the framework will attempt to automagically figure out
        /// the control and property that is to be bound, by looking for a control of the
        /// same name as the <paramref name="vmProperty"/>, and its most natural property.
        /// </param>
        /// <param name="selector">
        /// A function that will be used to transform the values of the property on the view model
        /// before being bound to the view property.
        /// </param>
        /// <returns>
        /// An instance of <see cref="IDisposable"/> that, when disposed,
        /// disconnects the binding.
        /// </returns>
        public IReactiveBinding <TView, TViewModel, TOut> OneWayBind <TViewModel, TView, TProp, TOut>(
            TViewModel viewModel,
            TView view,
            Expression <Func <TViewModel, TProp> > vmProperty,
            Expression <Func <TView, TOut> > viewProperty,
            Func <TProp, TOut> selector)
            where TViewModel : class
            where TView : class, IViewFor
        {
            var vmExpression   = Reflection.Rewrite(vmProperty.Body);
            var viewExpression = Reflection.Rewrite(viewProperty.Body);
            var ret            = EvalBindingHooks(viewModel, view, vmExpression, viewExpression, BindingDirection.OneWay);

            if (!ret)
            {
                return(null);
            }

            var source = Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Select(x => (TProp)x).Select(selector);

            IDisposable disp = BindToDirect(source, view, viewExpression);

            return(new ReactiveBinding <TView, TViewModel, TOut>(view, viewModel, viewExpression, vmExpression, source, BindingDirection.OneWay, disp));
        }
Esempio n. 16
0
        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));
        }