Fix the index provided by SortedView to filters
This commit is contained in:
parent
bcf250c631
commit
b848a80a61
@ -49,7 +49,7 @@ namespace ObservableCollections.Internal
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var array = ArrayPool<T>.Shared.Rent(count);
|
var array = ArrayPool<T>.Shared.Rent(16);
|
||||||
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
foreach (var item in source)
|
foreach (var item in source)
|
||||||
@ -75,8 +75,8 @@ namespace ObservableCollections.Internal
|
|||||||
if (array.Length == index)
|
if (array.Length == index)
|
||||||
{
|
{
|
||||||
ArrayPool<T>.Shared.Return(array, RuntimeHelpersEx.IsReferenceOrContainsReferences<T>());
|
ArrayPool<T>.Shared.Return(array, RuntimeHelpersEx.IsReferenceOrContainsReferences<T>());
|
||||||
|
array = ArrayPool<T>.Shared.Rent(index * 2);
|
||||||
}
|
}
|
||||||
array = ArrayPool<T>.Shared.Rent(index * 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -114,7 +114,7 @@ namespace ObservableCollections.Internal
|
|||||||
public event Action<NotifyCollectionChangedAction> CollectionStateChanged;
|
public event Action<NotifyCollectionChangedAction> CollectionStateChanged;
|
||||||
public event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged;
|
public event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged;
|
||||||
|
|
||||||
public object SyncRoot { get; } = new object();
|
public object SyncRoot { get; } = new();
|
||||||
|
|
||||||
public FreezedSortableView(IEnumerable<T> source, Func<T, TView> selector)
|
public FreezedSortableView(IEnumerable<T> source, Func<T, TView> selector)
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,7 @@ namespace ObservableCollections.Internal
|
|||||||
readonly IObservableCollection<T> source;
|
readonly IObservableCollection<T> source;
|
||||||
readonly Func<T, TView> transform;
|
readonly Func<T, TView> transform;
|
||||||
readonly Func<T, TKey> identitySelector;
|
readonly Func<T, TKey> identitySelector;
|
||||||
readonly SortedDictionary<(T Value, TKey Key), (T Value, TView View)> dict;
|
readonly SortedList<(T Value, TKey Key), (T Value, TView View)> list;
|
||||||
|
|
||||||
ISynchronizedViewFilter<T, TView> filter;
|
ISynchronizedViewFilter<T, TView> filter;
|
||||||
|
|
||||||
@ -29,13 +29,13 @@ namespace ObservableCollections.Internal
|
|||||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||||
lock (source.SyncRoot)
|
lock (source.SyncRoot)
|
||||||
{
|
{
|
||||||
var dict = new SortedDictionary<(T, TKey), (T, TView)>(new Comparer(comparer));
|
var dict = new Dictionary<(T, TKey), (T, TView)>(source.Count);
|
||||||
foreach (var v in source)
|
foreach (var v in source)
|
||||||
{
|
{
|
||||||
dict.Add((v, identitySelector(v)), (v, transform(v)));
|
dict.Add((v, identitySelector(v)), (v, transform(v)));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dict = dict;
|
this.list = new SortedList<(T Value, TKey Key), (T Value, TView View)>(dict, new Comparer(comparer));
|
||||||
this.source.CollectionChanged += SourceCollectionChanged;
|
this.source.CollectionChanged += SourceCollectionChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,7 +46,7 @@ namespace ObservableCollections.Internal
|
|||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
return dict.Count;
|
return list.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ namespace ObservableCollections.Internal
|
|||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
foreach (var (_, (value, view)) in dict)
|
foreach (var (_, (value, view)) in list)
|
||||||
{
|
{
|
||||||
filter.InvokeOnAttach(value, view);
|
filter.InvokeOnAttach(value, view);
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ namespace ObservableCollections.Internal
|
|||||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||||
if (resetAction != null)
|
if (resetAction != null)
|
||||||
{
|
{
|
||||||
foreach (var (_, (value, view)) in dict)
|
foreach (var (_, (value, view)) in list)
|
||||||
{
|
{
|
||||||
resetAction(value, view);
|
resetAction(value, view);
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ namespace ObservableCollections.Internal
|
|||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
foreach (var item in dict)
|
foreach (var item in list)
|
||||||
{
|
{
|
||||||
if (filter.IsMatch(item.Value.Value, item.Value.View))
|
if (filter.IsMatch(item.Value.Value, item.Value.View))
|
||||||
{
|
{
|
||||||
@ -114,83 +114,101 @@ namespace ObservableCollections.Internal
|
|||||||
switch (e.Action)
|
switch (e.Action)
|
||||||
{
|
{
|
||||||
case NotifyCollectionChangedAction.Add:
|
case NotifyCollectionChangedAction.Add:
|
||||||
|
{
|
||||||
|
// Add, Insert
|
||||||
|
if (e.IsSingleItem)
|
||||||
{
|
{
|
||||||
// Add, Insert
|
var value = e.NewItem;
|
||||||
if (e.IsSingleItem)
|
var view = transform(value);
|
||||||
|
var id = identitySelector(value);
|
||||||
|
list.Add((value, id), (value, view));
|
||||||
|
var index = list.IndexOfKey((value, id));
|
||||||
|
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, index));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var value in e.NewItems)
|
||||||
{
|
{
|
||||||
var value = e.NewItem;
|
|
||||||
var view = transform(value);
|
var view = transform(value);
|
||||||
var id = identitySelector(value);
|
var id = identitySelector(value);
|
||||||
dict.Add((value, id), (value, view));
|
list.Add((value, id), (value, view));
|
||||||
filter.InvokeOnAdd(value, view, e);
|
var index = list.IndexOfKey((value, id));
|
||||||
}
|
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, index));
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var value in e.NewItems)
|
|
||||||
{
|
|
||||||
var view = transform(value);
|
|
||||||
var id = identitySelector(value);
|
|
||||||
dict.Add((value, id), (value, view));
|
|
||||||
filter.InvokeOnAdd(value, view, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Remove:
|
case NotifyCollectionChangedAction.Remove:
|
||||||
|
{
|
||||||
|
if (e.IsSingleItem)
|
||||||
{
|
{
|
||||||
if (e.IsSingleItem)
|
var value = e.OldItem;
|
||||||
|
var id = identitySelector(value);
|
||||||
|
var key = (value, id);
|
||||||
|
if (list.TryGetValue(key, out var v))
|
||||||
{
|
{
|
||||||
var value = e.OldItem;
|
var index = list.IndexOfKey(key);
|
||||||
var id = identitySelector(value);
|
list.RemoveAt(index);
|
||||||
dict.Remove((value, id), out var v);
|
filter.InvokeOnRemove(v.Value, v.View, NotifyCollectionChangedEventArgs<T>.Remove(v.Value, index));
|
||||||
filter.InvokeOnRemove(v.Value, v.View, e);
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var value in e.OldItems)
|
||||||
{
|
{
|
||||||
foreach (var value in e.OldItems)
|
var id = identitySelector(value);
|
||||||
|
var key = (value, id);
|
||||||
|
if (list.TryGetValue(key, out var v))
|
||||||
{
|
{
|
||||||
var id = identitySelector(value);
|
var index = list.IndexOfKey((value, id));
|
||||||
dict.Remove((value, id), out var v);
|
list.RemoveAt(index);
|
||||||
filter.InvokeOnRemove(v.Value, v.View, e);
|
filter.InvokeOnRemove(v.Value, v.View, NotifyCollectionChangedEventArgs<T>.Remove(v.Value, index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Replace:
|
case NotifyCollectionChangedAction.Replace:
|
||||||
// ReplaceRange is not supported in all ObservableCollections collections
|
// ReplaceRange is not supported in all ObservableCollections collections
|
||||||
// Replace is remove old item and insert new item.
|
// Replace is remove old item and insert new item.
|
||||||
|
{
|
||||||
|
var oldValue = e.OldItem;
|
||||||
|
var oldKey = (oldValue, identitySelector(oldValue));
|
||||||
|
if (list.TryGetValue(oldKey, out var o))
|
||||||
{
|
{
|
||||||
var oldValue = e.OldItem;
|
var oldIndex = list.IndexOfKey(oldKey);
|
||||||
dict.Remove((oldValue, identitySelector(oldValue)), out var oldView);
|
list.RemoveAt(oldIndex);
|
||||||
|
filter.InvokeOnRemove(o, NotifyCollectionChangedEventArgs<T>.Remove(oldValue, oldIndex));
|
||||||
var value = e.NewItem;
|
|
||||||
var view = transform(value);
|
|
||||||
var id = identitySelector(value);
|
|
||||||
dict.Add((value, id), (value, view));
|
|
||||||
|
|
||||||
filter.InvokeOnRemove(oldView, e);
|
|
||||||
filter.InvokeOnAdd(value, view, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var value = e.NewItem;
|
||||||
|
var view = transform(value);
|
||||||
|
var id = identitySelector(value);
|
||||||
|
list.Add((value, id), (value, view));
|
||||||
|
var newIndex = list.IndexOfKey((value, id));
|
||||||
|
|
||||||
|
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, newIndex));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Move:
|
case NotifyCollectionChangedAction.Move:
|
||||||
|
{
|
||||||
|
// Move(index change) does not affect sorted list.
|
||||||
|
var oldValue = e.OldItem;
|
||||||
|
if (list.TryGetValue((oldValue, identitySelector(oldValue)), out var view))
|
||||||
{
|
{
|
||||||
// Move(index change) does not affect sorted list.
|
filter.InvokeOnMove(view, e);
|
||||||
var oldValue = e.OldItem;
|
|
||||||
if (dict.TryGetValue((oldValue, identitySelector(oldValue)), out var view))
|
|
||||||
{
|
|
||||||
filter.InvokeOnMove(view, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Reset:
|
case NotifyCollectionChangedAction.Reset:
|
||||||
if (!filter.IsNullFilter())
|
if (!filter.IsNullFilter())
|
||||||
{
|
{
|
||||||
foreach (var item in dict)
|
foreach (var item in list)
|
||||||
{
|
{
|
||||||
filter.InvokeOnRemove(item.Value, e);
|
filter.InvokeOnRemove(item.Value, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dict.Clear();
|
list.Clear();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -222,4 +240,4 @@ namespace ObservableCollections.Internal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,13 +7,13 @@ using System.Linq;
|
|||||||
namespace ObservableCollections.Internal
|
namespace ObservableCollections.Internal
|
||||||
{
|
{
|
||||||
internal class SortedViewViewComparer<T, TKey, TView> : ISynchronizedView<T, TView>
|
internal class SortedViewViewComparer<T, TKey, TView> : ISynchronizedView<T, TView>
|
||||||
|
|
||||||
{
|
{
|
||||||
readonly IObservableCollection<T> source;
|
readonly IObservableCollection<T> source;
|
||||||
readonly Func<T, TView> transform;
|
readonly Func<T, TView> transform;
|
||||||
readonly Func<T, TKey> identitySelector;
|
readonly Func<T, TKey> identitySelector;
|
||||||
readonly Dictionary<TKey, TView> viewMap; // view-map needs to use in remove.
|
readonly Dictionary<TKey, TView> viewMap; // view-map needs to use in remove.
|
||||||
readonly SortedDictionary<(TView View, TKey Key), (T Value, TView View)> dict;
|
readonly SortedList<(TView View, TKey Key), (T Value, TView View)> list;
|
||||||
|
|
||||||
ISynchronizedViewFilter<T, TView> filter;
|
ISynchronizedViewFilter<T, TView> filter;
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ namespace ObservableCollections.Internal
|
|||||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||||
lock (source.SyncRoot)
|
lock (source.SyncRoot)
|
||||||
{
|
{
|
||||||
var dict = new SortedDictionary<(TView, TKey), (T, TView)>(new Comparer(comparer));
|
var dict = new Dictionary<(TView, TKey), (T, TView)>(source.Count);
|
||||||
this.viewMap = new Dictionary<TKey, TView>();
|
this.viewMap = new Dictionary<TKey, TView>();
|
||||||
foreach (var value in source)
|
foreach (var value in source)
|
||||||
{
|
{
|
||||||
@ -39,7 +39,7 @@ namespace ObservableCollections.Internal
|
|||||||
dict.Add((view, id), (value, view));
|
dict.Add((view, id), (value, view));
|
||||||
viewMap.Add(id, view);
|
viewMap.Add(id, view);
|
||||||
}
|
}
|
||||||
this.dict = dict;
|
this.list = new SortedList<(TView View, TKey Key), (T Value, TView View)>(dict, new Comparer(comparer));
|
||||||
this.source.CollectionChanged += SourceCollectionChanged;
|
this.source.CollectionChanged += SourceCollectionChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ namespace ObservableCollections.Internal
|
|||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
return dict.Count;
|
return list.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ namespace ObservableCollections.Internal
|
|||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
foreach (var (_, (value, view)) in dict)
|
foreach (var (_, (value, view)) in list)
|
||||||
{
|
{
|
||||||
filter.InvokeOnAttach(value, view);
|
filter.InvokeOnAttach(value, view);
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ namespace ObservableCollections.Internal
|
|||||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||||
if (resetAction != null)
|
if (resetAction != null)
|
||||||
{
|
{
|
||||||
foreach (var (_, (value, view)) in dict)
|
foreach (var (_, (value, view)) in list)
|
||||||
{
|
{
|
||||||
resetAction(value, view);
|
resetAction(value, view);
|
||||||
}
|
}
|
||||||
@ -95,7 +95,7 @@ namespace ObservableCollections.Internal
|
|||||||
|
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
foreach (var item in dict)
|
foreach (var item in list)
|
||||||
{
|
{
|
||||||
if (filter.IsMatch(item.Value.Value, item.Value.View))
|
if (filter.IsMatch(item.Value.Value, item.Value.View))
|
||||||
{
|
{
|
||||||
@ -119,96 +119,114 @@ namespace ObservableCollections.Internal
|
|||||||
switch (e.Action)
|
switch (e.Action)
|
||||||
{
|
{
|
||||||
case NotifyCollectionChangedAction.Add:
|
case NotifyCollectionChangedAction.Add:
|
||||||
|
{
|
||||||
|
// Add, Insert
|
||||||
|
if (e.IsSingleItem)
|
||||||
{
|
{
|
||||||
// Add, Insert
|
var value = e.NewItem;
|
||||||
if (e.IsSingleItem)
|
var view = transform(value);
|
||||||
|
var id = identitySelector(value);
|
||||||
|
list.Add((view, id), (value, view));
|
||||||
|
viewMap.Add(id, view);
|
||||||
|
var index = list.IndexOfKey((view, id));
|
||||||
|
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, index));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var value in e.NewItems)
|
||||||
{
|
{
|
||||||
var value = e.NewItem;
|
|
||||||
var view = transform(value);
|
var view = transform(value);
|
||||||
var id = identitySelector(value);
|
var id = identitySelector(value);
|
||||||
dict.Add((view, id), (value, view));
|
list.Add((view, id), (value, view));
|
||||||
viewMap.Add(id, view);
|
viewMap.Add(id, view);
|
||||||
filter.InvokeOnAdd(value, view, e);
|
var index = list.IndexOfKey((view, id));
|
||||||
}
|
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, index));
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var value in e.NewItems)
|
|
||||||
{
|
|
||||||
var view = transform(value);
|
|
||||||
var id = identitySelector(value);
|
|
||||||
dict.Add((view, id), (value, view));
|
|
||||||
viewMap.Add(id, view);
|
|
||||||
filter.InvokeOnAdd(value, view, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case NotifyCollectionChangedAction.Remove:
|
case NotifyCollectionChangedAction.Remove:
|
||||||
|
{
|
||||||
|
if (e.IsSingleItem)
|
||||||
{
|
{
|
||||||
if (e.IsSingleItem)
|
var value = e.OldItem;
|
||||||
|
var id = identitySelector(value);
|
||||||
|
if (viewMap.Remove(id, out var view))
|
||||||
|
{
|
||||||
|
var key = (view, id);
|
||||||
|
if (list.TryGetValue(key, out var v))
|
||||||
|
{
|
||||||
|
var index = list.IndexOfKey(key);
|
||||||
|
list.RemoveAt(index);
|
||||||
|
filter.InvokeOnRemove(v, NotifyCollectionChangedEventArgs<T>.Remove(v.Value, index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var value in e.OldItems)
|
||||||
{
|
{
|
||||||
var value = e.OldItem;
|
|
||||||
var id = identitySelector(value);
|
var id = identitySelector(value);
|
||||||
if (viewMap.Remove(id, out var view))
|
if (viewMap.Remove(id, out var view))
|
||||||
{
|
{
|
||||||
dict.Remove((view, id), out var v);
|
var key = (view, id);
|
||||||
filter.InvokeOnRemove(v, e);
|
if (list.TryGetValue(key, out var v))
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var value in e.OldItems)
|
|
||||||
{
|
|
||||||
var id = identitySelector(value);
|
|
||||||
if (viewMap.Remove(id, out var view))
|
|
||||||
{
|
{
|
||||||
dict.Remove((view, id), out var v);
|
var index = list.IndexOfKey((view, id));
|
||||||
filter.InvokeOnRemove(v, e);
|
list.RemoveAt(index);
|
||||||
|
filter.InvokeOnRemove(v, NotifyCollectionChangedEventArgs<T>.Remove(v.Value, index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case NotifyCollectionChangedAction.Replace:
|
case NotifyCollectionChangedAction.Replace:
|
||||||
// Replace is remove old item and insert new item.
|
// Replace is remove old item and insert new item.
|
||||||
|
{
|
||||||
|
var oldValue = e.OldItem;
|
||||||
|
var oldId = identitySelector(oldValue);
|
||||||
|
if (viewMap.Remove(oldId, out var oldView))
|
||||||
{
|
{
|
||||||
var oldValue = e.OldItem;
|
var oldKey = (oldView, oldId);
|
||||||
var oldKey = identitySelector(oldValue);
|
if (list.TryGetValue(oldKey, out var v))
|
||||||
if (viewMap.Remove(oldKey, out var oldView))
|
|
||||||
{
|
{
|
||||||
dict.Remove((oldView, oldKey));
|
var oldIndex = list.IndexOfKey(oldKey);
|
||||||
filter.InvokeOnRemove(oldValue, oldView, e);
|
list.RemoveAt(oldIndex);
|
||||||
|
filter.InvokeOnRemove(oldValue, oldView, NotifyCollectionChangedEventArgs<T>.Remove(v.Value, oldIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = e.NewItem;
|
|
||||||
var view = transform(value);
|
|
||||||
var id = identitySelector(value);
|
|
||||||
dict.Add((view, id), (value, view));
|
|
||||||
viewMap.Add(id, view);
|
|
||||||
|
|
||||||
filter.InvokeOnAdd(value, view, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var value = e.NewItem;
|
||||||
|
var view = transform(value);
|
||||||
|
var id = identitySelector(value);
|
||||||
|
list.Add((view, id), (value, view));
|
||||||
|
viewMap.Add(id, view);
|
||||||
|
|
||||||
|
var index = list.IndexOfKey((view, id));
|
||||||
|
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, index));
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case NotifyCollectionChangedAction.Move:
|
case NotifyCollectionChangedAction.Move:
|
||||||
// Move(index change) does not affect soreted dict.
|
// Move(index change) does not affect soreted dict.
|
||||||
|
{
|
||||||
|
var value = e.OldItem;
|
||||||
|
var key = identitySelector(value);
|
||||||
|
if (viewMap.TryGetValue(key, out var view))
|
||||||
{
|
{
|
||||||
var value = e.OldItem;
|
filter.InvokeOnMove(value, view, e);
|
||||||
var key = identitySelector(value);
|
|
||||||
if (viewMap.TryGetValue(key, out var view))
|
|
||||||
{
|
|
||||||
filter.InvokeOnMove(value, view, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case NotifyCollectionChangedAction.Reset:
|
case NotifyCollectionChangedAction.Reset:
|
||||||
if (!filter.IsNullFilter())
|
if (!filter.IsNullFilter())
|
||||||
{
|
{
|
||||||
foreach (var item in dict)
|
foreach (var item in list)
|
||||||
{
|
{
|
||||||
filter.InvokeOnRemove(item.Value, e);
|
filter.InvokeOnRemove(item.Value, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dict.Clear();
|
list.Clear();
|
||||||
viewMap.Clear();
|
viewMap.Clear();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -2,7 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
#if (NETSTANDARD2_0 || NET_STANDARD_2_0 || NET_4_6) && !UNITY_2021_1_OR_NEWER
|
#if NETSTANDARD2_0 || NET_STANDARD_2_0 || NET_4_6
|
||||||
|
|
||||||
namespace System.Diagnostics.CodeAnalysis
|
namespace System.Diagnostics.CodeAnalysis
|
||||||
{
|
{
|
||||||
|
@ -114,7 +114,7 @@ namespace ObservableCollections.Internal
|
|||||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||||
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
|
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
|
||||||
|
|
||||||
public object SyncRoot { get; } = new object();
|
public object SyncRoot { get; } = new();
|
||||||
|
|
||||||
public FreezedSortableView(IEnumerable<T> source, Func<T, TView> selector)
|
public FreezedSortableView(IEnumerable<T> source, Func<T, TView> selector)
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,7 @@ namespace ObservableCollections.Internal
|
|||||||
readonly IObservableCollection<T> source;
|
readonly IObservableCollection<T> source;
|
||||||
readonly Func<T, TView> transform;
|
readonly Func<T, TView> transform;
|
||||||
readonly Func<T, TKey> identitySelector;
|
readonly Func<T, TKey> identitySelector;
|
||||||
readonly SortedDictionary<(T Value, TKey Key), (T Value, TView View)> dict;
|
readonly SortedList<(T Value, TKey Key), (T Value, TView View)> list;
|
||||||
|
|
||||||
ISynchronizedViewFilter<T, TView> filter;
|
ISynchronizedViewFilter<T, TView> filter;
|
||||||
|
|
||||||
@ -29,13 +29,13 @@ namespace ObservableCollections.Internal
|
|||||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||||
lock (source.SyncRoot)
|
lock (source.SyncRoot)
|
||||||
{
|
{
|
||||||
var dict = new SortedDictionary<(T, TKey), (T, TView)>(new Comparer(comparer));
|
var dict = new Dictionary<(T, TKey), (T, TView)>(source.Count);
|
||||||
foreach (var v in source)
|
foreach (var v in source)
|
||||||
{
|
{
|
||||||
dict.Add((v, identitySelector(v)), (v, transform(v)));
|
dict.Add((v, identitySelector(v)), (v, transform(v)));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dict = dict;
|
this.list = new SortedList<(T Value, TKey Key), (T Value, TView View)>(dict, new Comparer(comparer));
|
||||||
this.source.CollectionChanged += SourceCollectionChanged;
|
this.source.CollectionChanged += SourceCollectionChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,7 +46,7 @@ namespace ObservableCollections.Internal
|
|||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
return dict.Count;
|
return list.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ namespace ObservableCollections.Internal
|
|||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
foreach (var (_, (value, view)) in dict)
|
foreach (var (_, (value, view)) in list)
|
||||||
{
|
{
|
||||||
filter.InvokeOnAttach(value, view);
|
filter.InvokeOnAttach(value, view);
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ namespace ObservableCollections.Internal
|
|||||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||||
if (resetAction != null)
|
if (resetAction != null)
|
||||||
{
|
{
|
||||||
foreach (var (_, (value, view)) in dict)
|
foreach (var (_, (value, view)) in list)
|
||||||
{
|
{
|
||||||
resetAction(value, view);
|
resetAction(value, view);
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ namespace ObservableCollections.Internal
|
|||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
foreach (var item in dict)
|
foreach (var item in list)
|
||||||
{
|
{
|
||||||
if (filter.IsMatch(item.Value.Value, item.Value.View))
|
if (filter.IsMatch(item.Value.Value, item.Value.View))
|
||||||
{
|
{
|
||||||
@ -114,83 +114,101 @@ namespace ObservableCollections.Internal
|
|||||||
switch (e.Action)
|
switch (e.Action)
|
||||||
{
|
{
|
||||||
case NotifyCollectionChangedAction.Add:
|
case NotifyCollectionChangedAction.Add:
|
||||||
|
{
|
||||||
|
// Add, Insert
|
||||||
|
if (e.IsSingleItem)
|
||||||
{
|
{
|
||||||
// Add, Insert
|
var value = e.NewItem;
|
||||||
if (e.IsSingleItem)
|
var view = transform(value);
|
||||||
|
var id = identitySelector(value);
|
||||||
|
list.Add((value, id), (value, view));
|
||||||
|
var index = list.IndexOfKey((value, id));
|
||||||
|
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, index));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var value in e.NewItems)
|
||||||
{
|
{
|
||||||
var value = e.NewItem;
|
|
||||||
var view = transform(value);
|
var view = transform(value);
|
||||||
var id = identitySelector(value);
|
var id = identitySelector(value);
|
||||||
dict.Add((value, id), (value, view));
|
list.Add((value, id), (value, view));
|
||||||
filter.InvokeOnAdd(value, view, e);
|
var index = list.IndexOfKey((value, id));
|
||||||
}
|
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, index));
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var value in e.NewItems)
|
|
||||||
{
|
|
||||||
var view = transform(value);
|
|
||||||
var id = identitySelector(value);
|
|
||||||
dict.Add((value, id), (value, view));
|
|
||||||
filter.InvokeOnAdd(value, view, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Remove:
|
case NotifyCollectionChangedAction.Remove:
|
||||||
|
{
|
||||||
|
if (e.IsSingleItem)
|
||||||
{
|
{
|
||||||
if (e.IsSingleItem)
|
var value = e.OldItem;
|
||||||
|
var id = identitySelector(value);
|
||||||
|
var key = (value, id);
|
||||||
|
if (list.TryGetValue(key, out var v))
|
||||||
{
|
{
|
||||||
var value = e.OldItem;
|
var index = list.IndexOfKey(key);
|
||||||
var id = identitySelector(value);
|
list.RemoveAt(index);
|
||||||
dict.Remove((value, id), out var v);
|
filter.InvokeOnRemove(v.Value, v.View, NotifyCollectionChangedEventArgs<T>.Remove(v.Value, index));
|
||||||
filter.InvokeOnRemove(v.Value, v.View, e);
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var value in e.OldItems)
|
||||||
{
|
{
|
||||||
foreach (var value in e.OldItems)
|
var id = identitySelector(value);
|
||||||
|
var key = (value, id);
|
||||||
|
if (list.TryGetValue(key, out var v))
|
||||||
{
|
{
|
||||||
var id = identitySelector(value);
|
var index = list.IndexOfKey((value, id));
|
||||||
dict.Remove((value, id), out var v);
|
list.RemoveAt(index);
|
||||||
filter.InvokeOnRemove(v.Value, v.View, e);
|
filter.InvokeOnRemove(v.Value, v.View, NotifyCollectionChangedEventArgs<T>.Remove(v.Value, index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Replace:
|
case NotifyCollectionChangedAction.Replace:
|
||||||
// ReplaceRange is not supported in all ObservableCollections collections
|
// ReplaceRange is not supported in all ObservableCollections collections
|
||||||
// Replace is remove old item and insert new item.
|
// Replace is remove old item and insert new item.
|
||||||
|
{
|
||||||
|
var oldValue = e.OldItem;
|
||||||
|
var oldKey = (oldValue, identitySelector(oldValue));
|
||||||
|
if (list.TryGetValue(oldKey, out var o))
|
||||||
{
|
{
|
||||||
var oldValue = e.OldItem;
|
var oldIndex = list.IndexOfKey(oldKey);
|
||||||
dict.Remove((oldValue, identitySelector(oldValue)), out var oldView);
|
list.RemoveAt(oldIndex);
|
||||||
|
filter.InvokeOnRemove(o, NotifyCollectionChangedEventArgs<T>.Remove(oldValue, oldIndex));
|
||||||
var value = e.NewItem;
|
|
||||||
var view = transform(value);
|
|
||||||
var id = identitySelector(value);
|
|
||||||
dict.Add((value, id), (value, view));
|
|
||||||
|
|
||||||
filter.InvokeOnRemove(oldView, e);
|
|
||||||
filter.InvokeOnAdd(value, view, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var value = e.NewItem;
|
||||||
|
var view = transform(value);
|
||||||
|
var id = identitySelector(value);
|
||||||
|
list.Add((value, id), (value, view));
|
||||||
|
var newIndex = list.IndexOfKey((value, id));
|
||||||
|
|
||||||
|
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, newIndex));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Move:
|
case NotifyCollectionChangedAction.Move:
|
||||||
|
{
|
||||||
|
// Move(index change) does not affect sorted list.
|
||||||
|
var oldValue = e.OldItem;
|
||||||
|
if (list.TryGetValue((oldValue, identitySelector(oldValue)), out var view))
|
||||||
{
|
{
|
||||||
// Move(index change) does not affect sorted list.
|
filter.InvokeOnMove(view, e);
|
||||||
var oldValue = e.OldItem;
|
|
||||||
if (dict.TryGetValue((oldValue, identitySelector(oldValue)), out var view))
|
|
||||||
{
|
|
||||||
filter.InvokeOnMove(view, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Reset:
|
case NotifyCollectionChangedAction.Reset:
|
||||||
if (!filter.IsNullFilter())
|
if (!filter.IsNullFilter())
|
||||||
{
|
{
|
||||||
foreach (var item in dict)
|
foreach (var item in list)
|
||||||
{
|
{
|
||||||
filter.InvokeOnRemove(item.Value, e);
|
filter.InvokeOnRemove(item.Value, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dict.Clear();
|
list.Clear();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -222,4 +240,4 @@ namespace ObservableCollections.Internal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,13 +7,13 @@ using System.Linq;
|
|||||||
namespace ObservableCollections.Internal
|
namespace ObservableCollections.Internal
|
||||||
{
|
{
|
||||||
internal class SortedViewViewComparer<T, TKey, TView> : ISynchronizedView<T, TView>
|
internal class SortedViewViewComparer<T, TKey, TView> : ISynchronizedView<T, TView>
|
||||||
where TKey : notnull
|
where TKey : notnull
|
||||||
{
|
{
|
||||||
readonly IObservableCollection<T> source;
|
readonly IObservableCollection<T> source;
|
||||||
readonly Func<T, TView> transform;
|
readonly Func<T, TView> transform;
|
||||||
readonly Func<T, TKey> identitySelector;
|
readonly Func<T, TKey> identitySelector;
|
||||||
readonly Dictionary<TKey, TView> viewMap; // view-map needs to use in remove.
|
readonly Dictionary<TKey, TView> viewMap; // view-map needs to use in remove.
|
||||||
readonly SortedDictionary<(TView View, TKey Key), (T Value, TView View)> dict;
|
readonly SortedList<(TView View, TKey Key), (T Value, TView View)> list;
|
||||||
|
|
||||||
ISynchronizedViewFilter<T, TView> filter;
|
ISynchronizedViewFilter<T, TView> filter;
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ namespace ObservableCollections.Internal
|
|||||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||||
lock (source.SyncRoot)
|
lock (source.SyncRoot)
|
||||||
{
|
{
|
||||||
var dict = new SortedDictionary<(TView, TKey), (T, TView)>(new Comparer(comparer));
|
var dict = new Dictionary<(TView, TKey), (T, TView)>(source.Count);
|
||||||
this.viewMap = new Dictionary<TKey, TView>();
|
this.viewMap = new Dictionary<TKey, TView>();
|
||||||
foreach (var value in source)
|
foreach (var value in source)
|
||||||
{
|
{
|
||||||
@ -39,7 +39,7 @@ namespace ObservableCollections.Internal
|
|||||||
dict.Add((view, id), (value, view));
|
dict.Add((view, id), (value, view));
|
||||||
viewMap.Add(id, view);
|
viewMap.Add(id, view);
|
||||||
}
|
}
|
||||||
this.dict = dict;
|
this.list = new SortedList<(TView View, TKey Key), (T Value, TView View)>(dict, new Comparer(comparer));
|
||||||
this.source.CollectionChanged += SourceCollectionChanged;
|
this.source.CollectionChanged += SourceCollectionChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ namespace ObservableCollections.Internal
|
|||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
return dict.Count;
|
return list.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ namespace ObservableCollections.Internal
|
|||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
foreach (var (_, (value, view)) in dict)
|
foreach (var (_, (value, view)) in list)
|
||||||
{
|
{
|
||||||
filter.InvokeOnAttach(value, view);
|
filter.InvokeOnAttach(value, view);
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ namespace ObservableCollections.Internal
|
|||||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||||
if (resetAction != null)
|
if (resetAction != null)
|
||||||
{
|
{
|
||||||
foreach (var (_, (value, view)) in dict)
|
foreach (var (_, (value, view)) in list)
|
||||||
{
|
{
|
||||||
resetAction(value, view);
|
resetAction(value, view);
|
||||||
}
|
}
|
||||||
@ -95,7 +95,7 @@ namespace ObservableCollections.Internal
|
|||||||
|
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
foreach (var item in dict)
|
foreach (var item in list)
|
||||||
{
|
{
|
||||||
if (filter.IsMatch(item.Value.Value, item.Value.View))
|
if (filter.IsMatch(item.Value.Value, item.Value.View))
|
||||||
{
|
{
|
||||||
@ -119,96 +119,114 @@ namespace ObservableCollections.Internal
|
|||||||
switch (e.Action)
|
switch (e.Action)
|
||||||
{
|
{
|
||||||
case NotifyCollectionChangedAction.Add:
|
case NotifyCollectionChangedAction.Add:
|
||||||
|
{
|
||||||
|
// Add, Insert
|
||||||
|
if (e.IsSingleItem)
|
||||||
{
|
{
|
||||||
// Add, Insert
|
var value = e.NewItem;
|
||||||
if (e.IsSingleItem)
|
var view = transform(value);
|
||||||
|
var id = identitySelector(value);
|
||||||
|
list.Add((view, id), (value, view));
|
||||||
|
viewMap.Add(id, view);
|
||||||
|
var index = list.IndexOfKey((view, id));
|
||||||
|
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, index));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var value in e.NewItems)
|
||||||
{
|
{
|
||||||
var value = e.NewItem;
|
|
||||||
var view = transform(value);
|
var view = transform(value);
|
||||||
var id = identitySelector(value);
|
var id = identitySelector(value);
|
||||||
dict.Add((view, id), (value, view));
|
list.Add((view, id), (value, view));
|
||||||
viewMap.Add(id, view);
|
viewMap.Add(id, view);
|
||||||
filter.InvokeOnAdd(value, view, e);
|
var index = list.IndexOfKey((view, id));
|
||||||
}
|
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, index));
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var value in e.NewItems)
|
|
||||||
{
|
|
||||||
var view = transform(value);
|
|
||||||
var id = identitySelector(value);
|
|
||||||
dict.Add((view, id), (value, view));
|
|
||||||
viewMap.Add(id, view);
|
|
||||||
filter.InvokeOnAdd(value, view, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case NotifyCollectionChangedAction.Remove:
|
case NotifyCollectionChangedAction.Remove:
|
||||||
|
{
|
||||||
|
if (e.IsSingleItem)
|
||||||
{
|
{
|
||||||
if (e.IsSingleItem)
|
var value = e.OldItem;
|
||||||
|
var id = identitySelector(value);
|
||||||
|
if (viewMap.Remove(id, out var view))
|
||||||
|
{
|
||||||
|
var key = (view, id);
|
||||||
|
if (list.TryGetValue(key, out var v))
|
||||||
|
{
|
||||||
|
var index = list.IndexOfKey(key);
|
||||||
|
list.RemoveAt(index);
|
||||||
|
filter.InvokeOnRemove(v, NotifyCollectionChangedEventArgs<T>.Remove(v.Value, index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var value in e.OldItems)
|
||||||
{
|
{
|
||||||
var value = e.OldItem;
|
|
||||||
var id = identitySelector(value);
|
var id = identitySelector(value);
|
||||||
if (viewMap.Remove(id, out var view))
|
if (viewMap.Remove(id, out var view))
|
||||||
{
|
{
|
||||||
dict.Remove((view, id), out var v);
|
var key = (view, id);
|
||||||
filter.InvokeOnRemove(v, e);
|
if (list.TryGetValue(key, out var v))
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var value in e.OldItems)
|
|
||||||
{
|
|
||||||
var id = identitySelector(value);
|
|
||||||
if (viewMap.Remove(id, out var view))
|
|
||||||
{
|
{
|
||||||
dict.Remove((view, id), out var v);
|
var index = list.IndexOfKey((view, id));
|
||||||
filter.InvokeOnRemove(v, e);
|
list.RemoveAt(index);
|
||||||
|
filter.InvokeOnRemove(v, NotifyCollectionChangedEventArgs<T>.Remove(v.Value, index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case NotifyCollectionChangedAction.Replace:
|
case NotifyCollectionChangedAction.Replace:
|
||||||
// Replace is remove old item and insert new item.
|
// Replace is remove old item and insert new item.
|
||||||
|
{
|
||||||
|
var oldValue = e.OldItem;
|
||||||
|
var oldId = identitySelector(oldValue);
|
||||||
|
if (viewMap.Remove(oldId, out var oldView))
|
||||||
{
|
{
|
||||||
var oldValue = e.OldItem;
|
var oldKey = (oldView, oldId);
|
||||||
var oldKey = identitySelector(oldValue);
|
if (list.TryGetValue(oldKey, out var v))
|
||||||
if (viewMap.Remove(oldKey, out var oldView))
|
|
||||||
{
|
{
|
||||||
dict.Remove((oldView, oldKey));
|
var oldIndex = list.IndexOfKey(oldKey);
|
||||||
filter.InvokeOnRemove(oldValue, oldView, e);
|
list.RemoveAt(oldIndex);
|
||||||
|
filter.InvokeOnRemove(oldValue, oldView, NotifyCollectionChangedEventArgs<T>.Remove(v.Value, oldIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = e.NewItem;
|
|
||||||
var view = transform(value);
|
|
||||||
var id = identitySelector(value);
|
|
||||||
dict.Add((view, id), (value, view));
|
|
||||||
viewMap.Add(id, view);
|
|
||||||
|
|
||||||
filter.InvokeOnAdd(value, view, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var value = e.NewItem;
|
||||||
|
var view = transform(value);
|
||||||
|
var id = identitySelector(value);
|
||||||
|
list.Add((view, id), (value, view));
|
||||||
|
viewMap.Add(id, view);
|
||||||
|
|
||||||
|
var index = list.IndexOfKey((view, id));
|
||||||
|
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, index));
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case NotifyCollectionChangedAction.Move:
|
case NotifyCollectionChangedAction.Move:
|
||||||
// Move(index change) does not affect soreted dict.
|
// Move(index change) does not affect soreted dict.
|
||||||
|
{
|
||||||
|
var value = e.OldItem;
|
||||||
|
var key = identitySelector(value);
|
||||||
|
if (viewMap.TryGetValue(key, out var view))
|
||||||
{
|
{
|
||||||
var value = e.OldItem;
|
filter.InvokeOnMove(value, view, e);
|
||||||
var key = identitySelector(value);
|
|
||||||
if (viewMap.TryGetValue(key, out var view))
|
|
||||||
{
|
|
||||||
filter.InvokeOnMove(value, view, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case NotifyCollectionChangedAction.Reset:
|
case NotifyCollectionChangedAction.Reset:
|
||||||
if (!filter.IsNullFilter())
|
if (!filter.IsNullFilter())
|
||||||
{
|
{
|
||||||
foreach (var item in dict)
|
foreach (var item in list)
|
||||||
{
|
{
|
||||||
filter.InvokeOnRemove(item.Value, e);
|
filter.InvokeOnRemove(item.Value, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dict.Clear();
|
list.Clear();
|
||||||
viewMap.Clear();
|
viewMap.Clear();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
69
tests/ObservableCollections.Tests/SortedViewTest.cs
Normal file
69
tests/ObservableCollections.Tests/SortedViewTest.cs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace ObservableCollections.Tests;
|
||||||
|
|
||||||
|
public class SortedViewTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Sort()
|
||||||
|
{
|
||||||
|
var list = new ObservableList<int>();
|
||||||
|
var sortedView = list.CreateSortedView(
|
||||||
|
x => x,
|
||||||
|
x => new ViewContainer<int>(x),
|
||||||
|
Comparer<int>.Default);
|
||||||
|
|
||||||
|
list.Add(10);
|
||||||
|
list.Add(50);
|
||||||
|
list.Add(30);
|
||||||
|
list.Add(20);
|
||||||
|
list.Add(40);
|
||||||
|
|
||||||
|
using var e = sortedView.GetEnumerator();
|
||||||
|
e.MoveNext().Should().BeTrue();
|
||||||
|
e.Current.Value.Should().Be(10);
|
||||||
|
e.MoveNext().Should().BeTrue();
|
||||||
|
e.Current.Value.Should().Be(20);
|
||||||
|
e.MoveNext().Should().BeTrue();
|
||||||
|
e.Current.Value.Should().Be(30);
|
||||||
|
e.MoveNext().Should().BeTrue();
|
||||||
|
e.Current.Value.Should().Be(40);
|
||||||
|
e.MoveNext().Should().BeTrue();
|
||||||
|
e.Current.Value.Should().Be(50);
|
||||||
|
e.MoveNext().Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ObserveIndex()
|
||||||
|
{
|
||||||
|
var list = new ObservableList<int>();
|
||||||
|
var sortedView = list.CreateSortedView(
|
||||||
|
x => x,
|
||||||
|
x => new ViewContainer<int>(x),
|
||||||
|
Comparer<int>.Default);
|
||||||
|
|
||||||
|
var filter = new TestFilter<int>((value, view) => value % 2 == 0);
|
||||||
|
list.Add(50);
|
||||||
|
list.Add(10);
|
||||||
|
|
||||||
|
sortedView.AttachFilter(filter);
|
||||||
|
|
||||||
|
list.Add(20);
|
||||||
|
filter.CalledOnCollectionChanged[0].changedKind.Should().Be(ChangedKind.Add);
|
||||||
|
filter.CalledOnCollectionChanged[0].value.Should().Be(20);
|
||||||
|
filter.CalledOnCollectionChanged[0].index.Should().Be(1);
|
||||||
|
|
||||||
|
list.Remove(20);
|
||||||
|
filter.CalledOnCollectionChanged[1].changedKind.Should().Be(ChangedKind.Remove);
|
||||||
|
filter.CalledOnCollectionChanged[1].value.Should().Be(20);
|
||||||
|
filter.CalledOnCollectionChanged[1].oldIndex.Should().Be(1);
|
||||||
|
|
||||||
|
list[1] = 999; // from 10(at 0 in original) to 999
|
||||||
|
filter.CalledOnCollectionChanged[2].changedKind.Should().Be(ChangedKind.Remove);
|
||||||
|
filter.CalledOnCollectionChanged[2].value.Should().Be(10);
|
||||||
|
filter.CalledOnCollectionChanged[2].oldIndex.Should().Be(0);
|
||||||
|
filter.CalledOnCollectionChanged[3].changedKind.Should().Be(ChangedKind.Add);
|
||||||
|
filter.CalledOnCollectionChanged[3].value.Should().Be(999);
|
||||||
|
filter.CalledOnCollectionChanged[3].index.Should().Be(1);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace ObservableCollections.Tests;
|
||||||
|
|
||||||
|
public class SortedViewViewComparerTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Sort()
|
||||||
|
{
|
||||||
|
var list = new ObservableList<int>();
|
||||||
|
var sortedView = list.CreateSortedView(
|
||||||
|
x => x,
|
||||||
|
x => new ViewContainer<int>(x),
|
||||||
|
Comparer<ViewContainer<int>>.Default);
|
||||||
|
|
||||||
|
list.Add(10);
|
||||||
|
list.Add(50);
|
||||||
|
list.Add(30);
|
||||||
|
list.Add(20);
|
||||||
|
list.Add(40);
|
||||||
|
|
||||||
|
using var e = sortedView.GetEnumerator();
|
||||||
|
e.MoveNext().Should().BeTrue();
|
||||||
|
e.Current.Value.Should().Be(10);
|
||||||
|
e.MoveNext().Should().BeTrue();
|
||||||
|
e.Current.Value.Should().Be(20);
|
||||||
|
e.MoveNext().Should().BeTrue();
|
||||||
|
e.Current.Value.Should().Be(30);
|
||||||
|
e.MoveNext().Should().BeTrue();
|
||||||
|
e.Current.Value.Should().Be(40);
|
||||||
|
e.MoveNext().Should().BeTrue();
|
||||||
|
e.Current.Value.Should().Be(50);
|
||||||
|
e.MoveNext().Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ObserveIndex()
|
||||||
|
{
|
||||||
|
var list = new ObservableList<int>();
|
||||||
|
var sortedView = list.CreateSortedView(
|
||||||
|
x => x,
|
||||||
|
x => new ViewContainer<int>(x),
|
||||||
|
Comparer<ViewContainer<int>>.Default);
|
||||||
|
|
||||||
|
var filter = new TestFilter<int>((value, view) => value % 2 == 0);
|
||||||
|
list.Add(50);
|
||||||
|
list.Add(10);
|
||||||
|
|
||||||
|
sortedView.AttachFilter(filter);
|
||||||
|
|
||||||
|
list.Add(20);
|
||||||
|
filter.CalledOnCollectionChanged[0].changedKind.Should().Be(ChangedKind.Add);
|
||||||
|
filter.CalledOnCollectionChanged[0].value.Should().Be(20);
|
||||||
|
filter.CalledOnCollectionChanged[0].index.Should().Be(1);
|
||||||
|
|
||||||
|
list.Remove(20);
|
||||||
|
filter.CalledOnCollectionChanged[1].changedKind.Should().Be(ChangedKind.Remove);
|
||||||
|
filter.CalledOnCollectionChanged[1].value.Should().Be(20);
|
||||||
|
filter.CalledOnCollectionChanged[1].oldIndex.Should().Be(1);
|
||||||
|
|
||||||
|
list[1] = 999; // from 10(at 0 in original) to 999
|
||||||
|
filter.CalledOnCollectionChanged[2].changedKind.Should().Be(ChangedKind.Remove);
|
||||||
|
filter.CalledOnCollectionChanged[2].value.Should().Be(10);
|
||||||
|
filter.CalledOnCollectionChanged[2].oldIndex.Should().Be(0);
|
||||||
|
filter.CalledOnCollectionChanged[3].changedKind.Should().Be(ChangedKind.Add);
|
||||||
|
filter.CalledOnCollectionChanged[3].value.Should().Be(999);
|
||||||
|
filter.CalledOnCollectionChanged[3].index.Should().Be(1);
|
||||||
|
}
|
||||||
|
}
|
@ -35,7 +35,7 @@ namespace ObservableCollections.Tests
|
|||||||
readonly Func<T, ViewContainer<T>, bool> filter;
|
readonly Func<T, ViewContainer<T>, bool> filter;
|
||||||
public List<(T, ViewContainer<T>)> CalledWhenTrue = new();
|
public List<(T, ViewContainer<T>)> CalledWhenTrue = new();
|
||||||
public List<(T, ViewContainer<T>)> CalledWhenFalse = new();
|
public List<(T, ViewContainer<T>)> CalledWhenFalse = new();
|
||||||
public List<(ChangedKind changedKind, T value, ViewContainer<T> view)> CalledOnCollectionChanged = new();
|
public List<(ChangedKind changedKind, T value, ViewContainer<T> view, int index, int oldIndex)> CalledOnCollectionChanged = new();
|
||||||
|
|
||||||
public TestFilter(Func<T, ViewContainer<T>, bool> filter)
|
public TestFilter(Func<T, ViewContainer<T>, bool> filter)
|
||||||
{
|
{
|
||||||
@ -56,7 +56,7 @@ namespace ObservableCollections.Tests
|
|||||||
|
|
||||||
public void OnCollectionChanged(ChangedKind changedKind, T value, ViewContainer<T> view, in NotifyCollectionChangedEventArgs<T> eventArgs)
|
public void OnCollectionChanged(ChangedKind changedKind, T value, ViewContainer<T> view, in NotifyCollectionChangedEventArgs<T> eventArgs)
|
||||||
{
|
{
|
||||||
CalledOnCollectionChanged.Add((changedKind, value, view));
|
CalledOnCollectionChanged.Add((changedKind, value, view, eventArgs.NewStartingIndex, eventArgs.OldStartingIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WhenTrue(T value, ViewContainer<T> view)
|
public void WhenTrue(T value, ViewContainer<T> view)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user