more changed wip
This commit is contained in:
parent
5b3eb80158
commit
7eef45cadb
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
@ -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:
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
34
src/ObservableCollections/Shims/CollectionsMarshalEx.cs
Normal file
34
src/ObservableCollections/Shims/CollectionsMarshalEx.cs
Normal 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
|
144
src/ObservableCollections/SynchronizedViewChangedEventArgs.cs
Normal file
144
src/ObservableCollections/SynchronizedViewChangedEventArgs.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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.NewViewIndex, e.NewView);
|
||||
listView.Insert(e.NewStartingIndex, e.NewItem.View);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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.OldViewIndex);
|
||||
listView.RemoveAt(e.OldStartingIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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,11 +107,39 @@ namespace ObservableCollections
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Reset: // Clear or drastic changes
|
||||
if (e.SortOperation.IsNull)
|
||||
{
|
||||
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:
|
||||
break;
|
||||
|
Loading…
x
Reference in New Issue
Block a user