/// <summary>
        /// Constructs an ObservableAsPropertyHelper object.
        /// </summary>
        /// <param name="observable">The Observable to base the property on.</param>
        /// <param name="onChanged">The action to take when the property
        /// changes, typically this will call the ViewModel's
        /// RaisePropertyChanged method.</param>
        /// <param name="initialValue">The initial value of the property.</param>
        /// <param name="scheduler">The scheduler that the notifications will be
        /// provided on - this should normally be a Dispatcher-based scheduler
        /// (and is by default)</param>
        public ObservableAsPropertyHelper(
            IObservable <T> observable,
            Action <T> onChanged,
            T initialValue       = default(T),
            IScheduler scheduler = null)
        {
            Contract.Requires(observable != null);
            Contract.Requires(onChanged != null);

            scheduler  = scheduler ?? RxApp.DeferredScheduler;
            _lastValue = initialValue;

            var subj      = new ScheduledSubject <T>(scheduler);
            var exSubject = new ScheduledSubject <Exception>(scheduler, RxApp.DefaultExceptionHandler);

            subj.Subscribe(x => {
                this.Log().Debug("Property helper {0:X} changed", this.GetHashCode());
                _lastValue = x;
                onChanged(x);
            }, exSubject.OnNext);

            ThrownExceptions = exSubject;

            // Fire off an initial RaisePropertyChanged to make sure bindings
            // have a value
            subj.OnNext(initialValue);

            var src = observable.DistinctUntilChanged().Multicast(subj);

            _inner  = src.Connect();
            _source = src;
        }
예제 #2
0
 internal void notifyObservable <T>(T item, Subject <T> subject)
 {
     try {
         subject.OnNext(item);
     } catch (Exception ex) {
         this.Log().ErrorException("ReactiveObject Subscriber threw exception", ex);
         thrownExceptions.OnNext(ex);
     }
 }
예제 #3
0
        public ReactiveCommand(IObservable <bool> canExecute, bool allowsConcurrentExecution, IScheduler scheduler, bool initialCondition = true)
        {
            canExecute                = canExecute ?? Observable.Return(true);
            defaultScheduler          = scheduler ?? RxApp.MainThreadScheduler;
            AllowsConcurrentExecution = allowsConcurrentExecution;

            canExecute = canExecute.Catch <bool, Exception>(ex => {
                exceptions.OnNext(ex);
                return(Observable.Empty <bool>());
            });

            ThrownExceptions = exceptions = new ScheduledSubject <Exception>(defaultScheduler, RxApp.DefaultExceptionHandler);

            var isExecuting = inflight
                              .Scan(0, (acc, x) => acc + (x ? 1 : -1))
                              .Select(x => x > 0)
                              .Publish(false)
                              .PermaRef()
                              .DistinctUntilChanged();

            IsExecuting = isExecuting.ObserveOn(defaultScheduler);

            var isBusy = allowsConcurrentExecution ? Observable.Return(false) : isExecuting;
            var canExecuteAndNotBusy = Observable.CombineLatest(canExecute, isBusy, (ce, b) => ce && !b);

            var canExecuteObs = canExecuteAndNotBusy
                                .Publish(initialCondition)
                                .RefCount();

            CanExecuteObservable = canExecuteObs
                                   .DistinctUntilChanged()
                                   .ObserveOn(defaultScheduler);

            innerDisp = canExecuteObs.Subscribe(x => {
                if (canExecuteLatest == x)
                {
                    return;
                }

                canExecuteLatest = x;
                defaultScheduler.Schedule(() => this.raiseCanExecuteChanged(EventArgs.Empty));
            }, exceptions.OnNext);
        }
        /// <summary>
        /// Constructs an ObservableAsPropertyHelper object.
        /// </summary>
        /// <param name="observable">The Observable to base the property on.</param>
        /// <param name="onChanged">The action to take when the property
        /// changes, typically this will call the ViewModel's
        /// RaisePropertyChanged method.</param>
        /// <param name="onChanging">The action to take when the property
        /// changes, typically this will call the ViewModel's
        /// RaisePropertyChanging method.</param>
        /// <param name="initialValue">The initial value of the property.</param>
        /// <param name="scheduler">The scheduler that the notifications will be
        /// provided on - this should normally be a Dispatcher-based scheduler
        /// (and is by default)</param>
        public ObservableAsPropertyHelper(
            IObservable <T> observable,
            Action <T> onChanged,
            Action <T> onChanging = null,
            T initialValue        = default(T),
            IScheduler scheduler  = null)
        {
            Contract.Requires(observable != null);
            Contract.Requires(onChanged != null);

            scheduler  = scheduler ?? CurrentThreadScheduler.Instance;
            onChanging = onChanging ?? (_ => {});
            _lastValue = initialValue;

            var subj      = new ScheduledSubject <T>(scheduler);
            var exSubject = new ScheduledSubject <Exception>(CurrentThreadScheduler.Instance, RxApp.DefaultExceptionHandler);

            bool firedInitial = false;

            subj.Subscribe(x => {
                // Suppress a non-change between initialValue and the first value
                // from a Subscribe
                if (firedInitial && EqualityComparer <T> .Default.Equals(x, _lastValue))
                {
                    return;
                }

                onChanging(x);
                _lastValue = x;
                onChanged(x);
                firedInitial = true;
            }, exSubject.OnNext);

            ThrownExceptions = exSubject;

            // Fire off an initial RaisePropertyChanged to make sure bindings
            // have a value
            subj.OnNext(initialValue);
            _source = observable.DistinctUntilChanged().Multicast(subj);

            if (ModeDetector.InUnitTestRunner())
            {
                _inner = _source.Connect();
            }
        }
예제 #5
0
        /// <summary>
        /// Constructs an ObservableAsPropertyHelper object.
        /// </summary>
        /// <param name="observable">The Observable to base the property on.</param>
        /// <param name="onChanged">The action to take when the property
        /// changes, typically this will call the ViewModel's
        /// RaisePropertyChanged method.</param>
        /// <param name="initialValue">The initial value of the property.</param>
        /// <param name="scheduler">The scheduler that the notifications will be
        /// provided on - this should normally be a Dispatcher-based scheduler
        /// (and is by default)</param>
        public ObservableAsPropertyHelper(
            IObservable <T> observable,
            Action <T> onChanged,
            T initialValue       = default(T),
            IScheduler scheduler = null)
        {
            Contract.Requires(observable != null);
            Contract.Requires(onChanged != null);

            scheduler  = scheduler ?? RxApp.DeferredScheduler;
            _lastValue = initialValue;

            var subj      = new ScheduledSubject <T>(scheduler);
            var exSubject = new ScheduledSubject <Exception>(scheduler, RxApp.DefaultExceptionHandler);

            bool firedInitial = false;

            subj.Subscribe(x => {
                // Suppress a non-change between initialValue and the first value
                // from a Subscribe
                if (firedInitial && EqualityComparer <T> .Default.Equals(x, _lastValue))
                {
                    return;
                }

                this.Log().Debug("Property helper {0:X} changed", this.GetHashCode());
                _lastValue = x;
                onChanged(x);
                firedInitial = true;
            }, exSubject.OnNext);

            ThrownExceptions = exSubject;

            // Fire off an initial RaisePropertyChanged to make sure bindings
            // have a value
            subj.OnNext(initialValue);

            var src = observable.DistinctUntilChanged().Multicast(subj);

            _inner  = src.Connect();
            _source = src;
        }
예제 #6
0
        /// <summary>
        /// Initializes a new instance of the <see cref="CombinedReactiveCommand{TParam, TResult}"/> class.
        /// </summary>
        /// <param name="childCommands">The child commands which will be executed.</param>
        /// <param name="canExecute">A observable when the command can be executed.</param>
        /// <param name="outputScheduler">The scheduler where to dispatch the output from the command.</param>
        /// <exception cref="ArgumentNullException">Fires when required arguments are null.</exception>
        /// <exception cref="ArgumentException">Fires if the child commands container is empty.</exception>
        protected internal CombinedReactiveCommand(
            IEnumerable <ReactiveCommandBase <TParam, TResult> > childCommands,
            IObservable <bool> canExecute,
            IScheduler outputScheduler)
        {
            if (childCommands is null)
            {
                throw new ArgumentNullException(nameof(childCommands));
            }

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

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

            var childCommandsArray = childCommands.ToArray();

            if (childCommandsArray.Length == 0)
            {
                throw new ArgumentException("No child commands provided.", nameof(childCommands));
            }

            _exceptions = new ScheduledSubject <Exception>(outputScheduler, RxApp.DefaultExceptionHandler);

            var canChildrenExecute = childCommandsArray.Select(x => x.CanExecute)
                                     .CombineLatest()
                                     .Select(x => x.All(y => y));
            var combinedCanExecute = canExecute
                                     .Catch <bool, Exception>(ex =>
            {
                _exceptions.OnNext(ex);
                return(Observables.False);
            })
                                     .StartWith(false)
                                     .CombineLatest(canChildrenExecute, (ce, cce) => ce && cce)
                                     .DistinctUntilChanged()
                                     .Replay(1)
                                     .RefCount();

            _exceptionsSubscription = childCommandsArray.Select(x => x.ThrownExceptions)
                                      .Merge()
                                      .Subscribe(ex => _exceptions.OnNext(ex));

            _innerCommand = new ReactiveCommand <TParam, IList <TResult> >(
                param =>
                childCommandsArray
                .Select(x => x.Execute(param))
                .CombineLatest(),
                combinedCanExecute,
                outputScheduler);

            // we already handle exceptions on individual child commands above, but the same exception
            // will tick through innerCommand. Therefore, we need to ensure we ignore it or the default
            // handler will execute and the process will be torn down
            _innerCommand
            .ThrownExceptions
            .Subscribe();

            CanExecute.Subscribe(OnCanExecuteChanged);
        }