Created
August 23, 2019 16:10
-
-
Save DevJohnC/33590107fb9eea3e7e39ae591eaebd85 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
public class ObservableCollectionView<T> : IReadOnlyCollection<T>, INotifyCollectionChanged, IDisposable | |
{ | |
private readonly ObservableCollection<T> _sourceCollection; | |
private List<T> _localCollection = new List<T>(); | |
public int Count => _localCollection.Count; | |
public event NotifyCollectionChangedEventHandler CollectionChanged; | |
public bool IsOrdered => OrderComparer != null; | |
private IComparer<T> _orderComparer; | |
public IComparer<T> OrderComparer | |
{ | |
get => _orderComparer; | |
set | |
{ | |
_orderComparer = value; | |
Sort(); | |
} | |
} | |
public bool IsFiltered => Filter != null; | |
private Func<T, bool> _filter; | |
public Func<T, bool> Filter | |
{ | |
get => _filter; | |
set | |
{ | |
_filter = value; | |
ApplyFilter(); | |
} | |
} | |
public ObservableCollectionView(ObservableCollection<T> sourceCollection) | |
{ | |
_sourceCollection = sourceCollection; | |
AttachToCollection(); | |
} | |
private void AttachToCollection() | |
{ | |
_sourceCollection.CollectionChanged += SourceCollectionChanged; | |
} | |
private void DetachFromCollection() | |
{ | |
_sourceCollection.CollectionChanged += SourceCollectionChanged; | |
} | |
private void Sort() | |
{ | |
var sortComparison = OrderComparer; | |
var unorderedList = _localCollection; | |
var orderedList = new List<T>(); | |
var itemCount = unorderedList.Count; | |
for (var fromIndex = 0; fromIndex < Count; fromIndex++) | |
{ | |
var item = unorderedList[fromIndex]; | |
var toIndex = orderedList.BinarySearch(item, sortComparison); | |
if (toIndex < 0) | |
toIndex = ~toIndex; | |
orderedList.Insert(toIndex, item); | |
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs( | |
NotifyCollectionChangedAction.Move, | |
item, toIndex, fromIndex | |
)); | |
} | |
_localCollection = orderedList; | |
} | |
private void ApplyFilter() | |
{ | |
if (IsFiltered) | |
{ | |
RemoveFilteredItems(); | |
} | |
AddNonFilteredItems(); | |
} | |
private void AddNonFilteredItems() | |
{ | |
foreach (var item in _sourceCollection) | |
{ | |
if (PassesFilter(item)) | |
{ | |
var index = GetIndex(item); | |
if (index < 0) | |
AddItem(index, item); | |
} | |
} | |
} | |
private void RemoveFilteredItems() | |
{ | |
for (var i = 0; i < _localCollection.Count; i++) | |
{ | |
var item = _localCollection[i]; | |
if (!PassesFilter(item)) | |
{ | |
RemoveItem(i, item); | |
i--; | |
} | |
} | |
} | |
private bool PassesFilter(T obj) | |
{ | |
if (!IsFiltered) | |
return true; | |
return Filter(obj); | |
} | |
private bool TryAddItem(T obj) | |
{ | |
if (!PassesFilter(obj)) | |
return false; | |
if (!IsOrdered) | |
{ | |
AddItem(_localCollection.Count, obj); | |
} | |
else | |
{ | |
var index = _localCollection.BinarySearch(obj, OrderComparer); | |
AddItem(index, obj); | |
} | |
return true; | |
} | |
private void AddItem(int index, T obj) | |
{ | |
if (index < 0 && IsOrdered) | |
index = ~index; | |
if (index < 0 && !IsOrdered) | |
index = _localCollection.Count; | |
_localCollection.Insert(index, obj); | |
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs( | |
NotifyCollectionChangedAction.Add, obj, index | |
)); | |
} | |
private int GetIndex(T obj) | |
{ | |
if (!IsOrdered) | |
{ | |
return _localCollection.IndexOf(obj); | |
} | |
return _localCollection.BinarySearch(obj, OrderComparer); | |
} | |
private bool TryRemoveItem(T obj) | |
{ | |
var index = GetIndex(obj); | |
if (index < 0) | |
return false; | |
RemoveItem(index, obj); | |
return true; | |
} | |
private void RemoveItem(int index, T obj) | |
{ | |
_localCollection.RemoveAt(index); | |
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs( | |
NotifyCollectionChangedAction.Remove, | |
new[] { obj }, | |
index | |
)); | |
} | |
private void AddItems(IList newItems) | |
{ | |
var itemCount = newItems.Count; | |
for (var i = 0; i < itemCount; i++) | |
{ | |
var newObj = (T)newItems[i]; | |
TryAddItem(newObj); | |
} | |
} | |
private void RemoveItems(IList oldItems) | |
{ | |
var itemCount = oldItems.Count; | |
for (var i = 0; i < itemCount; i++) | |
{ | |
var oldObj = (T)oldItems[i]; | |
TryRemoveItem(oldObj); | |
} | |
} | |
private void SourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) | |
{ | |
switch (e.Action) | |
{ | |
case NotifyCollectionChangedAction.Add: | |
AddItems(e.NewItems); | |
break; | |
case NotifyCollectionChangedAction.Remove: | |
RemoveItems(e.OldItems); | |
break; | |
case NotifyCollectionChangedAction.Replace: | |
break; | |
case NotifyCollectionChangedAction.Move: | |
break; | |
case NotifyCollectionChangedAction.Reset: | |
break; | |
} | |
} | |
public IEnumerator<T> GetEnumerator() | |
=> _localCollection.GetEnumerator(); | |
IEnumerator IEnumerable.GetEnumerator() | |
=> GetEnumerator(); | |
public void Dispose() | |
{ | |
DetachFromCollection(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment