/// <summary> /// Prepares and sets up the observables and subjects used, particularly /// <see cref="ListChanges"/>, <see cref="INotifyObservableCountChanges.CountChanges"/> and <see cref="INotifyObserverExceptions.ObserverExceptions"/>. /// </summary> private void SetupObservablesAndObserversAndSubjects() { ListChangesObserver = _listChangesSubject.NotifyOn(Scheduler); // then connect to InnerList's ListChanged Event _innerListChangedRelevantListChangedEventsForwader = System.Reactive.Linq.Observable.FromEventPattern <ListChangedEventHandler, ListChangedEventArgs>( handler => InnerList.ListChanged += handler, handler => InnerList.ListChanged -= handler) .TakeWhile(_ => !IsDisposing && !IsDisposed) .SkipContinuouslyWhile(_ => !IsTrackingChanges) .Where(eventPattern => eventPattern?.EventArgs != null) .Select(eventPattern => eventPattern.EventArgs.ToObservableListChange(InnerList, this)) .ObserveOn(Scheduler) .Subscribe( NotifySubscribersAboutListChanges, exception => { // ToDo: at this point this instance is practically doomed / no longer forwarding any events & therefore further usage of the instance itself should be prevented, or the observable stream should re-connect/signal-and-swallow exceptions. Either way.. not ideal. var observerException = new ObserverException( $"An error occured notifying observers of this {this.GetType().Name} - consistency and future notifications are no longer guaranteed.", exception); ObserverExceptionsObserver.OnNext(observerException); }); }
/// <summary> /// Prepares and sets up the observables and subjects used, particularly /// <see cref="_collectionChangesSubject"/>, <see cref="_countChangesSubject"/> and <see cref="_observerExceptionsSubject"/> /// but also internally used RX subscriptions for <see cref="IBindingList.ListChanged"/> and somewhat hack-ish /// 'Count' and 'Items[]' <see cref="INotifyPropertyChanged"/> events on <see cref="CountChanges"/> and <see cref="CollectionChanges"/> /// occurrences (for WPF / Binding) /// </summary> private void SetupObservablesAndObserversAndSubjects() { ObserverExceptionsObserver = _observerExceptionsSubject.NotifyOn(Scheduler); CollectionChangesObserver = _collectionChangesSubject.NotifyOn(Scheduler); CountChangesObserver = _countChangesSubject.NotifyOn(Scheduler); // then connect to InnerList's ListChanged Event _innerListChangedRelevantCollectionChangedEventsForwader = System.Reactive.Linq.Observable.FromEventPattern <ListChangedEventHandler, ListChangedEventArgs>( handler => InnerList.ListChanged += handler, handler => InnerList.ListChanged -= handler) .TakeWhile(_ => !IsDisposing && !IsDisposed) .SkipContinuouslyWhile(_ => !IsTrackingChanges) .Where(eventPattern => eventPattern?.EventArgs != null) .SelectMany(eventPattern => eventPattern.EventArgs.ToObservableCollectionChanges(InnerList)) .ObserveOn(Scheduler) .Subscribe( NotifyObserversAboutCollectionChanges, exception => { // ToDo: at this point this instance is practically doomed / no longer forwarding any events & therefore further usage of the instance itself should be prevented, or the observable stream should re-connect/signal-and-swallow exceptions. Either way.. not ideal. var observerException = new ObserverException( $"An error occured notifying observers of this {this.GetType().Name} - consistency and future notifications are no longer guaranteed.", exception); ObserverExceptionsObserver.OnNext(observerException); }); // 'Count' and 'Item[]' PropertyChanged events are used by WPF typically via / for ObservableCollections, see // http://referencesource.microsoft.com/#System/compmod/system/collections/objectmodel/observablecollection.cs,421 _countChangesCountPropertyChangedForwarder = CountChanges .ObserveOn(Scheduler) .Subscribe(_ => RaisePropertyChanged(nameof(Count))); _collectionChangesItemIndexerPropertyChangedForwarder = CollectionChanges .ObserveOn(Scheduler) .Subscribe(_ => RaisePropertyChanged(ItemIndexerName)); }
/// <summary> /// Notifies all <see cref="CollectionChanges" /> and <see cref="Resets" /> subscribers and /// raises the (observable)collection changed events. /// </summary> /// <param name="observableCollectionChange">The observable collection change.</param> protected virtual void NotifyObserversAboutCollectionChanges(IObservableCollectionChange <T> observableCollectionChange) { if (observableCollectionChange == null) { throw new ArgumentNullException(nameof(observableCollectionChange)); } CheckForAndThrowIfDisposed(); // go ahead and check whether a Reset or item add, -change, -move or -remove shall be signaled // .. based on the ThresholdAmountWhenChangesAreNotifiedAsReset value var actualObservableCollectionChange = (observableCollectionChange.ChangeType == ObservableCollectionChangeType.Reset || IsItemsChangedAmountGreaterThanResetThreshold(1, ThresholdAmountWhenChangesAreNotifiedAsReset)) ? ObservableCollectionChange <T> .Reset : observableCollectionChange; // raise events and notify about collection changes if (actualObservableCollectionChange.ChangeType == ObservableCollectionChangeType.ItemAdded || actualObservableCollectionChange.ChangeType == ObservableCollectionChangeType.ItemRemoved || actualObservableCollectionChange.ChangeType == ObservableCollectionChangeType.Reset) { try { CountChangesObserver.OnNext(Count); } catch (Exception exception) { var observerException = new ObserverException( $"An error occured notifying {nameof(CountChanges)} Observers of this {this.GetType().Name}.", exception); ObserverExceptionsObserver.OnNext(observerException); if (observerException.Handled == false) { throw; } } } try { CollectionChangesObserver.OnNext(actualObservableCollectionChange); } catch (Exception exception) { var observerException = new ObserverException( $"An error occured notifying {nameof(CollectionChanges)} Observers of this {this.GetType().Name}.", exception); ObserverExceptionsObserver.OnNext(observerException); if (observerException.Handled == false) { throw; } } try { RaiseCollectionChanged(actualObservableCollectionChange.ToNotifyCollectionChangedEventArgs()); } catch (Exception exception) { var observerException = new ObserverException( $"An error occured notifying CollectionChanged Subscribers of this {this.GetType().Name}.", exception); ObserverExceptionsObserver.OnNext(observerException); if (observerException.Handled == false) { throw; } } }
/// <summary> /// Notifies all <see cref="ListChanges" /> and <see cref="INotifyObservableResets.Resets" /> subscribers and /// raises the (observable)collection changed events. /// </summary> /// <param name="observableListChange">The observable list change.</param> protected virtual void NotifySubscribersAboutListChanges(IObservableListChange <T> observableListChange) { // This is similar to what ObservableCollection implements via its NotifyObserversAboutCollectionChanges method, // however: // - no need to handle count-relevant changes because the underlying ObservableCollection takes care of this // - no (extra) (Raise)CollectionChanged call here, again.. already done by the ObservableCollection // - however as 'Move's are only possible for / with ObservableLists, we also raise a PropertyChangedEvent for 'Item[]' (for wpf) in case of a item move(s) if (observableListChange == null) { throw new ArgumentNullException(nameof(observableListChange)); } CheckForAndThrowIfDisposed(); // go ahead and check whether a Reset or item add, -change, -move or -remove shall be signaled // .. based on the ThresholdAmountWhenChangesAreNotifiedAsReset value var actualObservableListChange = (observableListChange.ChangeType == ObservableListChangeType.Reset || IsItemsChangedAmountGreaterThanResetThreshold(1, ThresholdAmountWhenChangesAreNotifiedAsReset)) ? ObservableListChange <T> .Reset(this) : observableListChange; // raise events and notify about list changes try { ListChangesObserver.OnNext(actualObservableListChange); } catch (Exception exception) { var observerException = new ObserverException( $"An error occured notifying {nameof(ListChanges)} observers of this {this.GetType().Name}.", exception); ObserverExceptionsObserver.OnNext(observerException); if (observerException.Handled == false) { throw; } } if (actualObservableListChange.ChangeType == ObservableListChangeType.ItemMoved) { try { RaisePropertyChanged(ItemIndexerName); } catch (Exception exception) { var observerException = new ObserverException( $"An error occured notifying {nameof(PropertyChanged)} subscribers of this {this.GetType().Name}.", exception); ObserverExceptionsObserver.OnNext(observerException); if (observerException.Handled == false) { throw; } } } }