Skip to content

Instantly share code, notes, and snippets.

@rotorgames
Created December 16, 2020 18:38
Show Gist options
  • Save rotorgames/cfa6db99f66ecd5c1eb7c172ed912c38 to your computer and use it in GitHub Desktop.
Save rotorgames/cfa6db99f66ecd5c1eb7c172ed912c38 to your computer and use it in GitHub Desktop.
Smart ObservableCollection
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
namespace Example
{
public class SmartObservableCollection<T> : ObservableCollection<T>
{
int _startNewIndex;
int _startOldIndex;
List<T> _newItems;
List<T> _oldItems;
NotifyCollectionChangedAction _action;
public void ReplaceSmart(IEnumerable<T> source)
{
ResetEventQueue();
if (source == null || !source.Any())
{
Clear();
return;
}
if (Count == 0)
{
foreach (var item in source)
Items.Add(item);
RaiseChangeNotificationEvents(NotifyCollectionChangedAction.Add, new List<T>(source), 0);
return;
}
var index = -1;
var sourceList = new List<T>(source);
for (int i = 0; i < Items.Count; i++)
{
var currentItem = Items[i];
if (!sourceList.Contains(currentItem))
{
var sourceItem = sourceList.ElementAtOrDefault(i);
if (sourceItem != null && !Items.Contains(sourceItem))
{
index = -1;
RaiseReplaceEvent(i, currentItem, sourceItem);
Items.RemoveAt(i);
Items.Insert(i, sourceItem);
continue;
}
if (index < 0)
index = i;
RaiseRemoveEvent(index++, currentItem);
Items.RemoveAt(i);
i--;
}
else
{
index = -1;
FireEventQueueAndReset();
}
}
index = -1;
foreach (var item in sourceList)
{
index++;
if (Items.Contains(item))
{
var existItemIndex = Items.IndexOf(item);
if (index == existItemIndex)
continue;
RaiseMoveEvent(item, existItemIndex, index);
Items.RemoveAt(existItemIndex);
Items.Insert(index, item);
continue;
}
if (index >= Items.Count)
{
RaiseAddEvent(index, item);
Items.Add(item);
continue;
}
var currentItemForIndex = Items[index];
if (source.Contains(currentItemForIndex))
{
var sourceItemIndex = sourceList.IndexOf(currentItemForIndex);
if (index == sourceItemIndex)
{
RaiseMoveEvent(currentItemForIndex, index, sourceItemIndex);
Items.RemoveAt(index);
Items.Insert(sourceItemIndex, currentItemForIndex);
continue;
}
}
RaiseAddEvent(index, item);
Items.Insert(index, item);
}
FireEventQueueAndReset();
}
private void RaiseChangeNotificationEvents(NotifyCollectionChangedAction action, List<T> changedItems = null, int startingIndex = -1)
{
OnPropertyChanged(new PropertyChangedEventArgs(nameof(Count)));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
if (changedItems is null)
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action));
else
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, changedItems: changedItems, startingIndex: startingIndex));
}
void ResetEventQueue()
{
_startNewIndex = -1;
_startOldIndex = -1;
_newItems = new List<T>();
_oldItems = new List<T>();
_action = NotifyCollectionChangedAction.Reset;
}
void FireEventQueueAndReset()
{
if (_action == NotifyCollectionChangedAction.Add)
RaiseChangeNotificationEvents(NotifyCollectionChangedAction.Add, _newItems, _startNewIndex);
else if (_action == NotifyCollectionChangedAction.Remove)
RaiseChangeNotificationEvents(NotifyCollectionChangedAction.Remove, _oldItems, _startOldIndex);
else if (_action == NotifyCollectionChangedAction.Replace)
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, _newItems, _oldItems, _startOldIndex));
else if (_action == NotifyCollectionChangedAction.Move)
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, _oldItems, _startNewIndex, _startOldIndex));
ResetEventQueue();
}
void RaiseAddEvent(int index, T item)
{
if (_action != NotifyCollectionChangedAction.Add || (_startNewIndex >= 0 && _startNewIndex + _newItems.Count != index))
FireEventQueueAndReset();
_action = NotifyCollectionChangedAction.Add;
if (_startNewIndex < 0)
_startNewIndex = index;
_newItems.Add(item);
}
void RaiseRemoveEvent(int index, T item)
{
if (_action != NotifyCollectionChangedAction.Remove || (_startOldIndex >= 0 && _startOldIndex + _oldItems.Count != index))
FireEventQueueAndReset();
_action = NotifyCollectionChangedAction.Remove;
if (_startOldIndex < 0)
_startOldIndex = index;
_oldItems.Add(item);
}
void RaiseReplaceEvent(int index, T oldItem, T newItem)
{
if (_action != NotifyCollectionChangedAction.Replace || (_startOldIndex >= 0 && _startOldIndex + _oldItems.Count != index))
FireEventQueueAndReset();
_action = NotifyCollectionChangedAction.Replace;
if (_startOldIndex < 0)
_startOldIndex = index;
_oldItems.Add(oldItem);
_newItems.Add(newItem);
}
void RaiseMoveEvent(T item, int oldIndex, int newIndex)
{
if (_action != NotifyCollectionChangedAction.Move
|| (_startOldIndex >= 0 && _startOldIndex + _oldItems.Count != oldIndex) || (_startNewIndex >= 0 && _startNewIndex + _newItems.Count != newIndex))
FireEventQueueAndReset();
_action = NotifyCollectionChangedAction.Move;
if (_startOldIndex < 0)
_startOldIndex = oldIndex;
if (_startNewIndex < 0)
_startNewIndex = newIndex;
_oldItems.Add(item);
_newItems.Add(item);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment