void setupRx(IEnumerable <T> initialContents = null, IScheduler scheduler = null, double resetChangeThreshold = 0.3) { if (rxObjectsSetup) { return; } scheduler = scheduler ?? RxApp.DeferredScheduler; _inner = _inner ?? new List <T>(); _changing = new Subject <NotifyCollectionChangedEventArgs>(); _changing.Subscribe(raiseCollectionChanging); _changed = new Subject <NotifyCollectionChangedEventArgs>(); _changed.Subscribe(raiseCollectionChanged); ResetChangeThreshold = resetChangeThreshold; _beforeItemsAdded = new Lazy <Subject <T> >(() => new Subject <T>()); _itemsAdded = new Lazy <Subject <T> >(() => new Subject <T>()); _beforeItemsRemoved = new Lazy <Subject <T> >(() => new Subject <T>()); _itemsRemoved = new Lazy <Subject <T> >(() => new Subject <T>()); _itemChanging = new Lazy <Subject <IObservedChange <T, object> > >(() => new Subject <IObservedChange <T, object> >()); _itemChanged = new Lazy <Subject <IObservedChange <T, object> > >(() => new Subject <IObservedChange <T, object> >()); _beforeItemsMoved = new Lazy <Subject <IMoveInfo <T> > >(() => new Subject <IMoveInfo <T> >()); _itemsMoved = new Lazy <Subject <IMoveInfo <T> > >(() => new Subject <IMoveInfo <T> >()); // NB: We have to do this instead of initializing _inner so that // Collection<T>'s accounting is correct foreach (var item in initialContents ?? Enumerable.Empty <T>()) { Add(item); } // NB: ObservableCollection has a Secret Handshake with WPF where // they fire an INPC notification with the token "Item[]". Emulate // it here CollectionCountChanging.Select(x => new PropertyChangingEventArgs("Count")).Subscribe(this.raisePropertyChanging); CollectionCountChanged.Select(x => new PropertyChangedEventArgs("Count")).Subscribe(this.raisePropertyChanged); Changing.Select(x => new PropertyChangingEventArgs("Item[]")).Subscribe(this.raisePropertyChanging); Changed.Select(x => new PropertyChangedEventArgs("Item[]")).Subscribe(this.raisePropertyChanged); rxObjectsSetup = true; }
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 }