/// <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="deferSubscription">
        /// A value indicating whether the <see cref="ObservableAsPropertyHelper{T}"/>
        /// should defer the subscription to the <paramref name="observable"/> source
        /// until the first call to <see cref="Value"/>, or if it should immediately
        /// subscribe to the the <paramref name="observable"/> source.
        /// </param>
        /// <param name="scheduler">The scheduler that the notifications will be
        /// provided on - this should normally be a Dispatcher-based scheduler
        /// </param>
        public ObservableAsPropertyHelper(
            IObservable <T> observable,
            Action <T> onChanged,
            Action <T> onChanging  = null,
            T initialValue         = default(T),
            bool deferSubscription = false,
            IScheduler scheduler   = null)
        {
            Contract.Requires(observable != null);
            Contract.Requires(onChanged != null);

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

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

            subj.Subscribe(x => {
                onChanging(x);
                _lastValue = x;
                onChanged(x);
            }, exSubject.OnNext);

            ThrownExceptions = exSubject;

            _lastValue = initialValue;
            _source    = observable.StartWith(initialValue).DistinctUntilChanged().Multicast(subj);
            if (!deferSubscription)
            {
                _inner     = _source.Connect();
                _activated = 1;
            }
        }
        /// <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;
        }
        /// <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();
            }
        }
Exemple #4
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;
        }
        void setupRx(IEnumerable <T> List = null)
        {
            _BeforeItemsAdded   = new ScheduledSubject <T>(RxApp.DeferredScheduler);
            _BeforeItemsRemoved = new ScheduledSubject <T>(RxApp.DeferredScheduler);
            aboutToClear        = new Subject <int>();
            cleared             = new Subject <int>();

            if (List != null)
            {
                foreach (var v in List)
                {
                    this.Add(v);
                }
            }

            var ocChangedEvent = new Subject <NotifyCollectionChangedEventArgs>();

            CollectionChanged += (o, e) => ocChangedEvent.OnNext(e);

            _ItemsAdded = ocChangedEvent
                          .Where(x =>
                                 x.Action == NotifyCollectionChangedAction.Add ||
                                 x.Action == NotifyCollectionChangedAction.Replace)
                          .SelectMany(x =>
                                      (x.NewItems != null ? x.NewItems.OfType <T>() : Enumerable.Empty <T>())
                                      .ToObservable())
                          .Multicast(new ScheduledSubject <T>(RxApp.DeferredScheduler))
                          .PermaRef();

            _ItemsRemoved = ocChangedEvent
                            .Where(x =>
                                   x.Action == NotifyCollectionChangedAction.Remove ||
                                   x.Action == NotifyCollectionChangedAction.Replace)
                            .SelectMany(x =>
                                        (x.OldItems != null ? x.OldItems.OfType <T>() : Enumerable.Empty <T>())
                                        .ToObservable())
                            .Multicast(new ScheduledSubject <T>(RxApp.DeferredScheduler))
                            .PermaRef();

            _CollectionCountChanging = Observable.Merge(
                _BeforeItemsAdded.Select(_ => this.Count),
                _BeforeItemsRemoved.Select(_ => this.Count),
                aboutToClear
                );

            _CollectionCountChanged = Observable.Merge(
                _ItemsAdded.Select(_ => this.Count),
                _ItemsRemoved.Select(_ => this.Count),
                cleared
                );

            _ItemChanging = new ScheduledSubject <IObservedChange <T, object> >(RxApp.DeferredScheduler);
            _ItemChanged  = new ScheduledSubject <IObservedChange <T, object> >(RxApp.DeferredScheduler);

            // TODO: Fix up this selector nonsense once SL/WP7 gets Covariance
            _Changing = Observable.Merge(
                _BeforeItemsAdded.Select <T, IObservedChange <object, object> >(x =>
                                                                                new ObservedChange <object, object>()
            {
                PropertyName = "Items", Sender = this, Value = this
            }),
                _BeforeItemsRemoved.Select <T, IObservedChange <object, object> >(x =>
                                                                                  new ObservedChange <object, object>()
            {
                PropertyName = "Items", Sender = this, Value = this
            }),
                aboutToClear.Select <int, IObservedChange <object, object> >(x =>
                                                                             new ObservedChange <object, object>()
            {
                PropertyName = "Items", Sender = this, Value = this
            }),
                _ItemChanging.Select <IObservedChange <T, object>, IObservedChange <object, object> >(x =>
                                                                                                      new ObservedChange <object, object>()
            {
                PropertyName = x.PropertyName, Sender = x.Sender, Value = x.Value
            }));

            _Changed = Observable.Merge(
                _ItemsAdded.Select <T, IObservedChange <object, object> >(x =>
                                                                          new ObservedChange <object, object>()
            {
                PropertyName = "Items", Sender = this, Value = this
            }),
                _ItemsRemoved.Select <T, IObservedChange <object, object> >(x =>
                                                                            new ObservedChange <object, object>()
            {
                PropertyName = "Items", Sender = this, Value = this
            }),
                _ItemChanged.Select <IObservedChange <T, object>, IObservedChange <object, object> >(x =>
                                                                                                     new ObservedChange <object, object>()
            {
                PropertyName = x.PropertyName, Sender = x.Sender, Value = x.Value
            }));

            _ItemsAdded.Subscribe(x => {
                this.Log().Debug("Item Added to {0:X} - {1}", this.GetHashCode(), x);
                if (propertyChangeWatchers == null)
                {
                    return;
                }
                addItemToPropertyTracking(x);
            });

            _ItemsRemoved.Subscribe(x => {
                this.Log().Debug("Item removed from {0:X} - {1}", this.GetHashCode(), x);
                if (propertyChangeWatchers == null || !propertyChangeWatchers.ContainsKey(x))
                {
                    return;
                }

                removeItemFromPropertyTracking(x);
            });

            IsEmpty = CollectionCountChanged.Select(x => x == 0);

#if DEBUG
            _ItemChanged.Subscribe(x =>
                                   this.Log().Debug("Object {0} changed in collection {1:X}", x, this.GetHashCode()));
#endif
        }