void sectionCollectionChanged(int section, IList <NotifyCollectionChangedEventArgs> xs) { if (xs.Count == 0) { return; } var resetOnlyNotification = new [] { new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset) }; this.Log().Info("Changed contents: [{0}]", String.Join(",", xs.Select(x => x.Action.ToString()))); if (xs.Any(x => x.Action == NotifyCollectionChangedAction.Reset)) { this.Log().Info("About to call ReloadData"); adapter.ReloadData(); didPerformUpdates.OnNext(resetOnlyNotification); return; } var updates = xs.Select(ea => Tuple.Create(ea, getChangedIndexes(ea))).ToList(); var allChangedIndexes = updates.SelectMany(u => u.Item2).ToList(); // Detect if we're changing the same cell more than // once - if so, issue a reset and be done if (allChangedIndexes.Count != allChangedIndexes.Distinct().Count()) { this.Log().Info("Detected a dupe in the changelist. Issuing Reset"); adapter.ReloadData(); didPerformUpdates.OnNext(resetOnlyNotification); return; } this.Log().Info("Beginning update"); adapter.PerformBatchUpdates(() => { foreach (var update in updates.AsEnumerable().Reverse()) { var changeAction = update.Item1.Action; var changedIndexes = update.Item2; switch (changeAction) { case NotifyCollectionChangedAction.Add: doUpdate(adapter.InsertItems, changedIndexes, section); break; case NotifyCollectionChangedAction.Remove: doUpdate(adapter.DeleteItems, changedIndexes, section); break; case NotifyCollectionChangedAction.Replace: doUpdate(adapter.ReloadItems, changedIndexes, section); break; case NotifyCollectionChangedAction.Move: // NB: ReactiveList currently only supports single-item // moves var ea = update.Item1; this.Log().Info("Calling MoveRow: {0}-{1} => {0}{2}", section, ea.OldStartingIndex, ea.NewStartingIndex); adapter.MoveItem( NSIndexPath.FromRowSection(ea.OldStartingIndex, section), NSIndexPath.FromRowSection(ea.NewStartingIndex, section)); break; default: this.Log().Info("Unknown Action: {0}", changeAction); break; } } this.Log().Info("Ending update"); didPerformUpdates.OnNext(xs); }); }
private void ApplyPendingChanges() { try { List <NotifyCollectionChangedEventArgs> allEventArgs = new List <NotifyCollectionChangedEventArgs>(); this.Log().Debug("Beginning update"); adapter.PerformBatchUpdates(() => { if (this.Log().Level >= LogLevel.Debug) { this.Log().Debug("The pending changes (in order received) are:"); foreach (var pendingChange in pendingChanges) { this.Log().Debug( "Section {0}: Action={1}, OldStartingIndex={2}, NewStartingIndex={3}, OldItems.Count={4}, NewItems.Count={5}", pendingChange.Item1, pendingChange.Item2.Action, pendingChange.Item2.OldStartingIndex, pendingChange.Item2.NewStartingIndex, pendingChange.Item2.OldItems == null ? "null" : pendingChange.Item2.OldItems.Count.ToString(), pendingChange.Item2.NewItems == null ? "null" : pendingChange.Item2.NewItems.Count.ToString()); } } foreach (var sectionedUpdates in pendingChanges.GroupBy(x => x.Item1)) { var section = sectionedUpdates.First().Item1; this.Log().Debug("Processing updates for section {0}", section); var allSectionEas = sectionedUpdates .Select(x => x.Item2) .ToList(); if (allSectionEas.Any(x => x.Action == NotifyCollectionChangedAction.Reset)) { this.Log().Debug("Section {0} included a reset notification, so reloading that section.", section); #if UNIFIED adapter.ReloadSections(new NSIndexSet((nuint)section)); #else adapter.ReloadSections(new NSIndexSet((uint)section)); #endif continue; } var updates = allSectionEas .SelectMany(GetUpdatesForEvent) .ToList(); if (this.Log().Level >= LogLevel.Debug) { this.Log().Debug( "Updates for section {0}: {1}", section, updates .Aggregate( new StringBuilder(), (current, next) => { if (current.Length > 0) { current.Append(":"); } return(current.Append(next)); }, x => x.ToString())); } var normalizedUpdates = IndexNormalizer.Normalize(updates); if (this.Log().Level >= LogLevel.Debug) { this.Log().Debug( "Normalized updates for section {0}: {1}", section, normalizedUpdates .Aggregate( new StringBuilder(), (current, next) => { if (current.Length > 0) { current.Append(":"); } return(current.Append(next)); }, x => x.ToString())); } foreach (var normalizedUpdate in normalizedUpdates) { switch (normalizedUpdate.Type) { case UpdateType.Add: DoUpdate(adapter.InsertItems, new[] { normalizedUpdate.Index }, section); break; case UpdateType.Delete: DoUpdate(adapter.DeleteItems, new[] { normalizedUpdate.Index }, section); break; default: throw new NotSupportedException(); } } } }, () => { this.Log().Debug("Ending update"); didPerformUpdates.OnNext(allEventArgs); }); } finally { pendingChanges.Clear(); isCollectingChanges = false; } }
public CommonReactiveSource( IUICollViewAdapter <TUIView, TUIViewCell> adapter, IEnumerable <ISectionInformation <TUIView, TUIViewCell> > sectionInfo) { this.adapter = adapter; this.sectionInfo = sectionInfo.ToList(); for (int i = 0; i < this.sectionInfo.Count; i++) { var current = this.sectionInfo[i].Collection; var section = i; var disp = current.Changed.Buffer(TimeSpan.FromMilliseconds(250), RxApp.MainThreadScheduler).Subscribe(xs => { if (xs.Count == 0) { return; } var resetOnlyNotification = new [] { new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset) }; this.Log().Info("Changed contents: [{0}]", String.Join(",", xs.Select(x => x.Action.ToString()))); if (xs.Any(x => x.Action == NotifyCollectionChangedAction.Reset)) { this.Log().Info("About to call ReloadData"); adapter.ReloadData(); didPerformUpdates.OnNext(resetOnlyNotification); return; } var updates = xs.Select(ea => Tuple.Create(ea, getChangedIndexes(ea))).ToList(); var allChangedIndexes = updates.SelectMany(u => u.Item2).ToList(); // Detect if we're changing the same cell more than // once - if so, issue a reset and be done if (allChangedIndexes.Count != allChangedIndexes.Distinct().Count()) { this.Log().Info("Detected a dupe in the changelist. Issuing Reset"); adapter.ReloadData(); didPerformUpdates.OnNext(resetOnlyNotification); return; } this.Log().Info("Beginning update"); adapter.PerformBatchUpdates(() => { foreach (var update in updates.AsEnumerable().Reverse()) { var changeAction = update.Item1.Action; var changedIndexes = update.Item2; switch (changeAction) { case NotifyCollectionChangedAction.Add: doUpdate(adapter.InsertItems, changedIndexes, section); break; case NotifyCollectionChangedAction.Remove: doUpdate(adapter.DeleteItems, changedIndexes, section); break; case NotifyCollectionChangedAction.Replace: doUpdate(adapter.ReloadItems, changedIndexes, section); break; case NotifyCollectionChangedAction.Move: // NB: ReactiveList currently only supports single-item // moves var ea = update.Item1; this.Log().Info("Calling MoveRow: {0}-{1} => {0}{2}", section, ea.OldStartingIndex, ea.NewStartingIndex); adapter.MoveItem( NSIndexPath.FromRowSection(ea.OldStartingIndex, section), NSIndexPath.FromRowSection(ea.NewStartingIndex, section)); break; default: this.Log().Info("Unknown Action: {0}", changeAction); break; } } this.Log().Info("Ending update"); didPerformUpdates.OnNext(xs); }); }); innerDisp.Add(disp); } }