list
This commit is contained in:
parent
7e37cfc878
commit
6ee7fb7301
@ -60,16 +60,16 @@ class HogeFilter : ISynchronizedViewFilter<int>
|
||||
switch (eventArgs.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
eventArgs.NewView.Value += " Add";
|
||||
eventArgs.NewItem.View.Value += " Add";
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
eventArgs.OldView.Value += " Remove";
|
||||
eventArgs.OldItem.View.Value += " Remove";
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
eventArgs.NewView.Value += $" Move {eventArgs.OldViewIndex} {eventArgs.NewViewIndex}";
|
||||
eventArgs.NewItem.View.Value += $" Move {eventArgs.OldStartingIndex} {eventArgs.NewStartingIndex}";
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
eventArgs.NewView.Value += $" Replace {eventArgs.NewViewIndex}";
|
||||
eventArgs.NewItem.View.Value += $" Replace {eventArgs.NewStartingIndex}";
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
break;
|
||||
|
55
src/ObservableCollections/Internal/FixedArray.cs
Normal file
55
src/ObservableCollections/Internal/FixedArray.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace ObservableCollections.Internal
|
||||
{
|
||||
internal ref struct FixedArray<T>
|
||||
{
|
||||
public readonly Span<T> Span;
|
||||
T[]? array;
|
||||
|
||||
public FixedArray(int size)
|
||||
{
|
||||
array = ArrayPool<T>.Shared.Rent(size);
|
||||
Span = array.AsSpan(0, size);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (array != null)
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array, RuntimeHelpersEx.IsReferenceOrContainsReferences<T>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal ref struct FixedBoolArray
|
||||
{
|
||||
public const int StackallocSize = 128;
|
||||
|
||||
public readonly Span<bool> Span;
|
||||
bool[]? array;
|
||||
|
||||
public FixedBoolArray(Span<bool> scratchBuffer, int capacity)
|
||||
{
|
||||
if (scratchBuffer.Length == 0)
|
||||
{
|
||||
array = ArrayPool<bool>.Shared.Rent(capacity);
|
||||
Span = array.AsSpan(0, capacity);
|
||||
}
|
||||
else
|
||||
{
|
||||
Span = scratchBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (array != null)
|
||||
{
|
||||
ArrayPool<bool>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
30
src/ObservableCollections/Internal/RemoveAllMatcher.cs
Normal file
30
src/ObservableCollections/Internal/RemoveAllMatcher.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ObservableCollections.Internal
|
||||
{
|
||||
internal class RemoveAllMatcher<T>
|
||||
{
|
||||
readonly HashSet<T> hashSet;
|
||||
|
||||
public RemoveAllMatcher(ReadOnlySpan<T> source)
|
||||
{
|
||||
#if !NETSTANDARD2_0
|
||||
var set = new HashSet<T>(capacity: source.Length);
|
||||
#else
|
||||
var set = new HashSet<T>();
|
||||
#endif
|
||||
foreach (var item in source)
|
||||
{
|
||||
set.Add(item);
|
||||
}
|
||||
|
||||
this.hashSet = set;
|
||||
}
|
||||
|
||||
public bool Predicate(T value)
|
||||
{
|
||||
return hashSet.Contains(value);
|
||||
}
|
||||
}
|
||||
}
|
@ -15,6 +15,10 @@
|
||||
|
||||
<ItemGroup Condition="$(TargetFramework) == 'netstandard2.0'">
|
||||
<PackageReference Include="System.Memory" Version="4.5.4" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="$(TargetFramework) == 'netstandard2.1'">
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -94,7 +94,7 @@ namespace ObservableCollections
|
||||
}
|
||||
}
|
||||
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<KeyValuePair<TKey, TValue>, TView>(NotifyCollectionChangedAction.Reset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<KeyValuePair<TKey, TValue>, TView>(NotifyCollectionChangedAction.Reset, true));
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ namespace ObservableCollections
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>>.Null;
|
||||
this.filteredCount = dict.Count;
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<KeyValuePair<TKey, TValue>, TView>(NotifyCollectionChangedAction.Reset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<KeyValuePair<TKey, TValue>, TView>(NotifyCollectionChangedAction.Reset, true));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ namespace ObservableCollections
|
||||
filteredCount++;
|
||||
}
|
||||
}
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true));
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ namespace ObservableCollections
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T>.Null;
|
||||
this.filteredCount = dict.Count;
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,32 +195,7 @@ namespace ObservableCollections
|
||||
switch (e.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
// Add
|
||||
if (e.NewStartingIndex == list.Count)
|
||||
{
|
||||
if (e.IsSingleItem)
|
||||
{
|
||||
var v = (e.NewItem, selector(e.NewItem));
|
||||
list.Add(v);
|
||||
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, e.NewStartingIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
var i = e.NewStartingIndex;
|
||||
|
||||
using var array = new ResizableArray<(T, TView)>(e.NewItems.Length);
|
||||
foreach (var item in e.NewItems)
|
||||
{
|
||||
array.Add((item, selector(item)));
|
||||
}
|
||||
|
||||
list.AddRange(array.Span);
|
||||
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, i++);
|
||||
}
|
||||
}
|
||||
// Insert
|
||||
else
|
||||
{
|
||||
// Add or Insert
|
||||
if (e.IsSingleItem)
|
||||
{
|
||||
var v = (e.NewItem, selector(e.NewItem));
|
||||
@ -229,14 +204,32 @@ namespace ObservableCollections
|
||||
}
|
||||
else
|
||||
{
|
||||
var span = e.NewItems;
|
||||
for (var i = 0; i < span.Length; i++)
|
||||
var items = e.NewItems;
|
||||
var length = items.Length;
|
||||
|
||||
using var valueViews = new FixedArray<(T, TView)>(length);
|
||||
using var views = new FixedArray<TView>(length);
|
||||
using var matches = new FixedBoolArray(length < FixedBoolArray.StackallocSize ? stackalloc bool[length] : default, length);
|
||||
var isMatchAll = true;
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
var v = (span[i], selector(span[i]));
|
||||
list.Insert(e.NewStartingIndex + i, v); // should we use InsertRange?
|
||||
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, e.NewStartingIndex + i);
|
||||
var item = items[i];
|
||||
var view = selector(item);
|
||||
views.Span[i] = view;
|
||||
valueViews.Span[i] = (item, view);
|
||||
var isMatch = matches.Span[i] = Filter.IsMatch(item);
|
||||
if (isMatch)
|
||||
{
|
||||
filteredCount++; // increment in this process
|
||||
}
|
||||
else
|
||||
{
|
||||
isMatchAll = false;
|
||||
}
|
||||
}
|
||||
|
||||
list.InsertRange(e.NewStartingIndex, valueViews.Span);
|
||||
this.InvokeOnAddRange(ViewChanged, e.NewItems, views.Span, isMatchAll, matches.Span, e.NewStartingIndex);
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
@ -248,17 +241,32 @@ namespace ObservableCollections
|
||||
}
|
||||
else
|
||||
{
|
||||
list.RemoveRange(e.OldStartingIndex, e.OldItems.Length); // TODO: no
|
||||
|
||||
|
||||
// TODO: Range operation before remove...!
|
||||
|
||||
var len = e.OldStartingIndex + e.OldItems.Length;
|
||||
for (var i = e.OldStartingIndex; i < len; i++)
|
||||
var length = e.OldItems.Length;
|
||||
using var values = new FixedArray<T>(length);
|
||||
using var views = new FixedArray<TView>(length);
|
||||
using var matches = new FixedBoolArray(length < FixedBoolArray.StackallocSize ? stackalloc bool[length] : default, length);
|
||||
var isMatchAll = true;
|
||||
var to = e.OldStartingIndex + length;
|
||||
var j = 0;
|
||||
for (int i = e.OldStartingIndex; i < to; i++)
|
||||
{
|
||||
var v = list[i];
|
||||
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, e.OldStartingIndex + i);
|
||||
var item = list[i];
|
||||
values.Span[j] = item.Item1;
|
||||
views.Span[j] = item.Item2;
|
||||
var isMatch = matches.Span[j] = Filter.IsMatch(item.Item1);
|
||||
if (isMatch)
|
||||
{
|
||||
filteredCount--; // decrement in this process
|
||||
}
|
||||
else
|
||||
{
|
||||
isMatchAll = false;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
list.RemoveRange(e.OldStartingIndex, e.OldItems.Length);
|
||||
this.InvokeOnRemoveRange(ViewChanged, values.Span, views.Span, isMatchAll, matches.Span, e.OldStartingIndex);
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
@ -290,14 +298,13 @@ namespace ObservableCollections
|
||||
{
|
||||
// Reverse
|
||||
list.Reverse(e.SortOperation.Index, e.SortOperation.Count);
|
||||
// TODO:Invoke
|
||||
this.InvokeOnReverseOrSort(ViewChanged, e.SortOperation);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sort
|
||||
list.Sort(e.SortOperation.Index, e.SortOperation.Count, new IgnoreViewComparer(e.SortOperation.Comparer ?? Comparer<T>.Default));
|
||||
// Span<T> d;
|
||||
|
||||
this.InvokeOnReverseOrSort(ViewChanged, e.SortOperation);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -89,7 +89,7 @@ namespace ObservableCollections
|
||||
filteredCount++;
|
||||
}
|
||||
}
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true));
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ namespace ObservableCollections
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T>.Null;
|
||||
this.filteredCount = queue.Count;
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ namespace ObservableCollections
|
||||
filteredCount++;
|
||||
}
|
||||
}
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true));
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ namespace ObservableCollections
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T>.Null;
|
||||
this.filteredCount = ringBuffer.Count;
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ namespace ObservableCollections
|
||||
filteredCount++;
|
||||
}
|
||||
}
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true));
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,7 +98,7 @@ namespace ObservableCollections
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T>.Null;
|
||||
this.filteredCount = stack.Count;
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,8 @@ namespace System.Collections.Generic
|
||||
{
|
||||
internal static class CollectionExtensions
|
||||
{
|
||||
const int ArrayMaxLength = 0X7FFFFFC7;
|
||||
|
||||
public static void Deconstruct<TKey, TValue>(this KeyValuePair<TKey, TValue> kvp, out TKey key, out TValue value)
|
||||
{
|
||||
key = kvp.Key;
|
||||
@ -108,7 +110,7 @@ namespace System.Collections.Generic
|
||||
{
|
||||
int newCapacity = list._items.Length == 0 ? 4 : 2 * list._items.Length;
|
||||
|
||||
if ((uint)newCapacity > Array.MaxLength) newCapacity = Array.MaxLength;
|
||||
if ((uint)newCapacity > ArrayMaxLength) newCapacity = ArrayMaxLength;
|
||||
|
||||
if (newCapacity < capacity) newCapacity = capacity;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Data;
|
||||
|
||||
namespace ObservableCollections
|
||||
{
|
||||
@ -10,7 +11,8 @@ namespace ObservableCollections
|
||||
(T Value, TView View) oldItem = default!,
|
||||
ReadOnlySpan<T> newValues = default!,
|
||||
ReadOnlySpan<TView> newViews = default!,
|
||||
ReadOnlySpan<(T Value, TView View)> oldItems = default!,
|
||||
ReadOnlySpan<T> oldValues = default!,
|
||||
ReadOnlySpan<TView> oldViews = default!,
|
||||
int newStartingIndex = -1,
|
||||
int oldStartingIndex = -1,
|
||||
SortOperation<T> sortOperation = default)
|
||||
@ -21,7 +23,8 @@ namespace ObservableCollections
|
||||
public readonly (T Value, TView View) OldItem = oldItem;
|
||||
public readonly ReadOnlySpan<T> NewValues = newValues;
|
||||
public readonly ReadOnlySpan<TView> NewViews = newViews;
|
||||
public readonly ReadOnlySpan<(T Value, TView View)> OldItems = oldItems;
|
||||
public readonly ReadOnlySpan<T> OldValues = oldValues;
|
||||
public readonly ReadOnlySpan<TView> OldViews = oldViews;
|
||||
public readonly int NewStartingIndex = newStartingIndex;
|
||||
public readonly int OldStartingIndex = oldStartingIndex;
|
||||
public readonly SortOperation<T> SortOperation = sortOperation;
|
||||
@ -57,15 +60,25 @@ namespace ObservableCollections
|
||||
}
|
||||
}
|
||||
|
||||
internal static void InvokeOnAddRange<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, ReadOnlySpan<T> values, ReadOnlySpan<TView> views, int index)
|
||||
internal static void InvokeOnAddRange<T, TView>(this ISynchronizedView<T, TView> collection, NotifyViewChangedEventHandler<T, TView>? ev, ReadOnlySpan<T> values, ReadOnlySpan<TView> views, bool isMatchAll, ReadOnlySpan<bool> matches, int index)
|
||||
{
|
||||
var isMatch = collection.Filter.IsMatch(value);
|
||||
if (isMatch)
|
||||
{
|
||||
filteredCount++;
|
||||
if (ev != null)
|
||||
{
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, false, newValues: values, newViews: views, newStartingIndex: index));
|
||||
if (isMatchAll)
|
||||
{
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, isSingleItem: false, newValues: values, newViews: views, newStartingIndex: index));
|
||||
}
|
||||
else
|
||||
{
|
||||
var startingIndex = index;
|
||||
for (var i = 0; i < matches.Length; i++)
|
||||
{
|
||||
if (matches[i])
|
||||
{
|
||||
var item = (values[i], views[i]);
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, isSingleItem: true, newItem: item, newStartingIndex: startingIndex++));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -83,7 +96,35 @@ namespace ObservableCollections
|
||||
filteredCount--;
|
||||
if (ev != null)
|
||||
{
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, oldValue: value, oldView: view, oldViewIndex: oldIndex));
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, true, oldItem: (value, view), oldStartingIndex: oldIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only use for ObservableList
|
||||
internal static void InvokeOnRemoveRange<T, TView>(this ISynchronizedView<T, TView> collection, NotifyViewChangedEventHandler<T, TView>? ev, ReadOnlySpan<T> values, ReadOnlySpan<TView> views, bool isMatchAll, ReadOnlySpan<bool> matches, int index)
|
||||
{
|
||||
if (ev != null)
|
||||
{
|
||||
if (isMatchAll)
|
||||
{
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, isSingleItem: false, oldValues: values, oldViews: views, oldStartingIndex: index));
|
||||
}
|
||||
else
|
||||
{
|
||||
var startingIndex = index;
|
||||
for (var i = 0; i < matches.Length; i++)
|
||||
{
|
||||
if (matches[i])
|
||||
{
|
||||
var item = (values[i], views[i]);
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, isSingleItem: true, oldItem: item, oldStartingIndex: index)); //remove for list, always same index
|
||||
}
|
||||
else
|
||||
{
|
||||
index++; // not matched, skip index
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -101,7 +142,7 @@ namespace ObservableCollections
|
||||
var isMatch = collection.Filter.IsMatch(value);
|
||||
if (isMatch)
|
||||
{
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Move, newValue: value, newView: view, newViewIndex: index, oldViewIndex: oldIndex));
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Move, true, newItem: (value, view), newStartingIndex: index, oldStartingIndex: oldIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -121,7 +162,7 @@ namespace ObservableCollections
|
||||
{
|
||||
if (ev != null)
|
||||
{
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Replace, newValue: value, newView: view, oldValue: oldValue, oldView: oldView, newViewIndex: index, oldViewIndex: oldIndex >= 0 ? oldIndex : index));
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Replace, true, newItem: (value, view), oldItem: (oldValue, oldView), newStartingIndex: index, oldStartingIndex: oldIndex >= 0 ? oldIndex : index));
|
||||
}
|
||||
}
|
||||
else if (oldMatched)
|
||||
@ -130,7 +171,7 @@ namespace ObservableCollections
|
||||
filteredCount--;
|
||||
if (ev != null)
|
||||
{
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, oldValue: value, oldView: view, oldViewIndex: oldIndex));
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, true, oldItem: (value, view), oldStartingIndex: oldIndex));
|
||||
}
|
||||
|
||||
}
|
||||
@ -140,7 +181,7 @@ namespace ObservableCollections
|
||||
filteredCount++;
|
||||
if (ev != null)
|
||||
{
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, newValue: value, newView: view, newViewIndex: index));
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, true, newItem: (value, view), newStartingIndex: index));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -150,7 +191,15 @@ namespace ObservableCollections
|
||||
filteredCount = 0;
|
||||
if (ev != null)
|
||||
{
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true));
|
||||
}
|
||||
}
|
||||
|
||||
internal static void InvokeOnReverseOrSort<T, TView>(this ISynchronizedView<T, TView> collection, NotifyViewChangedEventHandler<T, TView>? ev, SortOperation<T> sortOperation)
|
||||
{
|
||||
if (ev != null)
|
||||
{
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true, sortOperation: sortOperation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using ObservableCollections.Internal;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
@ -8,10 +9,19 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace ObservableCollections
|
||||
{
|
||||
|
||||
internal class SynchronizedViewList<T, TView> : ISynchronizedViewList<TView>
|
||||
{
|
||||
readonly ISynchronizedView<T, TView> parent;
|
||||
protected readonly List<TView> listView;
|
||||
|
||||
|
||||
|
||||
|
||||
//protected readonly SortedList<int, TView> listView; // key is original index
|
||||
|
||||
|
||||
|
||||
protected readonly object gate = new object();
|
||||
|
||||
public SynchronizedViewList(ISynchronizedView<T, TView> parent)
|
||||
@ -70,40 +80,38 @@ namespace ObservableCollections
|
||||
{
|
||||
if (e.OldStartingIndex == -1)
|
||||
{
|
||||
// TODO:...
|
||||
//listView.RemoveAll(
|
||||
|
||||
// e.OldItems
|
||||
var matcher = new RemoveAllMatcher<TView>(e.OldViews);
|
||||
listView.RemoveAll(matcher.Predicate);
|
||||
}
|
||||
else
|
||||
{
|
||||
listView.RemoveRange(e.OldStartingIndex, e.OldItems.Length);
|
||||
listView.RemoveRange(e.OldStartingIndex, e.OldViews.Length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Replace: // Indexer
|
||||
if (e.NewViewIndex == -1)
|
||||
if (e.NewStartingIndex == -1)
|
||||
{
|
||||
var index = listView.IndexOf(e.OldView);
|
||||
listView[index] = e.NewView;
|
||||
var index = listView.IndexOf(e.OldItem.View);
|
||||
listView[index] = e.NewItem.View;
|
||||
}
|
||||
else
|
||||
{
|
||||
listView[e.NewViewIndex] = e.NewView;
|
||||
listView[e.NewStartingIndex] = e.NewItem.View;
|
||||
}
|
||||
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Move: //Remove and Insert
|
||||
if (e.NewViewIndex == -1)
|
||||
if (e.NewStartingIndex == -1)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
listView.RemoveAt(e.OldViewIndex);
|
||||
listView.Insert(e.NewViewIndex, e.NewView);
|
||||
listView.RemoveAt(e.OldStartingIndex);
|
||||
listView.Insert(e.NewStartingIndex, e.NewItem.View);
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Reset: // Clear or drastic changes
|
||||
@ -121,16 +129,18 @@ namespace ObservableCollections
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
#pragma warning disable CS0436
|
||||
if (parent is ObservableList<T>.View<TView> observableListView)
|
||||
{
|
||||
#pragma warning disable CS0436
|
||||
var comparer = new ObservableList<T>.View<TView>.IgnoreViewComparer(e.SortOperation.Comparer ?? Comparer<T>.Default);
|
||||
var viewSpan = CollectionsMarshal.AsSpan(listView).Slice(e.SortOperation.Index, e.SortOperation.Count);
|
||||
var sourceSpan = CollectionsMarshal.AsSpan(observableListView.list).Slice(e.SortOperation.Index, e.SortOperation.Count);
|
||||
sourceSpan.Sort(viewSpan, comparer);
|
||||
#pragma warning restore CS0436
|
||||
sourceSpan.Sort(viewSpan, comparer); // span.Sort is NET6 or greater
|
||||
}
|
||||
else
|
||||
#pragma warning restore CS0436
|
||||
#endif
|
||||
{
|
||||
// can not get source Span, do Clear and Refresh
|
||||
listView.Clear();
|
||||
@ -223,22 +233,48 @@ namespace ObservableCollections
|
||||
switch (args.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Add, args.NewView, args.NewViewIndex)
|
||||
if (args.IsSingleItem)
|
||||
{
|
||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Add, args.NewItem.View, args.NewStartingIndex)
|
||||
{
|
||||
Collection = this,
|
||||
Invoker = raiseChangedEventInvoke,
|
||||
IsInvokeCollectionChanged = true,
|
||||
IsInvokePropertyChanged = true
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Add, args.NewViews.ToArray(), args.NewStartingIndex)
|
||||
{
|
||||
Collection = this,
|
||||
Invoker = raiseChangedEventInvoke,
|
||||
IsInvokeCollectionChanged = true,
|
||||
IsInvokePropertyChanged = true
|
||||
});
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Remove, args.OldView, args.OldViewIndex)
|
||||
if (args.IsSingleItem)
|
||||
{
|
||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Remove, args.OldItem.View, args.OldStartingIndex)
|
||||
{
|
||||
Collection = this,
|
||||
Invoker = raiseChangedEventInvoke,
|
||||
IsInvokeCollectionChanged = true,
|
||||
IsInvokePropertyChanged = true
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Remove, args.OldViews.ToArray(), args.OldStartingIndex)
|
||||
{
|
||||
Collection = this,
|
||||
Invoker = raiseChangedEventInvoke,
|
||||
IsInvokeCollectionChanged = true,
|
||||
IsInvokePropertyChanged = true
|
||||
});
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Reset)
|
||||
@ -250,7 +286,7 @@ namespace ObservableCollections
|
||||
});
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Replace, args.NewView, args.OldView, args.NewViewIndex)
|
||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Replace, args.NewItem.View, args.OldItem.View, args.NewStartingIndex)
|
||||
{
|
||||
Collection = this,
|
||||
Invoker = raiseChangedEventInvoke,
|
||||
@ -259,7 +295,7 @@ namespace ObservableCollections
|
||||
});
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Move, args.NewView, args.NewViewIndex, args.OldViewIndex)
|
||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Move, args.NewItem.View, args.NewStartingIndex, args.OldStartingIndex)
|
||||
{
|
||||
Collection = this,
|
||||
Invoker = raiseChangedEventInvoke,
|
||||
|
Loading…
x
Reference in New Issue
Block a user