private static IDisposable CreateSubscription<TModel, TViewModel>( INotifyCollectionChanged source, Func<TModel, TViewModel> converter, DispatcherCollectionRx<TViewModel> target) { return source.ListenCollectionChanged(e => DispatcherHelper.UIDispatcher.InvokeAsync(() => { if (e.NewItems != null && e.NewItems.Count >= 2) { throw new ArgumentException("Too many new items."); } try { switch (e.Action) { case NotifyCollectionChangedAction.Add: if (e.NewItems == null) { throw new ArgumentException("New item is null."); } target.Insert(e.NewStartingIndex, converter((TModel)e.NewItems[0])); break; case NotifyCollectionChangedAction.Move: target.Move(e.OldStartingIndex, e.NewStartingIndex); break; case NotifyCollectionChangedAction.Remove: if (typeof(IDisposable).IsAssignableFrom(typeof(TViewModel))) { ((IDisposable)target[e.OldStartingIndex]).Dispose(); } target.RemoveAt(e.OldStartingIndex); break; case NotifyCollectionChangedAction.Replace: if (typeof(IDisposable).IsAssignableFrom(typeof(TViewModel))) { ((IDisposable)target[e.NewStartingIndex]).Dispose(); } if (e.NewItems == null) { throw new ArgumentException("New item is null."); } target[e.NewStartingIndex] = converter((TModel)e.NewItems[0]); break; case NotifyCollectionChangedAction.Reset: if (typeof(IDisposable).IsAssignableFrom(typeof(TViewModel))) { // ReSharper disable once PossibleInvalidCastExceptionInForeachLoop foreach (IDisposable item in target) { item.Dispose(); } } target.Clear(); break; default: throw new ArgumentException(); } } catch (ArgumentOutOfRangeException aoex) { // collection inconsistent state throw new InvalidOperationException( "Collection state is invalid." + Environment.NewLine + "INDEX OUT OF RANGE - " + e.Action + "[" + typeof(TModel).Name + " -> " + typeof(TViewModel).Name + "]" + Environment.NewLine + "new start: " + e.NewStartingIndex + ", count: " + (e.NewItems == null ? "null" : e.NewItems.Count.ToString(CultureInfo.InvariantCulture)) + Environment.NewLine + "source length: " + ((IList<TModel>)source).Count + ", target length: " + target.Count + ".", aoex); } })); }
private static IDisposable CreateSubscription <TModel, TViewModel>( INotifyCollectionChanged source, Func <TModel, TViewModel> converter, DispatcherCollectionRx <TViewModel> target) { bool resetOccured = false; return(source .ListenCollectionChanged() .ObserveOn(DispatcherHolder.Dispatcher) .Subscribe(e => { if (e.NewItems != null && e.NewItems.Count >= 2) { throw new ArgumentException("Too many new items."); } try { switch (e.Action) { case NotifyCollectionChangedAction.Add: target.Insert(e.NewStartingIndex, converter((TModel)e.NewItems[0])); break; case NotifyCollectionChangedAction.Move: target.Move(e.OldStartingIndex, e.NewStartingIndex); break; case NotifyCollectionChangedAction.Remove: if (typeof(IDisposable).IsAssignableFrom(typeof(TViewModel))) { ((IDisposable)target[e.OldStartingIndex]).Dispose(); } target.RemoveAt(e.OldStartingIndex); break; case NotifyCollectionChangedAction.Replace: if (typeof(IDisposable).IsAssignableFrom(typeof(TViewModel))) { ((IDisposable)target[e.NewStartingIndex]).Dispose(); } target[e.NewStartingIndex] = converter((TModel)e.NewItems[0]); break; case NotifyCollectionChangedAction.Reset: if (typeof(IDisposable).IsAssignableFrom(typeof(TViewModel))) { foreach (IDisposable item in target) { item.Dispose(); } resetOccured = true; } target.Clear(); break; default: throw new ArgumentException(); } } catch (ArgumentOutOfRangeException aoex) { throw new InvalidOperationException( "Collection state is invalid." + Environment.NewLine + "INDEX OUT OF RANGE - " + e.Action + "[" + typeof(TModel).Name + " -> " + typeof(TViewModel).Name + ", reset: " + resetOccured + " ]" + Environment.NewLine + "new start: " + e.NewStartingIndex + ", count: " + (e.NewItems == null ? "null" : e.NewItems.Count.ToString()) + Environment.NewLine + "source length: " + ((IList <TModel>)source).Count + ", target length: " + target.Count + ".", aoex); } })); }