list
This commit is contained in:
parent
7e37cfc878
commit
6ee7fb7301
@ -60,16 +60,16 @@ class HogeFilter : ISynchronizedViewFilter<int>
|
|||||||
switch (eventArgs.Action)
|
switch (eventArgs.Action)
|
||||||
{
|
{
|
||||||
case NotifyCollectionChangedAction.Add:
|
case NotifyCollectionChangedAction.Add:
|
||||||
eventArgs.NewView.Value += " Add";
|
eventArgs.NewItem.View.Value += " Add";
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Remove:
|
case NotifyCollectionChangedAction.Remove:
|
||||||
eventArgs.OldView.Value += " Remove";
|
eventArgs.OldItem.View.Value += " Remove";
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Move:
|
case NotifyCollectionChangedAction.Move:
|
||||||
eventArgs.NewView.Value += $" Move {eventArgs.OldViewIndex} {eventArgs.NewViewIndex}";
|
eventArgs.NewItem.View.Value += $" Move {eventArgs.OldStartingIndex} {eventArgs.NewStartingIndex}";
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Replace:
|
case NotifyCollectionChangedAction.Replace:
|
||||||
eventArgs.NewView.Value += $" Replace {eventArgs.NewViewIndex}";
|
eventArgs.NewItem.View.Value += $" Replace {eventArgs.NewStartingIndex}";
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Reset:
|
case NotifyCollectionChangedAction.Reset:
|
||||||
break;
|
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'">
|
<ItemGroup Condition="$(TargetFramework) == 'netstandard2.0'">
|
||||||
<PackageReference Include="System.Memory" Version="4.5.4" />
|
<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>
|
||||||
|
|
||||||
<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.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>>.Null;
|
||||||
this.filteredCount = dict.Count;
|
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++;
|
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.filter = SynchronizedViewFilter<T>.Null;
|
||||||
this.filteredCount = dict.Count;
|
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)
|
switch (e.Action)
|
||||||
{
|
{
|
||||||
case NotifyCollectionChangedAction.Add:
|
case NotifyCollectionChangedAction.Add:
|
||||||
// Add
|
// Add or Insert
|
||||||
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
|
|
||||||
{
|
|
||||||
if (e.IsSingleItem)
|
if (e.IsSingleItem)
|
||||||
{
|
{
|
||||||
var v = (e.NewItem, selector(e.NewItem));
|
var v = (e.NewItem, selector(e.NewItem));
|
||||||
@ -229,14 +204,32 @@ namespace ObservableCollections
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var span = e.NewItems;
|
var items = e.NewItems;
|
||||||
for (var i = 0; i < span.Length; i++)
|
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]));
|
var item = items[i];
|
||||||
list.Insert(e.NewStartingIndex + i, v); // should we use InsertRange?
|
var view = selector(item);
|
||||||
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, e.NewStartingIndex + i);
|
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;
|
break;
|
||||||
case NotifyCollectionChangedAction.Remove:
|
case NotifyCollectionChangedAction.Remove:
|
||||||
@ -248,17 +241,32 @@ namespace ObservableCollections
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
list.RemoveRange(e.OldStartingIndex, e.OldItems.Length); // TODO: no
|
var length = e.OldItems.Length;
|
||||||
|
using var values = new FixedArray<T>(length);
|
||||||
|
using var views = new FixedArray<TView>(length);
|
||||||
// TODO: Range operation before remove...!
|
using var matches = new FixedBoolArray(length < FixedBoolArray.StackallocSize ? stackalloc bool[length] : default, length);
|
||||||
|
var isMatchAll = true;
|
||||||
var len = e.OldStartingIndex + e.OldItems.Length;
|
var to = e.OldStartingIndex + length;
|
||||||
for (var i = e.OldStartingIndex; i < len; i++)
|
var j = 0;
|
||||||
|
for (int i = e.OldStartingIndex; i < to; i++)
|
||||||
{
|
{
|
||||||
var v = list[i];
|
var item = list[i];
|
||||||
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, e.OldStartingIndex + 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;
|
break;
|
||||||
case NotifyCollectionChangedAction.Replace:
|
case NotifyCollectionChangedAction.Replace:
|
||||||
@ -290,14 +298,13 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
// Reverse
|
// Reverse
|
||||||
list.Reverse(e.SortOperation.Index, e.SortOperation.Count);
|
list.Reverse(e.SortOperation.Index, e.SortOperation.Count);
|
||||||
// TODO:Invoke
|
this.InvokeOnReverseOrSort(ViewChanged, e.SortOperation);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Sort
|
// Sort
|
||||||
list.Sort(e.SortOperation.Index, e.SortOperation.Count, new IgnoreViewComparer(e.SortOperation.Comparer ?? Comparer<T>.Default));
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -89,7 +89,7 @@ namespace ObservableCollections
|
|||||||
filteredCount++;
|
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.filter = SynchronizedViewFilter<T>.Null;
|
||||||
this.filteredCount = queue.Count;
|
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++;
|
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.filter = SynchronizedViewFilter<T>.Null;
|
||||||
this.filteredCount = ringBuffer.Count;
|
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++;
|
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.filter = SynchronizedViewFilter<T>.Null;
|
||||||
this.filteredCount = stack.Count;
|
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
|
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)
|
public static void Deconstruct<TKey, TValue>(this KeyValuePair<TKey, TValue> kvp, out TKey key, out TValue value)
|
||||||
{
|
{
|
||||||
key = kvp.Key;
|
key = kvp.Key;
|
||||||
@ -108,7 +110,7 @@ namespace System.Collections.Generic
|
|||||||
{
|
{
|
||||||
int newCapacity = list._items.Length == 0 ? 4 : 2 * list._items.Length;
|
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;
|
if (newCapacity < capacity) newCapacity = capacity;
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
namespace ObservableCollections
|
namespace ObservableCollections
|
||||||
{
|
{
|
||||||
@ -10,7 +11,8 @@ namespace ObservableCollections
|
|||||||
(T Value, TView View) oldItem = default!,
|
(T Value, TView View) oldItem = default!,
|
||||||
ReadOnlySpan<T> newValues = default!,
|
ReadOnlySpan<T> newValues = default!,
|
||||||
ReadOnlySpan<TView> newViews = default!,
|
ReadOnlySpan<TView> newViews = default!,
|
||||||
ReadOnlySpan<(T Value, TView View)> oldItems = default!,
|
ReadOnlySpan<T> oldValues = default!,
|
||||||
|
ReadOnlySpan<TView> oldViews = default!,
|
||||||
int newStartingIndex = -1,
|
int newStartingIndex = -1,
|
||||||
int oldStartingIndex = -1,
|
int oldStartingIndex = -1,
|
||||||
SortOperation<T> sortOperation = default)
|
SortOperation<T> sortOperation = default)
|
||||||
@ -21,7 +23,8 @@ namespace ObservableCollections
|
|||||||
public readonly (T Value, TView View) OldItem = oldItem;
|
public readonly (T Value, TView View) OldItem = oldItem;
|
||||||
public readonly ReadOnlySpan<T> NewValues = newValues;
|
public readonly ReadOnlySpan<T> NewValues = newValues;
|
||||||
public readonly ReadOnlySpan<TView> NewViews = newViews;
|
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 NewStartingIndex = newStartingIndex;
|
||||||
public readonly int OldStartingIndex = oldStartingIndex;
|
public readonly int OldStartingIndex = oldStartingIndex;
|
||||||
public readonly SortOperation<T> SortOperation = sortOperation;
|
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)
|
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--;
|
filteredCount--;
|
||||||
if (ev != null)
|
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);
|
var isMatch = collection.Filter.IsMatch(value);
|
||||||
if (isMatch)
|
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)
|
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)
|
else if (oldMatched)
|
||||||
@ -130,7 +171,7 @@ namespace ObservableCollections
|
|||||||
filteredCount--;
|
filteredCount--;
|
||||||
if (ev != null)
|
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++;
|
filteredCount++;
|
||||||
if (ev != null)
|
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;
|
filteredCount = 0;
|
||||||
if (ev != null)
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
@ -8,10 +9,19 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace ObservableCollections
|
namespace ObservableCollections
|
||||||
{
|
{
|
||||||
|
|
||||||
internal class SynchronizedViewList<T, TView> : ISynchronizedViewList<TView>
|
internal class SynchronizedViewList<T, TView> : ISynchronizedViewList<TView>
|
||||||
{
|
{
|
||||||
readonly ISynchronizedView<T, TView> parent;
|
readonly ISynchronizedView<T, TView> parent;
|
||||||
protected readonly List<TView> listView;
|
protected readonly List<TView> listView;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//protected readonly SortedList<int, TView> listView; // key is original index
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected readonly object gate = new object();
|
protected readonly object gate = new object();
|
||||||
|
|
||||||
public SynchronizedViewList(ISynchronizedView<T, TView> parent)
|
public SynchronizedViewList(ISynchronizedView<T, TView> parent)
|
||||||
@ -70,40 +80,38 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
if (e.OldStartingIndex == -1)
|
if (e.OldStartingIndex == -1)
|
||||||
{
|
{
|
||||||
// TODO:...
|
var matcher = new RemoveAllMatcher<TView>(e.OldViews);
|
||||||
//listView.RemoveAll(
|
listView.RemoveAll(matcher.Predicate);
|
||||||
|
|
||||||
// e.OldItems
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
listView.RemoveRange(e.OldStartingIndex, e.OldItems.Length);
|
listView.RemoveRange(e.OldStartingIndex, e.OldViews.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Replace: // Indexer
|
case NotifyCollectionChangedAction.Replace: // Indexer
|
||||||
if (e.NewViewIndex == -1)
|
if (e.NewStartingIndex == -1)
|
||||||
{
|
{
|
||||||
var index = listView.IndexOf(e.OldView);
|
var index = listView.IndexOf(e.OldItem.View);
|
||||||
listView[index] = e.NewView;
|
listView[index] = e.NewItem.View;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
listView[e.NewViewIndex] = e.NewView;
|
listView[e.NewStartingIndex] = e.NewItem.View;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Move: //Remove and Insert
|
case NotifyCollectionChangedAction.Move: //Remove and Insert
|
||||||
if (e.NewViewIndex == -1)
|
if (e.NewStartingIndex == -1)
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
listView.RemoveAt(e.OldViewIndex);
|
listView.RemoveAt(e.OldStartingIndex);
|
||||||
listView.Insert(e.NewViewIndex, e.NewView);
|
listView.Insert(e.NewStartingIndex, e.NewItem.View);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Reset: // Clear or drastic changes
|
case NotifyCollectionChangedAction.Reset: // Clear or drastic changes
|
||||||
@ -121,16 +129,18 @@ namespace ObservableCollections
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
#pragma warning disable CS0436
|
||||||
if (parent is ObservableList<T>.View<TView> observableListView)
|
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 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 viewSpan = CollectionsMarshal.AsSpan(listView).Slice(e.SortOperation.Index, e.SortOperation.Count);
|
||||||
var sourceSpan = CollectionsMarshal.AsSpan(observableListView.list).Slice(e.SortOperation.Index, e.SortOperation.Count);
|
var sourceSpan = CollectionsMarshal.AsSpan(observableListView.list).Slice(e.SortOperation.Index, e.SortOperation.Count);
|
||||||
sourceSpan.Sort(viewSpan, comparer);
|
sourceSpan.Sort(viewSpan, comparer); // span.Sort is NET6 or greater
|
||||||
#pragma warning restore CS0436
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
#pragma warning restore CS0436
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
// can not get source Span, do Clear and Refresh
|
// can not get source Span, do Clear and Refresh
|
||||||
listView.Clear();
|
listView.Clear();
|
||||||
@ -223,22 +233,48 @@ namespace ObservableCollections
|
|||||||
switch (args.Action)
|
switch (args.Action)
|
||||||
{
|
{
|
||||||
case NotifyCollectionChangedAction.Add:
|
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,
|
Collection = this,
|
||||||
Invoker = raiseChangedEventInvoke,
|
Invoker = raiseChangedEventInvoke,
|
||||||
IsInvokeCollectionChanged = true,
|
IsInvokeCollectionChanged = true,
|
||||||
IsInvokePropertyChanged = true
|
IsInvokePropertyChanged = true
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Add, args.NewViews.ToArray(), args.NewStartingIndex)
|
||||||
|
{
|
||||||
|
Collection = this,
|
||||||
|
Invoker = raiseChangedEventInvoke,
|
||||||
|
IsInvokeCollectionChanged = true,
|
||||||
|
IsInvokePropertyChanged = true
|
||||||
|
});
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Remove:
|
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,
|
Collection = this,
|
||||||
Invoker = raiseChangedEventInvoke,
|
Invoker = raiseChangedEventInvoke,
|
||||||
IsInvokeCollectionChanged = true,
|
IsInvokeCollectionChanged = true,
|
||||||
IsInvokePropertyChanged = true
|
IsInvokePropertyChanged = true
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Remove, args.OldViews.ToArray(), args.OldStartingIndex)
|
||||||
|
{
|
||||||
|
Collection = this,
|
||||||
|
Invoker = raiseChangedEventInvoke,
|
||||||
|
IsInvokeCollectionChanged = true,
|
||||||
|
IsInvokePropertyChanged = true
|
||||||
|
});
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Reset:
|
case NotifyCollectionChangedAction.Reset:
|
||||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Reset)
|
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Reset)
|
||||||
@ -250,7 +286,7 @@ namespace ObservableCollections
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Replace:
|
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,
|
Collection = this,
|
||||||
Invoker = raiseChangedEventInvoke,
|
Invoker = raiseChangedEventInvoke,
|
||||||
@ -259,7 +295,7 @@ namespace ObservableCollections
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Move:
|
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,
|
Collection = this,
|
||||||
Invoker = raiseChangedEventInvoke,
|
Invoker = raiseChangedEventInvoke,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user