more changed wip

This commit is contained in:
neuecc 2024-08-28 03:09:29 +09:00
parent 5b3eb80158
commit 7eef45cadb
8 changed files with 486 additions and 152 deletions

View File

@ -3,24 +3,6 @@ using System.Collections.Specialized;
namespace ObservableCollections
{
public readonly ref struct SynchronizedViewChangedEventArgs<T, TView>(
NotifyCollectionChangedAction action,
T newValue = default!,
T oldValue = default!,
TView newView = default!,
TView oldView = default!,
int newViewIndex = -1,
int oldViewIndex = -1)
{
public readonly NotifyCollectionChangedAction Action = action;
public readonly T NewValue = newValue;
public readonly T OldValue = oldValue;
public readonly TView NewView = newView;
public readonly TView OldView = oldView;
public readonly int NewViewIndex = newViewIndex;
public readonly int OldViewIndex = oldViewIndex;
}
public interface ISynchronizedViewFilter<T>
{
bool IsMatch(T value);
@ -38,118 +20,5 @@ namespace ObservableCollections
}
}
public static class SynchronizedViewFilterExtensions
{
public static void AttachFilter<T, TView>(this ISynchronizedView<T, TView> source, Func<T, bool> filter)
{
source.AttachFilter(new SynchronizedViewFilter<T>(filter));
}
public static bool IsNullFilter<T>(this ISynchronizedViewFilter<T> filter)
{
return filter == SynchronizedViewFilter<T>.Null;
}
internal static void InvokeOnAdd<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, (T value, TView view) value, int index)
{
InvokeOnAdd(collection, ref filteredCount, ev, value.value, value.view, index);
}
internal static void InvokeOnAdd<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, T value, TView view, int index)
{
var isMatch = collection.Filter.IsMatch(value);
if (isMatch)
{
filteredCount++;
if (ev != null)
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, newValue: value, newView: view, newViewIndex: index));
}
}
}
internal static void InvokeOnRemove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, (T value, TView view) value, int oldIndex)
{
InvokeOnRemove(collection, ref filteredCount, ev, value.value, value.view, oldIndex);
}
internal static void InvokeOnRemove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, T value, TView view, int oldIndex)
{
var isMatch = collection.Filter.IsMatch(value);
if (isMatch)
{
filteredCount--;
if (ev != null)
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, oldValue: value, oldView: view, oldViewIndex: oldIndex));
}
}
}
internal static void InvokeOnMove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, (T value, TView view) value, int index, int oldIndex)
{
InvokeOnMove(collection, ref filteredCount, ev, value.value, value.view, index, oldIndex);
}
internal static void InvokeOnMove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, T value, TView view, int index, int oldIndex)
{
if (ev != null)
{
// move does not changes filtered-count
var isMatch = collection.Filter.IsMatch(value);
if (isMatch)
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Move, newValue: value, newView: view, newViewIndex: index, oldViewIndex: oldIndex));
}
}
}
internal static void InvokeOnReplace<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, (T value, TView view) value, (T value, TView view) oldValue, int index, int oldIndex = -1)
{
InvokeOnReplace(collection, ref filteredCount, ev, value.value, value.view, oldValue.value, oldValue.view, index, oldIndex);
}
internal static void InvokeOnReplace<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, T value, TView view, T oldValue, TView oldView, int index, int oldIndex = -1)
{
var oldMatched = collection.Filter.IsMatch(oldValue);
var newMatched = collection.Filter.IsMatch(value);
var bothMatched = oldMatched && newMatched;
if (bothMatched)
{
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));
}
}
else if (oldMatched)
{
// only-old is remove
filteredCount--;
if (ev != null)
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, oldValue: value, oldView: view, oldViewIndex: oldIndex));
}
}
else if (newMatched)
{
// only-new is add
filteredCount++;
if (ev != null)
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, newValue: value, newView: view, newViewIndex: index));
}
}
}
internal static void InvokeOnReset<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev)
{
filteredCount = 0;
if (ev != null)
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
}
}
}
}

View File

@ -1,9 +1,54 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
namespace ObservableCollections
{
// default(SortOperation<T>) == IsNull
public readonly struct SortOperation<T>
{
public readonly int Index;
public readonly int Count;
public readonly IComparer<T>? Comparer;
public bool IsReverse => Comparer == ReverseSentinel.Instance;
public bool IsNull => Comparer == null;
public SortOperation(int index, int count, IComparer<T>? comparer)
{
Index = index;
Count = count;
Comparer = comparer ?? NullComparerSentinel.Instance;
}
public static SortOperation<T> CreateReverse(int index, int count)
{
return new SortOperation<T>(index, count, ReverseSentinel.Instance);
}
sealed class ReverseSentinel : IComparer<T>
{
public static IComparer<T> Instance = new ReverseSentinel();
public int Compare(T? x, T? y)
{
throw new NotImplementedException();
}
}
sealed class NullComparerSentinel : IComparer<T>
{
public static IComparer<T> Instance = new NullComparerSentinel();
public int Compare(T? x, T? y)
{
throw new NotImplementedException();
}
}
}
/// <summary>
/// Contract:
/// IsSingleItem ? (NewItem, OldItem) : (NewItems, OldItems)
@ -16,7 +61,7 @@ namespace ObservableCollections
/// Action.Move
/// NewStartingIndex, OldStartingIndex
/// Action.Reset
/// -
/// SortOperation(IsNull, IsReverse, Comparer)
/// </summary>
[StructLayout(LayoutKind.Auto)]
public readonly ref struct NotifyCollectionChangedEventArgs<T>
@ -29,8 +74,9 @@ namespace ObservableCollections
public readonly ReadOnlySpan<T> OldItems;
public readonly int NewStartingIndex;
public readonly int OldStartingIndex;
public readonly SortOperation<T> SortOperation;
public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, bool isSingleItem, T newItem = default!, T oldItem = default!, ReadOnlySpan<T> newItems = default, ReadOnlySpan<T> oldItems = default, int newStartingIndex = -1, int oldStartingIndex = -1)
public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, bool isSingleItem, T newItem = default!, T oldItem = default!, ReadOnlySpan<T> newItems = default, ReadOnlySpan<T> oldItems = default, int newStartingIndex = -1, int oldStartingIndex = -1, SortOperation<T> sortOperation = default)
{
Action = action;
IsSingleItem = isSingleItem;
@ -40,6 +86,7 @@ namespace ObservableCollections
OldItems = oldItems;
NewStartingIndex = newStartingIndex;
OldStartingIndex = oldStartingIndex;
SortOperation = sortOperation;
}
public static NotifyCollectionChangedEventArgs<T> Add(T newItem, int newStartingIndex)
@ -81,5 +128,15 @@ namespace ObservableCollections
{
return new NotifyCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Reset, true);
}
public static NotifyCollectionChangedEventArgs<T> Reverse(int index, int count)
{
return new NotifyCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Reset, true, sortOperation: SortOperation<T>.CreateReverse(index, count));
}
public static NotifyCollectionChangedEventArgs<T> Sort(int index, int count, IComparer<T>? comparer)
{
return new NotifyCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Reset, true, sortOperation: new SortOperation<T>(index, count, comparer));
}
}
}

View File

@ -41,7 +41,7 @@ namespace ObservableCollections
readonly ObservableList<T> source;
readonly Func<T, TView> selector;
readonly List<(T, TView)> list;
internal readonly List<(T, TView)> list; // unsafe, be careful to use
int filteredCount;
ISynchronizedViewFilter<T> filter;
@ -207,6 +207,10 @@ namespace ObservableCollections
else
{
var i = e.NewStartingIndex;
//new CloneCollection<(T, TView)>();
using var array = new ResizableArray<(T, TView)>(e.NewItems.Length);
foreach (var item in e.NewItems)
{
var v = (item, selector(item));
@ -245,7 +249,10 @@ namespace ObservableCollections
}
else
{
list.RemoveRange(e.OldStartingIndex, e.OldItems.Length); // remove from list first
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++)
@ -274,8 +281,25 @@ namespace ObservableCollections
}
break;
case NotifyCollectionChangedAction.Reset:
list.Clear();
this.InvokeOnReset(ref filteredCount, ViewChanged);
if (e.SortOperation.IsNull)
{
// None(Clear)
list.Clear();
this.InvokeOnReset(ref filteredCount, ViewChanged);
}
else if (e.SortOperation.IsReverse)
{
// Reverse
list.Reverse(e.SortOperation.Index, e.SortOperation.Count);
// TODO:Invoke
}
else
{
// Sort
list.Sort(e.SortOperation.Index, e.SortOperation.Count, new IgnoreViewComparer(e.SortOperation.Comparer ?? Comparer<T>.Default));
// Span<T> d;
}
break;
default:
break;
@ -284,6 +308,21 @@ namespace ObservableCollections
CollectionStateChanged?.Invoke(e.Action);
}
}
internal sealed class IgnoreViewComparer : IComparer<(T, TView)>
{
readonly IComparer<T> comparer;
public IgnoreViewComparer(IComparer<T> comparer)
{
this.comparer = comparer;
}
public int Compare((T, TView) x, (T, TView) y)
{
return comparer.Compare(x.Item1, y.Item1);
}
}
}
}
}

View File

@ -245,12 +245,9 @@ namespace ObservableCollections
{
lock (SyncRoot)
{
#if NET5_0_OR_GREATER
#pragma warning disable CS0436
var range = CollectionsMarshal.AsSpan(list).Slice(index, count);
#else
var range = list.GetRange(index, count);
#endif
#pragma warning restore CS0436
// require copy before remove
using (var xs = new CloneCollection<T>(range))
{
@ -270,5 +267,51 @@ namespace ObservableCollections
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Move(removedItem, newIndex, oldIndex));
}
}
public void Sort()
{
lock (SyncRoot)
{
list.Sort();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Sort(0, list.Count, null));
}
}
public void Sort(IComparer<T> comparer)
{
lock (SyncRoot)
{
list.Sort();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Sort(0, list.Count, comparer));
}
}
public void Sort(int index, int count, IComparer<T> comparer)
{
lock (SyncRoot)
{
list.Sort();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Sort(index, count, comparer));
}
}
public void Reverse()
{
lock (SyncRoot)
{
list.Sort();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Reverse(0, list.Count));
}
}
public void Reverse(int index, int count)
{
lock (SyncRoot)
{
list.Sort();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Reverse(index, count));
}
}
}
}

View File

@ -2,7 +2,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Runtime.InteropServices;
namespace System.Collections.Generic
{
@ -32,6 +35,89 @@ namespace System.Collections.Generic
return false;
}
#if !NET8_0_OR_GREATER
#pragma warning disable CS0436
// CollectionExtensions.AddRange
public static void AddRange<T>(this List<T> list, ReadOnlySpan<T> source)
{
if (!source.IsEmpty)
{
ref var view = ref Unsafe.As<List<T>, CollectionsMarshal.ListView<T>>(ref list!);
if (view._items.Length - view._size < source.Length)
{
Grow(ref view, checked(view._size + source.Length));
}
source.CopyTo(view._items.AsSpan(view._size));
view._size += source.Length;
view._version++;
}
}
// CollectionExtensions.InsertRange
public static void InsertRange<T>(this List<T> list, int index, ReadOnlySpan<T> source)
{
if (!source.IsEmpty)
{
ref var view = ref Unsafe.As<List<T>, CollectionsMarshal.ListView<T>>(ref list!);
if (view._items.Length - view._size < source.Length)
{
Grow(ref view, checked(view._size + source.Length));
}
if (index < view._size)
{
Array.Copy(view._items, index, view._items, index + source.Length, view._size - index);
}
source.CopyTo(view._items.AsSpan(index));
view._size += source.Length;
view._version++;
}
}
static void Grow<T>(ref CollectionsMarshal.ListView<T> list, int capacity)
{
SetCapacity(ref list, GetNewCapacity(ref list, capacity));
}
static void SetCapacity<T>(ref CollectionsMarshal.ListView<T> list, int value)
{
if (value != list._items.Length)
{
if (value > 0)
{
T[] newItems = new T[value];
if (list._size > 0)
{
Array.Copy(list._items, newItems, list._size);
}
list._items = newItems;
}
else
{
list._items = Array.Empty<T>();
}
}
}
static int GetNewCapacity<T>(ref CollectionsMarshal.ListView<T> list, int capacity)
{
int newCapacity = list._items.Length == 0 ? 4 : 2 * list._items.Length;
if ((uint)newCapacity > Array.MaxLength) newCapacity = Array.MaxLength;
if (newCapacity < capacity) newCapacity = capacity;
return newCapacity;
}
#pragma warning restore CS0436
#endif
#if !NET6_0_OR_GREATER
public static bool TryGetNonEnumeratedCount<T>(this IEnumerable<T> source, out int count)

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
#if !NET7_0_OR_GREATER
#pragma warning disable CS0649
#pragma warning disable CS8618
#pragma warning disable CS8619
namespace System.Runtime.InteropServices;
internal static class CollectionsMarshal
{
/// <summary>
/// similar as AsSpan but modify size to create fixed-size span.
/// </summary>
public static Span<T> AsSpan<T>(List<T>? list)
{
if (list is null) return default;
ref var view = ref Unsafe.As<List<T>, ListView<T>>(ref list!);
return view._items.AsSpan(0, view._size);
}
internal sealed class ListView<T>
{
public T[] _items;
public int _size;
public int _version;
}
}
#endif

View File

@ -0,0 +1,144 @@
using System;
using System.Collections.Specialized;
namespace ObservableCollections
{
public readonly ref struct SynchronizedViewChangedEventArgs<T, TView>(
NotifyCollectionChangedAction action,
bool isSingleItem,
(T Value, TView View) newItem = default!,
(T Value, TView View) oldItem = default!,
ReadOnlySpan<T> newValues = default!,
ReadOnlySpan<TView> newViews = default!,
ReadOnlySpan<(T Value, TView View)> oldItems = default!,
int newStartingIndex = -1,
int oldStartingIndex = -1,
SortOperation<T> sortOperation = default)
{
public readonly NotifyCollectionChangedAction Action = action;
public readonly bool IsSingleItem = isSingleItem;
public readonly (T Value, TView View) NewItem = newItem;
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 int NewStartingIndex = newStartingIndex;
public readonly int OldStartingIndex = oldStartingIndex;
public readonly SortOperation<T> SortOperation = sortOperation;
}
public static class SynchronizedViewExtensions
{
public static void AttachFilter<T, TView>(this ISynchronizedView<T, TView> source, Func<T, bool> filter)
{
source.AttachFilter(new SynchronizedViewFilter<T>(filter));
}
public static bool IsNullFilter<T>(this ISynchronizedViewFilter<T> filter)
{
return filter == SynchronizedViewFilter<T>.Null;
}
internal static void InvokeOnAdd<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, (T value, TView view) value, int index)
{
InvokeOnAdd(collection, ref filteredCount, ev, value.value, value.view, index);
}
internal static void InvokeOnAdd<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, T value, TView view, int index)
{
var isMatch = collection.Filter.IsMatch(value);
if (isMatch)
{
filteredCount++;
if (ev != null)
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, newValue: value, newView: view, newViewIndex: index));
}
}
}
internal static void InvokeOnRemove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, (T value, TView view) value, int oldIndex)
{
InvokeOnRemove(collection, ref filteredCount, ev, value.value, value.view, oldIndex);
}
internal static void InvokeOnRemove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, T value, TView view, int oldIndex)
{
var isMatch = collection.Filter.IsMatch(value);
if (isMatch)
{
filteredCount--;
if (ev != null)
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, oldValue: value, oldView: view, oldViewIndex: oldIndex));
}
}
}
internal static void InvokeOnMove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, (T value, TView view) value, int index, int oldIndex)
{
InvokeOnMove(collection, ref filteredCount, ev, value.value, value.view, index, oldIndex);
}
internal static void InvokeOnMove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, T value, TView view, int index, int oldIndex)
{
if (ev != null)
{
// move does not changes filtered-count
var isMatch = collection.Filter.IsMatch(value);
if (isMatch)
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Move, newValue: value, newView: view, newViewIndex: index, oldViewIndex: oldIndex));
}
}
}
internal static void InvokeOnReplace<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, (T value, TView view) value, (T value, TView view) oldValue, int index, int oldIndex = -1)
{
InvokeOnReplace(collection, ref filteredCount, ev, value.value, value.view, oldValue.value, oldValue.view, index, oldIndex);
}
internal static void InvokeOnReplace<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, T value, TView view, T oldValue, TView oldView, int index, int oldIndex = -1)
{
var oldMatched = collection.Filter.IsMatch(oldValue);
var newMatched = collection.Filter.IsMatch(value);
var bothMatched = oldMatched && newMatched;
if (bothMatched)
{
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));
}
}
else if (oldMatched)
{
// only-old is remove
filteredCount--;
if (ev != null)
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, oldValue: value, oldView: view, oldViewIndex: oldIndex));
}
}
else if (newMatched)
{
// only-new is add
filteredCount++;
if (ev != null)
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, newValue: value, newView: view, newViewIndex: index));
}
}
}
internal static void InvokeOnReset<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev)
{
filteredCount = 0;
if (ev != null)
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
}
}
}
}

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
namespace ObservableCollections
{
@ -30,24 +31,57 @@ namespace ObservableCollections
switch (e.Action)
{
case NotifyCollectionChangedAction.Add: // Add or Insert
if (e.NewViewIndex == -1)
if (e.IsSingleItem)
{
listView.Add(e.NewView);
if (e.NewStartingIndex == -1)
{
listView.Add(e.NewItem.View);
}
else
{
listView.Insert(e.NewStartingIndex, e.NewItem.View);
}
}
else
{
listView.Insert(e.NewViewIndex, e.NewView);
if (e.NewStartingIndex == -1)
{
listView.AddRange(e.NewViews);
}
else
{
listView.InsertRange(e.NewStartingIndex, e.NewViews);
}
}
break;
case NotifyCollectionChangedAction.Remove: // Remove
if (e.OldViewIndex == -1) // can't gurantee correct remove if index is not provided
if (e.IsSingleItem)
{
listView.Remove(e.OldView);
if (e.OldStartingIndex == -1) // can't gurantee correct remove if index is not provided
{
listView.Remove(e.OldItem.View);
}
else
{
listView.RemoveAt(e.OldStartingIndex);
}
}
else
{
listView.RemoveAt(e.OldViewIndex);
if (e.OldStartingIndex == -1)
{
// TODO:...
//listView.RemoveAll(
// e.OldItems
}
else
{
listView.RemoveRange(e.OldStartingIndex, e.OldItems.Length);
}
}
break;
case NotifyCollectionChangedAction.Replace: // Indexer
if (e.NewViewIndex == -1)
@ -73,10 +107,38 @@ namespace ObservableCollections
}
break;
case NotifyCollectionChangedAction.Reset: // Clear or drastic changes
listView.Clear();
foreach (var item in parent) // refresh
if (e.SortOperation.IsNull)
{
listView.Add(item);
listView.Clear();
foreach (var item in parent) // refresh
{
listView.Add(item);
}
}
else if (e.SortOperation.IsReverse)
{
listView.Reverse();
}
else
{
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
}
else
{
// can not get source Span, do Clear and Refresh
listView.Clear();
foreach (var item in parent)
{
listView.Add(item);
}
}
}
break;
default: