private void Update(Chunk <NotifyCollectionChangedEventArgs> changes) { if (changes is null || changes.Count == 0) { return; } using (changes.ClearTransaction()) { if (changes.Count > 1) { this.Refresh(changes); return; } var e = changes[0]; switch (e.Action) { case NotifyCollectionChangedAction.Add: { if (!e.TryGetSingleNewItem <TSource>(out var newSource)) { goto case NotifyCollectionChangedAction.Reset; } var index = e.NewStartingIndex; var value = this.GetOrCreate(newSource, index); this.Tracker.Insert(index, value); var args = this.UpdateRange(index + 1, this.Count - 1); args.Add(Diff.CreateAddEventArgs(value, index)); this.Notify(args); break; } case NotifyCollectionChangedAction.Remove: { if (!e.TryGetSingleOldItem <TSource>(out var oldSource)) { goto case NotifyCollectionChangedAction.Reset; } var index = e.OldStartingIndex; var value = this.Tracker[index]; this.Tracker.RemoveAt(index); var argses = this.UpdateRange(index, this.Count - 1); argses.Add(Diff.CreateRemoveEventArgs(value, index)); this.Notify(argses); this.factory.Remove(oldSource, value); break; } case NotifyCollectionChangedAction.Replace: { if (!e.TryGetSingleNewItem <TSource>(out var newSource) || !e.TryGetSingleOldItem <TSource>(out var oldSource)) { goto case NotifyCollectionChangedAction.Reset; } var index = e.NewStartingIndex; var value = this.GetOrCreate(newSource, index); var oldValue = this.Tracker[e.OldStartingIndex]; this.Tracker[index] = value; var arg = Diff.CreateReplaceEventArgs(value, oldValue, index); this.Notify(arg); this.factory.Remove(oldSource, oldValue); break; } case NotifyCollectionChangedAction.Move: { var value = this.Tracker[e.OldStartingIndex]; this.Tracker.RemoveAt(e.OldStartingIndex); this.Tracker.Insert(e.NewStartingIndex, value); var args = this.UpdateRange(Math.Min(e.OldStartingIndex, e.NewStartingIndex), Math.Max(e.OldStartingIndex, e.NewStartingIndex)); args.Add(Diff.CreateMoveEventArgs(value, e.NewStartingIndex, e.OldStartingIndex)); this.Notify(args); break; } case NotifyCollectionChangedAction.Reset: using (this.factory.RefreshTransaction()) { this.Refresh(changes); } break; default: throw new ArgumentOutOfRangeException(nameof(changes)); } } }