Created
December 16, 2020 18:38
-
-
Save rotorgames/cfa6db99f66ecd5c1eb7c172ed912c38 to your computer and use it in GitHub Desktop.
Smart ObservableCollection
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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