optimize for ObservableList
This commit is contained in:
parent
a981b3121f
commit
d84965e20e
@ -20,6 +20,12 @@
|
|||||||
<StackPanel>
|
<StackPanel>
|
||||||
<ListBox ItemsSource="{Binding ItemsView}" />
|
<ListBox ItemsSource="{Binding ItemsView}" />
|
||||||
<Button Content="Add" Command="{Binding AddCommand}" />
|
<Button Content="Add" Command="{Binding AddCommand}" />
|
||||||
|
<Button Content="Insert" Command="{Binding InsertAtRandomCommand}" />
|
||||||
|
<Button Content="Remove" Command="{Binding RemoveAtRandomCommand}" />
|
||||||
<Button Content="Clear" Command="{Binding ClearCommand}" />
|
<Button Content="Clear" Command="{Binding ClearCommand}" />
|
||||||
|
<Button Content="Reverse" Command="{Binding ReverseCommand}" />
|
||||||
|
<Button Content="Sort" Command="{Binding SortCommand}" />
|
||||||
|
<Button Content="AttachFilter" Command="{Binding AttachFilterCommand}" />
|
||||||
|
<Button Content="ResetFilter" Command="{Binding ResetFilterCommand}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Window>
|
</Window>
|
||||||
|
@ -76,19 +76,26 @@ namespace WpfApp
|
|||||||
private ObservableList<int> observableList { get; } = new ObservableList<int>();
|
private ObservableList<int> observableList { get; } = new ObservableList<int>();
|
||||||
public INotifyCollectionChangedSynchronizedView<int> ItemsView { get; }
|
public INotifyCollectionChangedSynchronizedView<int> ItemsView { get; }
|
||||||
public ReactiveCommand<Unit> AddCommand { get; } = new ReactiveCommand<Unit>();
|
public ReactiveCommand<Unit> AddCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> InsertAtRandomCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> RemoveAtRandomCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
public ReactiveCommand<Unit> ClearCommand { get; } = new ReactiveCommand<Unit>();
|
public ReactiveCommand<Unit> ClearCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> ReverseCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> SortCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> AttachFilterCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> ResetFilterCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
|
||||||
public ViewModel()
|
public ViewModel()
|
||||||
{
|
{
|
||||||
observableList.Add(1);
|
observableList.Add(1);
|
||||||
observableList.Add(2);
|
observableList.Add(2);
|
||||||
|
|
||||||
ItemsView = observableList.CreateView(x => x).ToNotifyCollectionChanged(SynchronizationContextCollectionEventDispatcher.Current);
|
var view = observableList.CreateView(x => x);
|
||||||
|
ItemsView = view.ToNotifyCollectionChanged(SynchronizationContextCollectionEventDispatcher.Current);
|
||||||
|
|
||||||
|
|
||||||
// ItemsView = observableList.CreateView(x => x).ToNotifyCollectionChanged();
|
// check for optimize list
|
||||||
|
// ItemsView = observableList.ToNotifyCollectionChanged(SynchronizationContextCollectionEventDispatcher.Current);
|
||||||
|
|
||||||
// BindingOperations.EnableCollectionSynchronization(ItemsView, new object());
|
|
||||||
|
|
||||||
AddCommand.Subscribe(_ =>
|
AddCommand.Subscribe(_ =>
|
||||||
{
|
{
|
||||||
@ -98,12 +105,43 @@ namespace WpfApp
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// var iii = 10;
|
InsertAtRandomCommand.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
var from = Random.Shared.Next(0, view.Count);
|
||||||
|
observableList.Insert(from, Random.Shared.Next());
|
||||||
|
});
|
||||||
|
|
||||||
|
RemoveAtRandomCommand.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
var from = Random.Shared.Next(0, view.Count);
|
||||||
|
observableList.RemoveAt(from);
|
||||||
|
});
|
||||||
|
|
||||||
ClearCommand.Subscribe(_ =>
|
ClearCommand.Subscribe(_ =>
|
||||||
{
|
{
|
||||||
// observableList.Add(iii++);
|
|
||||||
observableList.Clear();
|
observableList.Clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
ReverseCommand.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
observableList.Reverse();
|
||||||
|
});
|
||||||
|
|
||||||
|
SortCommand.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
observableList.Sort();
|
||||||
|
});
|
||||||
|
|
||||||
|
AttachFilterCommand.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
view.AttachFilter(x => x % 2 == 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
ResetFilterCommand.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
view.ResetFilter();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ public static class ObservableCollectionR3Extensions
|
|||||||
return new ObservableCollectionClear<T>(source, cancellationToken);
|
return new ObservableCollectionClear<T>(source, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Observable<Unit> ObserveReverse<T>(this IObservableCollection<T> source, CancellationToken cancellationToken = default)
|
public static Observable<(int Index, int Count))> ObserveReverse<T>(this IObservableCollection<T> source, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return new ObservableCollectionReverse<T>(source, cancellationToken);
|
return new ObservableCollectionReverse<T>(source, cancellationToken);
|
||||||
}
|
}
|
||||||
@ -251,24 +251,24 @@ sealed class ObservableCollectionClear<T>(IObservableCollection<T> collection, C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class ObservableCollectionReverse<T>(IObservableCollection<T> collection, CancellationToken cancellationToken) : Observable<Unit>
|
sealed class ObservableCollectionReverse<T>(IObservableCollection<T> collection, CancellationToken cancellationToken) : Observable<(int Index, int Count)>
|
||||||
{
|
{
|
||||||
protected override IDisposable SubscribeCore(Observer<Unit> observer)
|
protected override IDisposable SubscribeCore(Observer<(int Index, int Count)> observer)
|
||||||
{
|
{
|
||||||
return new _ObservableCollectionReverse(collection, observer, cancellationToken);
|
return new _ObservableCollectionReverse(collection, observer, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class _ObservableCollectionReverse(
|
sealed class _ObservableCollectionReverse(
|
||||||
IObservableCollection<T> collection,
|
IObservableCollection<T> collection,
|
||||||
Observer<Unit> observer,
|
Observer<(int Index, int Count)> observer,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
: ObservableCollectionObserverBase<T, Unit>(collection, observer, cancellationToken)
|
: ObservableCollectionObserverBase<T, (int Index, int Count)>(collection, observer, cancellationToken)
|
||||||
{
|
{
|
||||||
protected override void Handler(in NotifyCollectionChangedEventArgs<T> eventArgs)
|
protected override void Handler(in NotifyCollectionChangedEventArgs<T> eventArgs)
|
||||||
{
|
{
|
||||||
if (eventArgs.Action == NotifyCollectionChangedAction.Reset && eventArgs.SortOperation.IsReverse)
|
if (eventArgs.Action == NotifyCollectionChangedAction.Reset && eventArgs.SortOperation.IsReverse)
|
||||||
{
|
{
|
||||||
observer.OnNext(Unit.Default);
|
observer.OnNext((eventArgs.SortOperation.Index, eventArgs.SortOperation.Count));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ public class AlternateIndexList<T> : IEnumerable<T>
|
|||||||
|
|
||||||
public int Count => list.Count;
|
public int Count => list.Count;
|
||||||
|
|
||||||
public void Insert(int alternateIndex, T value)
|
public int Insert(int alternateIndex, T value)
|
||||||
{
|
{
|
||||||
var index = list.BinarySearch(alternateIndex);
|
var index = list.BinarySearch(alternateIndex);
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
@ -49,9 +49,10 @@ public class AlternateIndexList<T> : IEnumerable<T>
|
|||||||
}
|
}
|
||||||
list.Insert(index, new(alternateIndex, value));
|
list.Insert(index, new(alternateIndex, value));
|
||||||
UpdateAlternateIndex(index + 1, 1);
|
UpdateAlternateIndex(index + 1, 1);
|
||||||
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InsertRange(int startingAlternateIndex, IEnumerable<T> values)
|
public int InsertRange(int startingAlternateIndex, IEnumerable<T> values)
|
||||||
{
|
{
|
||||||
var index = list.BinarySearch(startingAlternateIndex);
|
var index = list.BinarySearch(startingAlternateIndex);
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
@ -62,9 +63,10 @@ public class AlternateIndexList<T> : IEnumerable<T>
|
|||||||
using var iter = new InsertIterator(startingAlternateIndex, values);
|
using var iter = new InsertIterator(startingAlternateIndex, values);
|
||||||
list.InsertRange(index, iter);
|
list.InsertRange(index, iter);
|
||||||
UpdateAlternateIndex(index + iter.ConsumedCount, iter.ConsumedCount);
|
UpdateAlternateIndex(index + iter.ConsumedCount, iter.ConsumedCount);
|
||||||
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove(T value)
|
public int Remove(T value)
|
||||||
{
|
{
|
||||||
var index = list.FindIndex(x => EqualityComparer<T>.Default.Equals(x.Value, value));
|
var index = list.FindIndex(x => EqualityComparer<T>.Default.Equals(x.Value, value));
|
||||||
if (index != -1)
|
if (index != -1)
|
||||||
@ -72,9 +74,10 @@ public class AlternateIndexList<T> : IEnumerable<T>
|
|||||||
list.RemoveAt(index);
|
list.RemoveAt(index);
|
||||||
UpdateAlternateIndex(index, -1);
|
UpdateAlternateIndex(index, -1);
|
||||||
}
|
}
|
||||||
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveAt(int alternateIndex)
|
public int RemoveAt(int alternateIndex)
|
||||||
{
|
{
|
||||||
var index = list.BinarySearch(alternateIndex);
|
var index = list.BinarySearch(alternateIndex);
|
||||||
if (index != -1)
|
if (index != -1)
|
||||||
@ -82,9 +85,10 @@ public class AlternateIndexList<T> : IEnumerable<T>
|
|||||||
list.RemoveAt(index);
|
list.RemoveAt(index);
|
||||||
UpdateAlternateIndex(index, -1);
|
UpdateAlternateIndex(index, -1);
|
||||||
}
|
}
|
||||||
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveRange(int alternateIndex, int count)
|
public int RemoveRange(int alternateIndex, int count)
|
||||||
{
|
{
|
||||||
var index = list.BinarySearch(alternateIndex);
|
var index = list.BinarySearch(alternateIndex);
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
@ -94,6 +98,7 @@ public class AlternateIndexList<T> : IEnumerable<T>
|
|||||||
|
|
||||||
list.RemoveRange(index, count);
|
list.RemoveRange(index, count);
|
||||||
UpdateAlternateIndex(index, -count);
|
UpdateAlternateIndex(index, -count);
|
||||||
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetAtAlternateIndex(int alternateIndex, [MaybeNullWhen(true)] out T value)
|
public bool TryGetAtAlternateIndex(int alternateIndex, [MaybeNullWhen(true)] out T value)
|
||||||
@ -108,23 +113,23 @@ public class AlternateIndexList<T> : IEnumerable<T>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TrySetAtAlternateIndex(int alternateIndex, T value)
|
public bool TrySetAtAlternateIndex(int alternateIndex, T value, out int setIndex)
|
||||||
{
|
{
|
||||||
var index = list.BinarySearch(alternateIndex);
|
setIndex = list.BinarySearch(alternateIndex);
|
||||||
if (index < 0)
|
if (setIndex < 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
CollectionsMarshal.AsSpan(list)[index].Value = value;
|
CollectionsMarshal.AsSpan(list)[setIndex].Value = value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryReplaceByValue(T searchValue, T replaceValue)
|
public bool TryReplaceByValue(T searchValue, T replaceValue, out int replacedIndex)
|
||||||
{
|
{
|
||||||
var index = list.FindIndex(x => EqualityComparer<T>.Default.Equals(x.Value, searchValue));
|
replacedIndex = list.FindIndex(x => EqualityComparer<T>.Default.Equals(x.Value, searchValue));
|
||||||
if (index != -1)
|
if (replacedIndex != -1)
|
||||||
{
|
{
|
||||||
CollectionsMarshal.AsSpan(list)[index].Value = replaceValue;
|
CollectionsMarshal.AsSpan(list)[replacedIndex].Value = replaceValue;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -16,8 +16,6 @@ namespace ObservableCollections
|
|||||||
|
|
||||||
public bool IsReverse => Comparer == ReverseSentinel.Instance;
|
public bool IsReverse => Comparer == ReverseSentinel.Instance;
|
||||||
public bool IsNull => Comparer == null;
|
public bool IsNull => Comparer == null;
|
||||||
|
|
||||||
[MemberNotNullWhen(true, nameof(Comparer))]
|
|
||||||
public bool IsSort => !IsNull && !IsReverse;
|
public bool IsSort => !IsNull && !IsReverse;
|
||||||
|
|
||||||
public SortOperation(int index, int count, IComparer<T>? comparer)
|
public SortOperation(int index, int count, IComparer<T>? comparer)
|
||||||
@ -53,7 +51,7 @@ namespace ObservableCollections
|
|||||||
|
|
||||||
public int Compare(T? x, T? y)
|
public int Compare(T? x, T? y)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return Comparer<T>.Default.Compare(x!, y!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ namespace ObservableCollections
|
|||||||
|
|
||||||
public ISynchronizedViewList<TView> ToViewList()
|
public ISynchronizedViewList<TView> ToViewList()
|
||||||
{
|
{
|
||||||
return new SynchronizedViewList<KeyValuePair<TKey, TValue>, TView>(this);
|
return new FiltableSynchronizedViewList<KeyValuePair<TKey, TValue>, TView>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
||||||
|
@ -105,7 +105,7 @@ namespace ObservableCollections
|
|||||||
|
|
||||||
public ISynchronizedViewList<TView> ToViewList()
|
public ISynchronizedViewList<TView> ToViewList()
|
||||||
{
|
{
|
||||||
return new SynchronizedViewList<T, TView>(this);
|
return new FiltableSynchronizedViewList<T, TView>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
||||||
|
@ -16,17 +16,30 @@ namespace ObservableCollections
|
|||||||
|
|
||||||
public ISynchronizedViewList<T> ToViewList()
|
public ISynchronizedViewList<T> ToViewList()
|
||||||
{
|
{
|
||||||
return CreateView(static x => x).ToViewList();
|
// NOTE: for more optimize, no need to create View.
|
||||||
|
return ToViewList(static x => x);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISynchronizedViewList<TView> ToViewList<TView>(Func<T, TView> transform)
|
||||||
|
{
|
||||||
|
// Optimized for non filtered
|
||||||
|
return new NonFilteredSynchronizedViewList<T, TView>(CreateView(transform));
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedView<T> ToNotifyCollectionChanged()
|
public INotifyCollectionChangedSynchronizedView<T> ToNotifyCollectionChanged()
|
||||||
{
|
{
|
||||||
return CreateView(static x => x).ToNotifyCollectionChanged();
|
return ToNotifyCollectionChanged(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedView<T> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
|
public INotifyCollectionChangedSynchronizedView<T> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
|
||||||
{
|
{
|
||||||
return CreateView(static x => x).ToNotifyCollectionChanged(collectionEventDispatcher);
|
return ToNotifyCollectionChanged(static x => x, collectionEventDispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged<TView>(Func<T, TView> transform, ICollectionEventDispatcher? collectionEventDispatcher)
|
||||||
|
{
|
||||||
|
// Optimized for non filtered
|
||||||
|
return new NonFilteredNotifyCollectionChangedSynchronizedView<T, TView>(CreateView(transform), collectionEventDispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class View<TView> : ISynchronizedView<T, TView>
|
internal sealed class View<TView> : ISynchronizedView<T, TView>
|
||||||
@ -123,7 +136,7 @@ namespace ObservableCollections
|
|||||||
|
|
||||||
public ISynchronizedViewList<TView> ToViewList()
|
public ISynchronizedViewList<TView> ToViewList()
|
||||||
{
|
{
|
||||||
return new SynchronizedViewList<T, TView>(this);
|
return new FiltableSynchronizedViewList<T, TView>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
||||||
@ -315,7 +328,7 @@ namespace ObservableCollections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class IgnoreViewComparer : IComparer<(T, TView)>
|
sealed class IgnoreViewComparer : IComparer<(T, TView)>
|
||||||
{
|
{
|
||||||
readonly IComparer<T> comparer;
|
readonly IComparer<T> comparer;
|
||||||
|
|
||||||
|
@ -291,7 +291,7 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
list.Sort();
|
list.Sort(comparer);
|
||||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Sort(0, list.Count, comparer));
|
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Sort(0, list.Count, comparer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -300,7 +300,7 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
list.Sort();
|
list.Sort(index, count, comparer);
|
||||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Sort(index, count, comparer));
|
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Sort(index, count, comparer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -309,7 +309,7 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
list.Sort();
|
list.Reverse();
|
||||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Reverse(0, list.Count));
|
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Reverse(0, list.Count));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -318,7 +318,7 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
list.Sort();
|
list.Reverse(index, count);
|
||||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Reverse(index, count));
|
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Reverse(index, count));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,7 @@ namespace ObservableCollections
|
|||||||
|
|
||||||
public ISynchronizedViewList<TView> ToViewList()
|
public ISynchronizedViewList<TView> ToViewList()
|
||||||
{
|
{
|
||||||
return new SynchronizedViewList<T, TView>(this);
|
return new FiltableSynchronizedViewList<T, TView>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
||||||
|
@ -107,7 +107,7 @@ namespace ObservableCollections
|
|||||||
|
|
||||||
public ISynchronizedViewList<TView> ToViewList()
|
public ISynchronizedViewList<TView> ToViewList()
|
||||||
{
|
{
|
||||||
return new SynchronizedViewList<T, TView>(this);
|
return new FiltableSynchronizedViewList<T, TView>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
||||||
|
@ -104,7 +104,7 @@ namespace ObservableCollections
|
|||||||
|
|
||||||
public ISynchronizedViewList<TView> ToViewList()
|
public ISynchronizedViewList<TView> ToViewList()
|
||||||
{
|
{
|
||||||
return new SynchronizedViewList<T, TView>(this);
|
return new FiltableSynchronizedViewList<T, TView>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace ObservableCollections
|
namespace ObservableCollections
|
||||||
{
|
{
|
||||||
@ -28,6 +29,55 @@ namespace ObservableCollections
|
|||||||
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;
|
||||||
|
|
||||||
|
public SynchronizedViewChangedEventArgs<T, TView> WithNewStartingIndex(int newStartingIndex)
|
||||||
|
{
|
||||||
|
// MEMO: struct copy and replace only newStartingIndex memory maybe fast.
|
||||||
|
return new SynchronizedViewChangedEventArgs<T, TView>(
|
||||||
|
action,
|
||||||
|
IsSingleItem,
|
||||||
|
newItem: NewItem,
|
||||||
|
oldItem: OldItem,
|
||||||
|
newValues: NewValues,
|
||||||
|
newViews: NewViews,
|
||||||
|
oldValues: OldValues,
|
||||||
|
oldViews: OldViews,
|
||||||
|
newStartingIndex: newStartingIndex, // replace
|
||||||
|
oldStartingIndex: OldStartingIndex,
|
||||||
|
sortOperation: SortOperation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SynchronizedViewChangedEventArgs<T, TView> WithOldStartingIndex(int oldStartingIndex)
|
||||||
|
{
|
||||||
|
return new SynchronizedViewChangedEventArgs<T, TView>(
|
||||||
|
action,
|
||||||
|
IsSingleItem,
|
||||||
|
newItem: NewItem,
|
||||||
|
oldItem: OldItem,
|
||||||
|
newValues: NewValues,
|
||||||
|
newViews: NewViews,
|
||||||
|
oldValues: OldValues,
|
||||||
|
oldViews: OldViews,
|
||||||
|
newStartingIndex: NewStartingIndex,
|
||||||
|
oldStartingIndex: oldStartingIndex, // replace
|
||||||
|
sortOperation: SortOperation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SynchronizedViewChangedEventArgs<T, TView> WithNewAndOldStartingIndex(int newStartingIndex, int oldStartingIndex)
|
||||||
|
{
|
||||||
|
return new SynchronizedViewChangedEventArgs<T, TView>(
|
||||||
|
action,
|
||||||
|
IsSingleItem,
|
||||||
|
newItem: NewItem,
|
||||||
|
oldItem: OldItem,
|
||||||
|
newValues: NewValues,
|
||||||
|
newViews: NewViews,
|
||||||
|
oldValues: OldValues,
|
||||||
|
oldViews: OldViews,
|
||||||
|
newStartingIndex: newStartingIndex, // replace
|
||||||
|
oldStartingIndex: oldStartingIndex, // replace
|
||||||
|
sortOperation: SortOperation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SynchronizedViewExtensions
|
public static class SynchronizedViewExtensions
|
||||||
|
@ -5,17 +5,18 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ObservableCollections;
|
namespace ObservableCollections;
|
||||||
|
|
||||||
internal class SynchronizedViewList<T, TView> : ISynchronizedViewList<TView>
|
internal class FiltableSynchronizedViewList<T, TView> : ISynchronizedViewList<TView>
|
||||||
{
|
{
|
||||||
readonly ISynchronizedView<T, TView> parent;
|
readonly ISynchronizedView<T, TView> parent;
|
||||||
protected readonly AlternateIndexList<TView> listView;
|
protected readonly AlternateIndexList<TView> listView;
|
||||||
protected readonly object gate = new object();
|
protected readonly object gate = new object();
|
||||||
|
|
||||||
public SynchronizedViewList(ISynchronizedView<T, TView> parent)
|
public FiltableSynchronizedViewList(ISynchronizedView<T, TView> parent)
|
||||||
{
|
{
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
lock (parent.SyncRoot)
|
lock (parent.SyncRoot)
|
||||||
@ -60,24 +61,29 @@ internal class SynchronizedViewList<T, TView> : ISynchronizedViewList<TView>
|
|||||||
case NotifyCollectionChangedAction.Add: // Add or Insert
|
case NotifyCollectionChangedAction.Add: // Add or Insert
|
||||||
if (e.IsSingleItem)
|
if (e.IsSingleItem)
|
||||||
{
|
{
|
||||||
listView.Insert(e.NewStartingIndex, e.NewItem.View);
|
var index = listView.Insert(e.NewStartingIndex, e.NewItem.View);
|
||||||
|
OnCollectionChanged(e.WithNewStartingIndex(index));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
using var array = new CloneCollection<TView>(e.NewViews);
|
using var array = new CloneCollection<TView>(e.NewViews);
|
||||||
listView.InsertRange(e.NewStartingIndex, array.AsEnumerable());
|
var index = listView.InsertRange(e.NewStartingIndex, array.AsEnumerable());
|
||||||
|
OnCollectionChanged(e.WithNewStartingIndex(index));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case NotifyCollectionChangedAction.Remove: // Remove
|
case NotifyCollectionChangedAction.Remove: // Remove
|
||||||
|
{
|
||||||
|
int index = e.OldStartingIndex;
|
||||||
if (e.IsSingleItem)
|
if (e.IsSingleItem)
|
||||||
{
|
{
|
||||||
if (e.OldStartingIndex == -1) // can't gurantee correct remove if index is not provided
|
if (e.OldStartingIndex == -1) // can't gurantee correct remove if index is not provided
|
||||||
{
|
{
|
||||||
listView.Remove(e.OldItem.View);
|
index = listView.Remove(e.OldItem.View);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
listView.RemoveAt(e.OldStartingIndex);
|
index = listView.RemoveAt(e.OldStartingIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -87,34 +93,53 @@ internal class SynchronizedViewList<T, TView> : ISynchronizedViewList<TView>
|
|||||||
foreach (var view in e.OldViews) // index is unknown, can't do batching
|
foreach (var view in e.OldViews) // index is unknown, can't do batching
|
||||||
{
|
{
|
||||||
listView.Remove(view);
|
listView.Remove(view);
|
||||||
|
OnCollectionChanged(e.WithOldStartingIndex(index));
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
listView.RemoveRange(e.OldStartingIndex, e.OldViews.Length);
|
index = listView.RemoveRange(e.OldStartingIndex, e.OldViews.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
OnCollectionChanged(e.WithOldStartingIndex(index));
|
||||||
|
return;
|
||||||
|
}
|
||||||
case NotifyCollectionChangedAction.Replace: // Indexer
|
case NotifyCollectionChangedAction.Replace: // Indexer
|
||||||
if (e.NewStartingIndex == -1)
|
if (e.NewStartingIndex == -1)
|
||||||
{
|
{
|
||||||
listView.TryReplaceByValue(e.OldItem.View, e.NewItem.View);
|
if (listView.TryReplaceByValue(e.OldItem.View, e.NewItem.View, out var replacedIndex))
|
||||||
|
{
|
||||||
|
OnCollectionChanged(e.WithNewAndOldStartingIndex(replacedIndex, replacedIndex));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
listView.TrySetAtAlternateIndex(e.NewStartingIndex, e.NewItem.View);
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (listView.TrySetAtAlternateIndex(e.NewStartingIndex, e.NewItem.View, out var setIndex))
|
||||||
|
{
|
||||||
|
OnCollectionChanged(e.WithNewAndOldStartingIndex(setIndex, setIndex));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
case NotifyCollectionChangedAction.Move: //Remove and Insert
|
case NotifyCollectionChangedAction.Move: //Remove and Insert
|
||||||
if (e.NewStartingIndex == -1)
|
if (e.NewStartingIndex == -1)
|
||||||
{
|
{
|
||||||
// do nothing
|
return; // do nothing
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
listView.RemoveAt(e.OldStartingIndex);
|
var oldIndex = listView.RemoveAt(e.OldStartingIndex);
|
||||||
listView.Insert(e.NewStartingIndex, e.NewItem.View);
|
var newIndex = listView.Insert(e.NewStartingIndex, e.NewItem.View);
|
||||||
|
OnCollectionChanged(e.WithNewAndOldStartingIndex(newStartingIndex: newIndex, oldStartingIndex: oldIndex));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Reset: // Clear or drastic changes
|
case NotifyCollectionChangedAction.Reset: // Clear or drastic changes
|
||||||
@ -176,8 +201,225 @@ internal class SynchronizedViewList<T, TView> : ISynchronizedViewList<TView>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class NonFilteredSynchronizedViewList<T, TView> : ISynchronizedViewList<TView>
|
||||||
|
{
|
||||||
|
readonly ISynchronizedView<T, TView> parent;
|
||||||
|
protected readonly List<TView> listView; // no filter can be faster
|
||||||
|
protected readonly object gate = new object();
|
||||||
|
|
||||||
|
public NonFilteredSynchronizedViewList(ISynchronizedView<T, TView> parent)
|
||||||
|
{
|
||||||
|
this.parent = parent;
|
||||||
|
lock (parent.SyncRoot)
|
||||||
|
{
|
||||||
|
listView = parent.ToList(); // iterate filtered
|
||||||
|
parent.ViewChanged += Parent_ViewChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Parent_ViewChanged(in SynchronizedViewChangedEventArgs<T, TView> e)
|
||||||
|
{
|
||||||
|
// event is called inside parent lock
|
||||||
|
lock (gate)
|
||||||
|
{
|
||||||
|
switch (e.Action)
|
||||||
|
{
|
||||||
|
case NotifyCollectionChangedAction.Add: // Add or Insert
|
||||||
|
if (e.IsSingleItem)
|
||||||
|
{
|
||||||
|
listView.Insert(e.NewStartingIndex, e.NewItem.View);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
listView.InsertRange(e.NewStartingIndex, e.NewViews);
|
||||||
|
#else
|
||||||
|
using var array = new CloneCollection<TView>(e.NewViews);
|
||||||
|
listView.InsertRange(e.NewStartingIndex, array.AsEnumerable());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NotifyCollectionChangedAction.Remove: // Remove
|
||||||
|
{
|
||||||
|
if (e.IsSingleItem)
|
||||||
|
{
|
||||||
|
if (e.OldStartingIndex == -1) // can't gurantee correct remove if index is not provided
|
||||||
|
{
|
||||||
|
var index = listView.IndexOf(e.OldItem.View);
|
||||||
|
listView.RemoveAt(index);
|
||||||
|
OnCollectionChanged(e.WithOldStartingIndex(index));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
listView.RemoveAt(e.OldStartingIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (e.OldStartingIndex == -1)
|
||||||
|
{
|
||||||
|
foreach (var view in e.OldViews) // index is unknown, can't do batching
|
||||||
|
{
|
||||||
|
var index = listView.IndexOf(view);
|
||||||
|
listView.RemoveAt(index);
|
||||||
|
OnCollectionChanged(e.WithOldStartingIndex(index));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
listView.RemoveRange(e.OldStartingIndex, e.OldViews.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NotifyCollectionChangedAction.Replace: // Indexer
|
||||||
|
if (e.NewStartingIndex == -1)
|
||||||
|
{
|
||||||
|
var index = listView.IndexOf(e.OldItem.View);
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
listView[index] = e.NewItem.View;
|
||||||
|
OnCollectionChanged(e.WithNewAndOldStartingIndex(index, index));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
listView[e.NewStartingIndex] = e.NewItem.View;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NotifyCollectionChangedAction.Move: //Remove and Insert
|
||||||
|
if (e.NewStartingIndex == -1)
|
||||||
|
{
|
||||||
|
return; // do nothing
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
listView.RemoveAt(e.OldStartingIndex);
|
||||||
|
listView.Insert(e.NewStartingIndex, e.NewItem.View);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NotifyCollectionChangedAction.Reset: // Clear or drastic changes
|
||||||
|
if (e.SortOperation.IsNull)
|
||||||
|
{
|
||||||
|
listView.Clear();
|
||||||
|
foreach (var item in parent.Unfiltered) // refresh
|
||||||
|
{
|
||||||
|
listView.Add(item.View);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (e.SortOperation.IsReverse)
|
||||||
|
{
|
||||||
|
listView.Reverse(e.SortOperation.Index, e.SortOperation.Count);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
#pragma warning disable CS0436
|
||||||
|
if (parent is ObservableList<T>.View<TView> observableListView && typeof(T) == typeof(TView))
|
||||||
|
{
|
||||||
|
var comparer = new ViewComparer(e.SortOperation.Comparer ?? Comparer<T>.Default);
|
||||||
|
var viewSpan = CollectionsMarshal.AsSpan(listView).Slice(e.SortOperation.Index, e.SortOperation.Count);
|
||||||
|
viewSpan.Sort(comparer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#pragma warning restore CS0436
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
// can not get source Span, do Clear and Refresh
|
||||||
|
listView.Clear();
|
||||||
|
foreach (var item in parent.Unfiltered)
|
||||||
|
{
|
||||||
|
listView.Add(item.View);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnCollectionChanged(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class ViewComparer : IComparer<TView>
|
||||||
|
{
|
||||||
|
readonly IComparer<T> comparer;
|
||||||
|
|
||||||
|
public ViewComparer(IComparer<T> comparer)
|
||||||
|
{
|
||||||
|
this.comparer = comparer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Compare(TView? x, TView? y)
|
||||||
|
{
|
||||||
|
var t1 = Unsafe.As<TView, T>(ref x!);
|
||||||
|
var t2 = Unsafe.As<TView, T>(ref y!);
|
||||||
|
return comparer.Compare(t1, t2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnCollectionChanged(in SynchronizedViewChangedEventArgs<T, TView> args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public TView this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (gate)
|
||||||
|
{
|
||||||
|
return listView[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (gate)
|
||||||
|
{
|
||||||
|
return listView.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<TView> GetEnumerator()
|
||||||
|
{
|
||||||
|
lock (gate)
|
||||||
|
{
|
||||||
|
foreach (var item in listView)
|
||||||
|
{
|
||||||
|
yield return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return listView.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
parent.ViewChanged -= Parent_ViewChanged;
|
||||||
|
parent.Dispose(); // Dispose parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal class NotifyCollectionChangedSynchronizedView<T, TView> :
|
internal class NotifyCollectionChangedSynchronizedView<T, TView> :
|
||||||
SynchronizedViewList<T, TView>,
|
FiltableSynchronizedViewList<T, TView>,
|
||||||
INotifyCollectionChangedSynchronizedView<TView>,
|
INotifyCollectionChangedSynchronizedView<TView>,
|
||||||
IList<TView>, IList
|
IList<TView>, IList
|
||||||
{
|
{
|
||||||
@ -420,3 +662,248 @@ internal class NotifyCollectionChangedSynchronizedView<T, TView> :
|
|||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class NonFilteredNotifyCollectionChangedSynchronizedView<T, TView> :
|
||||||
|
NonFilteredSynchronizedViewList<T, TView>,
|
||||||
|
INotifyCollectionChangedSynchronizedView<TView>,
|
||||||
|
IList<TView>, IList
|
||||||
|
{
|
||||||
|
static readonly PropertyChangedEventArgs CountPropertyChangedEventArgs = new("Count");
|
||||||
|
static readonly Action<NotifyCollectionChangedEventArgs> raiseChangedEventInvoke = RaiseChangedEvent;
|
||||||
|
|
||||||
|
readonly ICollectionEventDispatcher eventDispatcher;
|
||||||
|
|
||||||
|
public event NotifyCollectionChangedEventHandler? CollectionChanged;
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
|
public NonFilteredNotifyCollectionChangedSynchronizedView(ISynchronizedView<T, TView> parent, ICollectionEventDispatcher? eventDispatcher)
|
||||||
|
: base(parent)
|
||||||
|
{
|
||||||
|
this.eventDispatcher = eventDispatcher ?? InlineCollectionEventDispatcher.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnCollectionChanged(in SynchronizedViewChangedEventArgs<T, TView> args)
|
||||||
|
{
|
||||||
|
if (CollectionChanged == null && PropertyChanged == null) return;
|
||||||
|
|
||||||
|
switch (args.Action)
|
||||||
|
{
|
||||||
|
case NotifyCollectionChangedAction.Add:
|
||||||
|
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:
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
Collection = this,
|
||||||
|
Invoker = raiseChangedEventInvoke,
|
||||||
|
IsInvokeCollectionChanged = true,
|
||||||
|
IsInvokePropertyChanged = true
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case NotifyCollectionChangedAction.Replace:
|
||||||
|
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Replace, args.NewItem.View, args.OldItem.View, args.NewStartingIndex)
|
||||||
|
{
|
||||||
|
Collection = this,
|
||||||
|
Invoker = raiseChangedEventInvoke,
|
||||||
|
IsInvokeCollectionChanged = true,
|
||||||
|
IsInvokePropertyChanged = false
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case NotifyCollectionChangedAction.Move:
|
||||||
|
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Move, args.NewItem.View, args.NewStartingIndex, args.OldStartingIndex)
|
||||||
|
{
|
||||||
|
Collection = this,
|
||||||
|
Invoker = raiseChangedEventInvoke,
|
||||||
|
IsInvokeCollectionChanged = true,
|
||||||
|
IsInvokePropertyChanged = false
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RaiseChangedEvent(NotifyCollectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
var e2 = (CollectionEventDispatcherEventArgs)e;
|
||||||
|
var self = (NonFilteredNotifyCollectionChangedSynchronizedView<T, TView>)e2.Collection;
|
||||||
|
|
||||||
|
if (e2.IsInvokeCollectionChanged)
|
||||||
|
{
|
||||||
|
self.CollectionChanged?.Invoke(self, e);
|
||||||
|
}
|
||||||
|
if (e2.IsInvokePropertyChanged)
|
||||||
|
{
|
||||||
|
self.PropertyChanged?.Invoke(self, CountPropertyChangedEventArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IList<T>, IList implementation
|
||||||
|
|
||||||
|
TView IList<TView>.this[int index]
|
||||||
|
{
|
||||||
|
get => ((IReadOnlyList<TView>)this)[index];
|
||||||
|
set => throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
object? IList.this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this[index];
|
||||||
|
}
|
||||||
|
set => throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsCompatibleObject(object? value)
|
||||||
|
{
|
||||||
|
return value is TView || value == null && default(TView) == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly => true;
|
||||||
|
|
||||||
|
public bool IsFixedSize => false;
|
||||||
|
|
||||||
|
public bool IsSynchronized => true;
|
||||||
|
|
||||||
|
public object SyncRoot => gate;
|
||||||
|
|
||||||
|
public void Add(TView item)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Add(object? value)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(TView item)
|
||||||
|
{
|
||||||
|
lock (gate)
|
||||||
|
{
|
||||||
|
foreach (var listItem in listView)
|
||||||
|
{
|
||||||
|
if (EqualityComparer<TView>.Default.Equals(listItem, item))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(object? value)
|
||||||
|
{
|
||||||
|
if (IsCompatibleObject(value))
|
||||||
|
{
|
||||||
|
return Contains((TView)value!);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(TView[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(Array array, int index)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int IndexOf(TView item)
|
||||||
|
{
|
||||||
|
lock (gate)
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
foreach (var listItem in listView)
|
||||||
|
{
|
||||||
|
if (EqualityComparer<TView>.Default.Equals(listItem, item))
|
||||||
|
{
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int IndexOf(object? item)
|
||||||
|
{
|
||||||
|
if (IsCompatibleObject(item))
|
||||||
|
{
|
||||||
|
return IndexOf((TView)item!);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(int index, TView item)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(int index, object? value)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(TView item)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(object? value)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAt(int index)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user