c
This commit is contained in:
parent
295cef5ae5
commit
5b3eb80158
@ -33,13 +33,13 @@ var viewModels = models.CreateView(x => new ViewModel
|
||||
Value = "@" + x
|
||||
});
|
||||
|
||||
viewModels.AttachFilter(new HogeFilter(), true);
|
||||
viewModels.AttachFilter(new HogeFilter());
|
||||
|
||||
models.Add(100);
|
||||
|
||||
foreach (var (x, xs) in viewModels)
|
||||
foreach (var x in viewModels)
|
||||
{
|
||||
System.Console.WriteLine(xs.Value);
|
||||
System.Console.WriteLine(x);
|
||||
}
|
||||
|
||||
class ViewModel
|
||||
@ -48,23 +48,13 @@ class ViewModel
|
||||
public string Value { get; set; } = default!;
|
||||
}
|
||||
|
||||
class HogeFilter : ISynchronizedViewFilter<int, ViewModel>
|
||||
class HogeFilter : ISynchronizedViewFilter<int>
|
||||
{
|
||||
public bool IsMatch(int value, ViewModel view)
|
||||
public bool IsMatch(int value)
|
||||
{
|
||||
return value % 2 == 0;
|
||||
}
|
||||
|
||||
public void WhenTrue(int value, ViewModel view)
|
||||
{
|
||||
view.Value = $"@{value} (even)";
|
||||
}
|
||||
|
||||
public void WhenFalse(int value, ViewModel view)
|
||||
{
|
||||
view.Value = $"@{value} (odd)";
|
||||
}
|
||||
|
||||
public void OnCollectionChanged(in SynchronizedViewChangedEventArgs<int, ViewModel> eventArgs)
|
||||
{
|
||||
switch (eventArgs.Action)
|
||||
|
@ -1,59 +0,0 @@
|
||||
#nullable disable
|
||||
|
||||
using ObservableCollections.Internal;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace ObservableCollections
|
||||
{
|
||||
public sealed class FreezedDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>, IFreezedCollection<KeyValuePair<TKey, TValue>>
|
||||
where TKey : notnull
|
||||
{
|
||||
readonly IReadOnlyDictionary<TKey, TValue> dictionary;
|
||||
|
||||
public FreezedDictionary(IReadOnlyDictionary<TKey, TValue> dictionary)
|
||||
{
|
||||
this.dictionary = dictionary;
|
||||
}
|
||||
|
||||
public TValue this[TKey key] => dictionary[key];
|
||||
|
||||
public IEnumerable<TKey> Keys => dictionary.Keys;
|
||||
|
||||
public IEnumerable<TValue> Values => dictionary.Values;
|
||||
|
||||
public int Count => dictionary.Count;
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
return dictionary.ContainsKey(key);
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
return dictionary.GetEnumerator();
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
|
||||
{
|
||||
return dictionary.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable)dictionary).GetEnumerator();
|
||||
}
|
||||
|
||||
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, bool reverse = false)
|
||||
{
|
||||
return new FreezedView<KeyValuePair<TKey, TValue>, TView>(dictionary, transform, reverse);
|
||||
}
|
||||
|
||||
public ISortableSynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateSortableView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform)
|
||||
{
|
||||
return new FreezedSortableView<KeyValuePair<TKey, TValue>, TView>(dictionary, transform);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
using ObservableCollections.Internal;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace ObservableCollections
|
||||
{
|
||||
public sealed class FreezedList<T> : IReadOnlyList<T>, IFreezedCollection<T>
|
||||
{
|
||||
readonly IReadOnlyList<T> list;
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return list[index];
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return list.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly => true;
|
||||
|
||||
public FreezedList(IReadOnlyList<T> list)
|
||||
{
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
|
||||
{
|
||||
return new FreezedView<T, TView>(list, transform, reverse);
|
||||
}
|
||||
|
||||
public ISortableSynchronizedView<T, TView> CreateSortableView<TView>(Func<T, TView> transform)
|
||||
{
|
||||
return new FreezedSortableView<T, TView>(list, transform);
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
return list.Contains(item);
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return list.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
using ObservableCollections.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
@ -7,6 +6,7 @@ using System.ComponentModel;
|
||||
namespace ObservableCollections
|
||||
{
|
||||
public delegate void NotifyCollectionChangedEventHandler<T>(in NotifyCollectionChangedEventArgs<T> e);
|
||||
public delegate void NotifyViewChangedEventHandler<T, TView>(in SynchronizedViewChangedEventArgs<T, TView> e);
|
||||
|
||||
public interface IObservableCollection<T> : IReadOnlyCollection<T>
|
||||
{
|
||||
@ -25,12 +25,6 @@ namespace ObservableCollections
|
||||
{
|
||||
}
|
||||
|
||||
public interface IFreezedCollection<T>
|
||||
{
|
||||
ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false);
|
||||
ISortableSynchronizedView<T, TView> CreateSortableView<TView>(Func<T, TView> transform);
|
||||
}
|
||||
|
||||
public interface ISynchronizedView<T, TView> : IReadOnlyCollection<TView>, IDisposable
|
||||
{
|
||||
object SyncRoot { get; }
|
||||
@ -39,7 +33,7 @@ namespace ObservableCollections
|
||||
IEnumerable<(T Value, TView View)> Unfiltered { get; }
|
||||
int UnfilteredCount { get; }
|
||||
|
||||
event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
|
||||
event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
|
||||
event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
|
||||
void AttachFilter(ISynchronizedViewFilter<T> filter);
|
||||
@ -49,17 +43,6 @@ namespace ObservableCollections
|
||||
INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher);
|
||||
}
|
||||
|
||||
public interface ISortableSynchronizedView<T, TView> : ISynchronizedView<T, TView>
|
||||
{
|
||||
void Sort(IComparer<T> comparer);
|
||||
void Sort(IComparer<TView> viewComparer);
|
||||
}
|
||||
|
||||
// will be implemented in the future?
|
||||
//public interface IGroupedSynchoronizedView<T, TKey, TView> : ILookup<TKey, (T, TView)>, ISynchronizedView<T, TView>
|
||||
//{
|
||||
//}
|
||||
|
||||
public interface ISynchronizedViewList<out TView> : IReadOnlyList<TView>, IDisposable
|
||||
{
|
||||
}
|
||||
@ -67,72 +50,4 @@ namespace ObservableCollections
|
||||
public interface INotifyCollectionChangedSynchronizedView<out TView> : IReadOnlyCollection<TView>, INotifyCollectionChanged, INotifyPropertyChanged, IDisposable
|
||||
{
|
||||
}
|
||||
|
||||
public static class ObservableCollectionsExtensions
|
||||
{
|
||||
public static ISynchronizedView<T, TView> CreateSortedView<T, TKey, TView>(this IObservableCollection<T> source, Func<T, TKey> identitySelector, Func<T, TView> transform, IComparer<T> comparer)
|
||||
where TKey : notnull
|
||||
{
|
||||
return new SortedView<T, TKey, TView>(source, identitySelector, transform, comparer);
|
||||
}
|
||||
|
||||
public static ISynchronizedView<T, TView> CreateSortedView<T, TKey, TView>(this IObservableCollection<T> source, Func<T, TKey> identitySelector, Func<T, TView> transform, IComparer<TView> viewComparer)
|
||||
where TKey : notnull
|
||||
{
|
||||
return new SortedViewViewComparer<T, TKey, TView>(source, identitySelector, transform, viewComparer);
|
||||
}
|
||||
|
||||
public static ISynchronizedView<T, TView> CreateSortedView<T, TKey, TView, TCompare>(this IObservableCollection<T> source, Func<T, TKey> identitySelector, Func<T, TView> transform, Func<T, TCompare> compareSelector, bool ascending = true)
|
||||
where TKey : notnull
|
||||
{
|
||||
return source.CreateSortedView(identitySelector, transform, new AnonymousComparer<T, TCompare>(compareSelector, ascending));
|
||||
}
|
||||
|
||||
public static ISortableSynchronizedView<T, TView> CreateSortableView<T, TView>(this IFreezedCollection<T> source, Func<T, TView> transform, IComparer<T> initialSort)
|
||||
{
|
||||
var view = source.CreateSortableView(transform);
|
||||
view.Sort(initialSort);
|
||||
return view;
|
||||
}
|
||||
|
||||
public static ISortableSynchronizedView<T, TView> CreateSortableView<T, TView>(this IFreezedCollection<T> source, Func<T, TView> transform, IComparer<TView> initialViewSort)
|
||||
{
|
||||
var view = source.CreateSortableView(transform);
|
||||
view.Sort(initialViewSort);
|
||||
return view;
|
||||
}
|
||||
|
||||
public static ISortableSynchronizedView<T, TView> CreateSortableView<T, TView, TCompare>(this IFreezedCollection<T> source, Func<T, TView> transform, Func<T, TCompare> initialCompareSelector, bool ascending = true)
|
||||
{
|
||||
var view = source.CreateSortableView(transform);
|
||||
view.Sort(initialCompareSelector, ascending);
|
||||
return view;
|
||||
}
|
||||
|
||||
public static void Sort<T, TView, TCompare>(this ISortableSynchronizedView<T, TView> source, Func<T, TCompare> compareSelector, bool ascending = true)
|
||||
{
|
||||
source.Sort(new AnonymousComparer<T, TCompare>(compareSelector, ascending));
|
||||
}
|
||||
|
||||
class AnonymousComparer<T, TCompare> : IComparer<T>
|
||||
{
|
||||
readonly Func<T, TCompare> selector;
|
||||
readonly int f;
|
||||
|
||||
public AnonymousComparer(Func<T, TCompare> selector, bool ascending)
|
||||
{
|
||||
this.selector = selector;
|
||||
this.f = ascending ? 1 : -1;
|
||||
}
|
||||
|
||||
public int Compare(T? x, T? y)
|
||||
{
|
||||
if (x == null && y == null) return 0;
|
||||
if (x == null) return 1 * f;
|
||||
if (y == null) return -1 * f;
|
||||
|
||||
return Comparer<TCompare>.Default.Compare(selector(x), selector(y)) * f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,8 +3,8 @@ using System.Collections.Specialized;
|
||||
|
||||
namespace ObservableCollections
|
||||
{
|
||||
public readonly struct SynchronizedViewChangedEventArgs<T, TView>(
|
||||
NotifyViewChangedAction action,
|
||||
public readonly ref struct SynchronizedViewChangedEventArgs<T, TView>(
|
||||
NotifyCollectionChangedAction action,
|
||||
T newValue = default!,
|
||||
T oldValue = default!,
|
||||
TView newView = default!,
|
||||
@ -12,7 +12,7 @@ namespace ObservableCollections
|
||||
int newViewIndex = -1,
|
||||
int oldViewIndex = -1)
|
||||
{
|
||||
public readonly NotifyViewChangedAction Action = action;
|
||||
public readonly NotifyCollectionChangedAction Action = action;
|
||||
public readonly T NewValue = newValue;
|
||||
public readonly T OldValue = oldValue;
|
||||
public readonly TView NewView = newView;
|
||||
@ -21,16 +21,6 @@ namespace ObservableCollections
|
||||
public readonly int OldViewIndex = oldViewIndex;
|
||||
}
|
||||
|
||||
public enum NotifyViewChangedAction
|
||||
{
|
||||
Add = 0,
|
||||
Remove = 1,
|
||||
Replace = 2,
|
||||
Move = 3,
|
||||
Reset = 4,
|
||||
FilterReset = 5,
|
||||
}
|
||||
|
||||
public interface ISynchronizedViewFilter<T>
|
||||
{
|
||||
bool IsMatch(T value);
|
||||
@ -60,12 +50,12 @@ namespace ObservableCollections
|
||||
return filter == SynchronizedViewFilter<T>.Null;
|
||||
}
|
||||
|
||||
internal static void InvokeOnAdd<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev, (T value, TView view) value, int index)
|
||||
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, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev, T value, TView view, int 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)
|
||||
@ -73,17 +63,17 @@ namespace ObservableCollections
|
||||
filteredCount++;
|
||||
if (ev != null)
|
||||
{
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.Add, newValue: value, newView: view, newViewIndex: index));
|
||||
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, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev, (T value, TView view) value, int oldIndex)
|
||||
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, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev, T value, TView view, int 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)
|
||||
@ -91,17 +81,17 @@ namespace ObservableCollections
|
||||
filteredCount--;
|
||||
if (ev != null)
|
||||
{
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.Remove, oldValue: value, oldView: view, oldViewIndex: oldIndex));
|
||||
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, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev, (T value, TView view) value, int index, int 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, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev, T value, TView view, int index, int 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)
|
||||
{
|
||||
@ -109,17 +99,17 @@ namespace ObservableCollections
|
||||
var isMatch = collection.Filter.IsMatch(value);
|
||||
if (isMatch)
|
||||
{
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.Move, newValue: value, newView: view, newViewIndex: index, oldViewIndex: oldIndex));
|
||||
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, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev, (T value, TView view) value, (T value, TView view) oldValue, int index, int oldIndex = -1)
|
||||
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, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev, T value, TView view, T oldValue, TView oldView, int index, int oldIndex = -1)
|
||||
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);
|
||||
@ -129,7 +119,7 @@ namespace ObservableCollections
|
||||
{
|
||||
if (ev != null)
|
||||
{
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.Replace, newValue: value, newView: view, oldValue: oldValue, oldView: oldView, newViewIndex: index, oldViewIndex: oldIndex >= 0 ? oldIndex : index));
|
||||
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)
|
||||
@ -138,7 +128,7 @@ namespace ObservableCollections
|
||||
filteredCount--;
|
||||
if (ev != null)
|
||||
{
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.Remove, oldValue: value, oldView: view, oldViewIndex: oldIndex));
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, oldValue: value, oldView: view, oldViewIndex: oldIndex));
|
||||
}
|
||||
|
||||
}
|
||||
@ -148,17 +138,17 @@ namespace ObservableCollections
|
||||
filteredCount++;
|
||||
if (ev != null)
|
||||
{
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.Add, newValue: value, newView: view, newViewIndex: index));
|
||||
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, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev)
|
||||
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>(NotifyViewChangedAction.Reset));
|
||||
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,264 +0,0 @@
|
||||
#pragma warning disable CS0067
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
|
||||
namespace ObservableCollections.Internal
|
||||
{
|
||||
internal sealed class FreezedView<T, TView> : ISynchronizedView<T, TView>
|
||||
{
|
||||
readonly bool reverse;
|
||||
readonly List<(T, TView)> list;
|
||||
|
||||
ISynchronizedViewFilter<T, TView> filter;
|
||||
|
||||
public ISynchronizedViewFilter<T, TView> CurrentFilter
|
||||
{
|
||||
get { lock (SyncRoot) return filter; }
|
||||
}
|
||||
|
||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
|
||||
|
||||
public object SyncRoot { get; } = new object();
|
||||
|
||||
public FreezedView(IEnumerable<T> source, Func<T, TView> selector, bool reverse)
|
||||
{
|
||||
this.reverse = reverse;
|
||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||
this.list = source.Select(x => (x, selector(x))).ToList();
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return list.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = filter;
|
||||
for (var i = 0; i < list.Count; i++)
|
||||
{
|
||||
var (value, view) = list[i];
|
||||
if (invokeAddEventForCurrentElements)
|
||||
{
|
||||
filter.InvokeOnAdd(value, view, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
filter.InvokeOnAttach(value, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetFilter(Action<T, TView>? resetAction)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||
if (resetAction != null)
|
||||
{
|
||||
foreach (var (item, view) in list)
|
||||
{
|
||||
resetAction(item, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<(T, TView)> GetEnumerator()
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
if (!reverse)
|
||||
{
|
||||
foreach (var item in list)
|
||||
{
|
||||
if (filter.IsMatch(item.Item1, item.Item2))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in list.AsEnumerable().Reverse())
|
||||
{
|
||||
if (filter.IsMatch(item.Item1, item.Item2))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
||||
{
|
||||
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, null);
|
||||
}
|
||||
|
||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
|
||||
{
|
||||
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, collectionEventDispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class FreezedSortableView<T, TView> : ISortableSynchronizedView<T, TView>
|
||||
{
|
||||
readonly (T, TView)[] array;
|
||||
|
||||
ISynchronizedViewFilter<T, TView> filter;
|
||||
|
||||
public ISynchronizedViewFilter<T, TView> CurrentFilter
|
||||
{
|
||||
get { lock (SyncRoot) return filter; }
|
||||
}
|
||||
|
||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
|
||||
|
||||
public object SyncRoot { get; } = new object();
|
||||
|
||||
public FreezedSortableView(IEnumerable<T> source, Func<T, TView> selector)
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||
this.array = source.Select(x => (x, selector(x))).ToArray();
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return array.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = filter;
|
||||
for (var i = 0; i < array.Length; i++)
|
||||
{
|
||||
var (value, view) = array[i];
|
||||
if (invokeAddEventForCurrentElements)
|
||||
{
|
||||
filter.InvokeOnAdd(value, view, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
filter.InvokeOnAttach(value, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetFilter(Action<T, TView>? resetAction)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||
if (resetAction != null)
|
||||
{
|
||||
foreach (var (item, view) in array)
|
||||
{
|
||||
resetAction(item, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<(T, TView)> GetEnumerator()
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
foreach (var item in array)
|
||||
{
|
||||
if (filter.IsMatch(item.Item1, item.Item2))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public void Sort(IComparer<T> comparer)
|
||||
{
|
||||
Array.Sort(array, new TComparer(comparer));
|
||||
}
|
||||
|
||||
public void Sort(IComparer<TView> viewComparer)
|
||||
{
|
||||
Array.Sort(array, new TViewComparer(viewComparer));
|
||||
}
|
||||
|
||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
||||
{
|
||||
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, null);
|
||||
}
|
||||
|
||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
|
||||
{
|
||||
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, collectionEventDispatcher);
|
||||
}
|
||||
|
||||
class TComparer : IComparer<(T, TView)>
|
||||
{
|
||||
readonly IComparer<T> comparer;
|
||||
|
||||
public TComparer(IComparer<T> comparer)
|
||||
{
|
||||
this.comparer = comparer;
|
||||
}
|
||||
|
||||
public int Compare((T, TView) x, (T, TView) y)
|
||||
{
|
||||
return comparer.Compare(x.Item1, y.Item1);
|
||||
}
|
||||
}
|
||||
|
||||
class TViewComparer : IComparer<(T, TView)>
|
||||
{
|
||||
readonly IComparer<TView> comparer;
|
||||
|
||||
public TViewComparer(IComparer<TView> comparer)
|
||||
{
|
||||
this.comparer = comparer;
|
||||
}
|
||||
|
||||
public int Compare((T, TView) x, (T, TView) y)
|
||||
{
|
||||
return comparer.Compare(x.Item2, y.Item2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,259 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace ObservableCollections.Internal
|
||||
{
|
||||
internal class SortedView<T, TKey, TView> : ISynchronizedView<T, TView>
|
||||
where TKey : notnull
|
||||
{
|
||||
public ISynchronizedViewFilter<T, TView> CurrentFilter
|
||||
{
|
||||
get { lock (SyncRoot) return filter; }
|
||||
}
|
||||
|
||||
readonly IObservableCollection<T> source;
|
||||
readonly Func<T, TView> transform;
|
||||
readonly Func<T, TKey> identitySelector;
|
||||
readonly SortedList<(T Value, TKey Key), (T Value, TView View)> list;
|
||||
|
||||
ISynchronizedViewFilter<T, TView> filter;
|
||||
|
||||
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
|
||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
|
||||
public object SyncRoot { get; } = new object();
|
||||
|
||||
public SortedView(IObservableCollection<T> source, Func<T, TKey> identitySelector, Func<T, TView> transform, IComparer<T> comparer)
|
||||
{
|
||||
this.source = source;
|
||||
this.identitySelector = identitySelector;
|
||||
this.transform = transform;
|
||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||
lock (source.SyncRoot)
|
||||
{
|
||||
var dict = new Dictionary<(T, TKey), (T, TView)>(source.Count);
|
||||
foreach (var v in source)
|
||||
{
|
||||
dict.Add((v, identitySelector(v)), (v, transform(v)));
|
||||
}
|
||||
|
||||
this.list = new SortedList<(T Value, TKey Key), (T Value, TView View)>(dict, new Comparer(comparer));
|
||||
this.source.CollectionChanged += SourceCollectionChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return list.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = filter;
|
||||
var i = 0;
|
||||
foreach (var (_, (value, view)) in list)
|
||||
{
|
||||
if (invokeAddEventForCurrentElements)
|
||||
{
|
||||
filter.InvokeOnAdd(value, view, i++);
|
||||
}
|
||||
else
|
||||
{
|
||||
filter.InvokeOnAttach(value, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetFilter(Action<T, TView>? resetAction)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||
if (resetAction != null)
|
||||
{
|
||||
foreach (var (_, (value, view)) in list)
|
||||
{
|
||||
resetAction(value, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, null);
|
||||
}
|
||||
}
|
||||
|
||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, collectionEventDispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<(T, TView)> GetEnumerator()
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
foreach (var item in list)
|
||||
{
|
||||
if (filter.IsMatch(item.Value.Value, item.Value.View))
|
||||
{
|
||||
yield return item.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.source.CollectionChanged -= SourceCollectionChanged;
|
||||
}
|
||||
|
||||
private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<T> e)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
{
|
||||
// Add, Insert
|
||||
if (e.IsSingleItem)
|
||||
{
|
||||
var value = e.NewItem;
|
||||
var view = transform(value);
|
||||
var id = identitySelector(value);
|
||||
list.Add((value, id), (value, view));
|
||||
var index = list.IndexOfKey((value, id));
|
||||
filter.InvokeOnAdd(value, view, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var value in e.NewItems)
|
||||
{
|
||||
var view = transform(value);
|
||||
var id = identitySelector(value);
|
||||
list.Add((value, id), (value, view));
|
||||
var index = list.IndexOfKey((value, id));
|
||||
filter.InvokeOnAdd(value, view, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
{
|
||||
if (e.IsSingleItem)
|
||||
{
|
||||
var value = e.OldItem;
|
||||
var id = identitySelector(value);
|
||||
var key = (value, id);
|
||||
if (list.TryGetValue(key, out var v))
|
||||
{
|
||||
var index = list.IndexOfKey(key);
|
||||
list.RemoveAt(index);
|
||||
filter.InvokeOnRemove(v.Value, v.View, index);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var value in e.OldItems)
|
||||
{
|
||||
var id = identitySelector(value);
|
||||
var key = (value, id);
|
||||
if (list.TryGetValue(key, out var v))
|
||||
{
|
||||
var index = list.IndexOfKey((value, id));
|
||||
list.RemoveAt(index);
|
||||
filter.InvokeOnRemove(v.Value, v.View, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
// ReplaceRange is not supported in all ObservableCollections collections
|
||||
// Replace is remove old item and insert new item.
|
||||
{
|
||||
var oldValue = e.OldItem;
|
||||
var oldKey = (oldValue, identitySelector(oldValue));
|
||||
var oldIndex = -1;
|
||||
if (list.TryGetValue(oldKey, out var o))
|
||||
{
|
||||
oldIndex = list.IndexOfKey(oldKey);
|
||||
list.RemoveAt(oldIndex);
|
||||
}
|
||||
|
||||
var value = e.NewItem;
|
||||
var view = transform(value);
|
||||
var id = identitySelector(value);
|
||||
list.Add((value, id), (value, view));
|
||||
var newIndex = list.IndexOfKey((value, id));
|
||||
|
||||
filter.InvokeOnReplace((value, view), o, newIndex, oldIndex: oldIndex);
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
{
|
||||
// Move(index change) does not affect sorted list.
|
||||
var oldValue = e.OldItem;
|
||||
var oldKey = (oldValue, identitySelector(oldValue));
|
||||
if (list.TryGetValue(oldKey, out var v))
|
||||
{
|
||||
var index = list.IndexOfKey(oldKey);
|
||||
filter.InvokeOnMove(v, index, index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
list.Clear();
|
||||
filter.InvokeOnReset();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
RoutingCollectionChanged?.Invoke(e);
|
||||
CollectionStateChanged?.Invoke(e.Action);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Comparer : IComparer<(T value, TKey id)>
|
||||
{
|
||||
readonly IComparer<T> comparer;
|
||||
|
||||
public Comparer(IComparer<T> comparer)
|
||||
{
|
||||
this.comparer = comparer;
|
||||
}
|
||||
|
||||
public int Compare((T value, TKey id) x, (T value, TKey id) y)
|
||||
{
|
||||
var compare = comparer.Compare(x.value, y.value);
|
||||
if (compare == 0)
|
||||
{
|
||||
compare = Comparer<TKey>.Default.Compare(x.id, y.id);
|
||||
}
|
||||
|
||||
return compare;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,278 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
|
||||
namespace ObservableCollections.Internal
|
||||
{
|
||||
internal class SortedViewViewComparer<T, TKey, TView> : ISynchronizedView<T, TView>
|
||||
where TKey : notnull
|
||||
{
|
||||
readonly IObservableCollection<T> source;
|
||||
readonly Func<T, TView> transform;
|
||||
readonly Func<T, TKey> identitySelector;
|
||||
readonly Dictionary<TKey, TView> viewMap; // view-map needs to use in remove.
|
||||
readonly SortedList<(TView View, TKey Key), (T Value, TView View)> list;
|
||||
|
||||
ISynchronizedViewFilter<T, TView> filter;
|
||||
|
||||
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
|
||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
|
||||
public object SyncRoot { get; } = new object();
|
||||
|
||||
public ISynchronizedViewFilter<T, TView> CurrentFilter
|
||||
{
|
||||
get { lock (SyncRoot) return filter; }
|
||||
}
|
||||
|
||||
public SortedViewViewComparer(IObservableCollection<T> source, Func<T, TKey> identitySelector, Func<T, TView> transform, IComparer<TView> comparer)
|
||||
{
|
||||
this.source = source;
|
||||
this.identitySelector = identitySelector;
|
||||
this.transform = transform;
|
||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||
lock (source.SyncRoot)
|
||||
{
|
||||
var dict = new Dictionary<(TView, TKey), (T, TView)>(source.Count);
|
||||
this.viewMap = new Dictionary<TKey, TView>();
|
||||
foreach (var value in source)
|
||||
{
|
||||
var view = transform(value);
|
||||
var id = identitySelector(value);
|
||||
dict.Add((view, id), (value, view));
|
||||
viewMap.Add(id, view);
|
||||
}
|
||||
this.list = new SortedList<(TView View, TKey Key), (T Value, TView View)>(dict, new Comparer(comparer));
|
||||
this.source.CollectionChanged += SourceCollectionChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return list.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = filter;
|
||||
var i = 0;
|
||||
foreach (var (_, (value, view)) in list)
|
||||
{
|
||||
if (invokeAddEventForCurrentElements)
|
||||
{
|
||||
filter.InvokeOnAdd(value, view, i++);
|
||||
}
|
||||
else
|
||||
{
|
||||
filter.InvokeOnAttach(value, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetFilter(Action<T, TView>? resetAction)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||
if (resetAction != null)
|
||||
{
|
||||
foreach (var (_, (value, view)) in list)
|
||||
{
|
||||
resetAction(value, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, null);
|
||||
}
|
||||
}
|
||||
|
||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, collectionEventDispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<(T, TView)> GetEnumerator()
|
||||
{
|
||||
|
||||
lock (SyncRoot)
|
||||
{
|
||||
foreach (var item in list)
|
||||
{
|
||||
if (filter.IsMatch(item.Value.Value, item.Value.View))
|
||||
{
|
||||
yield return item.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.source.CollectionChanged -= SourceCollectionChanged;
|
||||
}
|
||||
|
||||
private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<T> e)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
{
|
||||
// Add, Insert
|
||||
if (e.IsSingleItem)
|
||||
{
|
||||
var value = e.NewItem;
|
||||
var view = transform(value);
|
||||
var id = identitySelector(value);
|
||||
list.Add((view, id), (value, view));
|
||||
viewMap.Add(id, view);
|
||||
var index = list.IndexOfKey((view, id));
|
||||
filter.InvokeOnAdd(value, view, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var value in e.NewItems)
|
||||
{
|
||||
var view = transform(value);
|
||||
var id = identitySelector(value);
|
||||
list.Add((view, id), (value, view));
|
||||
viewMap.Add(id, view);
|
||||
var index = list.IndexOfKey((view, id));
|
||||
filter.InvokeOnAdd(value, view, index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
{
|
||||
if (e.IsSingleItem)
|
||||
{
|
||||
var value = e.OldItem;
|
||||
var id = identitySelector(value);
|
||||
if (viewMap.Remove(id, out var view))
|
||||
{
|
||||
var key = (view, id);
|
||||
if (list.TryGetValue(key, out var v))
|
||||
{
|
||||
var index = list.IndexOfKey(key);
|
||||
list.RemoveAt(index);
|
||||
filter.InvokeOnRemove(v, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var value in e.OldItems)
|
||||
{
|
||||
var id = identitySelector(value);
|
||||
if (viewMap.Remove(id, out var view))
|
||||
{
|
||||
var key = (view, id);
|
||||
if (list.TryGetValue(key, out var v))
|
||||
{
|
||||
var index = list.IndexOfKey((view, id));
|
||||
list.RemoveAt(index);
|
||||
filter.InvokeOnRemove(v, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
// Replace is remove old item and insert new item.
|
||||
{
|
||||
var oldValue = e.OldItem;
|
||||
var oldId = identitySelector(oldValue);
|
||||
var oldIndex = -1;
|
||||
if (viewMap.Remove(oldId, out var oldView))
|
||||
{
|
||||
var oldKey = (oldView, oldId);
|
||||
if (list.TryGetValue(oldKey, out var v))
|
||||
{
|
||||
oldIndex = list.IndexOfKey(oldKey);
|
||||
list.RemoveAt(oldIndex);
|
||||
}
|
||||
}
|
||||
|
||||
var value = e.NewItem;
|
||||
var view = transform(value);
|
||||
var id = identitySelector(value);
|
||||
list.Add((view, id), (value, view));
|
||||
viewMap.Add(id, view);
|
||||
|
||||
var index = list.IndexOfKey((view, id));
|
||||
filter.InvokeOnReplace(value, view, oldValue, oldView!, index, oldIndex);
|
||||
break;
|
||||
}
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
// Move(index change) does not affect soreted dict.
|
||||
{
|
||||
var value = e.OldItem;
|
||||
var id = identitySelector(value);
|
||||
if (viewMap.TryGetValue(id, out var view))
|
||||
{
|
||||
var index = list.IndexOfKey((view, id));
|
||||
filter.InvokeOnMove(value, view, index, index);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
list.Clear();
|
||||
viewMap.Clear();
|
||||
filter.InvokeOnReset();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
RoutingCollectionChanged?.Invoke(e);
|
||||
CollectionStateChanged?.Invoke(e.Action);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Comparer : IComparer<(TView view, TKey id)>
|
||||
{
|
||||
readonly IComparer<TView> comparer;
|
||||
|
||||
public Comparer(IComparer<TView> comparer)
|
||||
{
|
||||
this.comparer = comparer;
|
||||
}
|
||||
|
||||
public int Compare((TView view, TKey id) x, (TView view, TKey id) y)
|
||||
{
|
||||
var compare = comparer.Compare(x.view, y.view);
|
||||
if (compare == 0)
|
||||
{
|
||||
compare = Comparer<TKey>.Default.Compare(x.id, y.id);
|
||||
}
|
||||
|
||||
return compare;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ namespace ObservableCollections
|
||||
}
|
||||
|
||||
public object SyncRoot { get; }
|
||||
public event Action<SynchronizedViewChangedEventArgs<KeyValuePair<TKey, TValue>, TView>>? ViewChanged;
|
||||
public event NotifyViewChangedEventHandler<KeyValuePair<TKey, TValue>, TView>? ViewChanged;
|
||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
|
||||
public ISynchronizedViewFilter<KeyValuePair<TKey, TValue>> Filter
|
||||
@ -94,7 +94,7 @@ namespace ObservableCollections
|
||||
}
|
||||
}
|
||||
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<KeyValuePair<TKey, TValue>, TView>(NotifyViewChangedAction.FilterReset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<KeyValuePair<TKey, TValue>, TView>(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ namespace ObservableCollections
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>>.Null;
|
||||
this.filteredCount = dict.Count;
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<KeyValuePair<TKey, TValue>, TView>(NotifyViewChangedAction.FilterReset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<KeyValuePair<TKey, TValue>, TView>(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -320,9 +320,9 @@ namespace ObservableCollections
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
|
||||
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform)
|
||||
{
|
||||
return new ObservableRingBuffer<T>.View<TView>(this, transform, reverse);
|
||||
return new ObservableRingBuffer<T>.View<TView>(this, transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ namespace ObservableCollections
|
||||
|
||||
ISynchronizedViewFilter<T> filter;
|
||||
|
||||
public event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
|
||||
public event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
|
||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
|
||||
public object SyncRoot { get; }
|
||||
@ -89,6 +89,7 @@ namespace ObservableCollections
|
||||
filteredCount++;
|
||||
}
|
||||
}
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,7 +99,7 @@ namespace ObservableCollections
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T>.Null;
|
||||
this.filteredCount = dict.Count;
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ namespace ObservableCollections
|
||||
|
||||
ISynchronizedViewFilter<T> filter;
|
||||
|
||||
public event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
|
||||
public event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
|
||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
|
||||
public object SyncRoot { get; }
|
||||
@ -107,7 +107,7 @@ namespace ObservableCollections
|
||||
}
|
||||
}
|
||||
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,7 +117,7 @@ namespace ObservableCollections
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T>.Null;
|
||||
this.filteredCount = list.Count;
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,11 +245,12 @@ namespace ObservableCollections
|
||||
}
|
||||
else
|
||||
{
|
||||
list.RemoveRange(e.OldStartingIndex, e.OldItems.Length); // remove from list first
|
||||
|
||||
var len = e.OldStartingIndex + e.OldItems.Length;
|
||||
for (var i = e.OldStartingIndex; i < len; i++)
|
||||
{
|
||||
var v = list[i];
|
||||
list.RemoveAt(e.OldStartingIndex + i); // should we use RemoveRange?
|
||||
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, e.OldStartingIndex + i);
|
||||
}
|
||||
}
|
||||
|
@ -19,13 +19,12 @@ namespace ObservableCollections
|
||||
{
|
||||
readonly ObservableQueue<T> source;
|
||||
readonly Func<T, TView> selector;
|
||||
readonly bool reverse;
|
||||
protected readonly Queue<(T, TView)> queue;
|
||||
int filteredCount;
|
||||
|
||||
ISynchronizedViewFilter<T> filter;
|
||||
|
||||
public event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
|
||||
public event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
|
||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
|
||||
public object SyncRoot { get; }
|
||||
@ -90,7 +89,7 @@ namespace ObservableCollections
|
||||
filteredCount++;
|
||||
}
|
||||
}
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +99,7 @@ namespace ObservableCollections
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T>.Null;
|
||||
this.filteredCount = queue.Count;
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ namespace ObservableCollections
|
||||
|
||||
ISynchronizedViewFilter<T> filter;
|
||||
|
||||
public event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
|
||||
public event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
|
||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
|
||||
public object SyncRoot { get; }
|
||||
@ -91,6 +91,7 @@ namespace ObservableCollections
|
||||
filteredCount++;
|
||||
}
|
||||
}
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +101,7 @@ namespace ObservableCollections
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T>.Null;
|
||||
this.filteredCount = ringBuffer.Count;
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ namespace ObservableCollections
|
||||
|
||||
ISynchronizedViewFilter<T> filter;
|
||||
|
||||
public event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
|
||||
public event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
|
||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
|
||||
public object SyncRoot { get; }
|
||||
@ -88,7 +88,7 @@ namespace ObservableCollections
|
||||
filteredCount++;
|
||||
}
|
||||
}
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,7 +98,7 @@ namespace ObservableCollections
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T>.Null;
|
||||
this.filteredCount = stack.Count;
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
|
||||
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace ObservableCollections.Internal
|
||||
namespace ObservableCollections
|
||||
{
|
||||
internal class SynchronizedViewList<T, TView> : ISynchronizedViewList<TView>
|
||||
{
|
||||
@ -18,18 +18,18 @@ namespace ObservableCollections.Internal
|
||||
this.parent = parent;
|
||||
lock (parent.SyncRoot)
|
||||
{
|
||||
this.listView = parent.ToList();
|
||||
listView = parent.ToList();
|
||||
parent.ViewChanged += Parent_ViewChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void Parent_ViewChanged(SynchronizedViewChangedEventArgs<T, TView> e)
|
||||
private void Parent_ViewChanged(in SynchronizedViewChangedEventArgs<T, TView> e)
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case NotifyViewChangedAction.Add: // Add or Insert
|
||||
case NotifyCollectionChangedAction.Add: // Add or Insert
|
||||
if (e.NewViewIndex == -1)
|
||||
{
|
||||
listView.Add(e.NewView);
|
||||
@ -39,7 +39,7 @@ namespace ObservableCollections.Internal
|
||||
listView.Insert(e.NewViewIndex, e.NewView);
|
||||
}
|
||||
break;
|
||||
case NotifyViewChangedAction.Remove: // Remove
|
||||
case NotifyCollectionChangedAction.Remove: // Remove
|
||||
if (e.OldViewIndex == -1) // can't gurantee correct remove if index is not provided
|
||||
{
|
||||
listView.Remove(e.OldView);
|
||||
@ -49,7 +49,7 @@ namespace ObservableCollections.Internal
|
||||
listView.RemoveAt(e.OldViewIndex);
|
||||
}
|
||||
break;
|
||||
case NotifyViewChangedAction.Replace: // Indexer
|
||||
case NotifyCollectionChangedAction.Replace: // Indexer
|
||||
if (e.NewViewIndex == -1)
|
||||
{
|
||||
var index = listView.IndexOf(e.OldView);
|
||||
@ -61,7 +61,7 @@ namespace ObservableCollections.Internal
|
||||
}
|
||||
|
||||
break;
|
||||
case NotifyViewChangedAction.Move: //Remove and Insert
|
||||
case NotifyCollectionChangedAction.Move: //Remove and Insert
|
||||
if (e.NewViewIndex == -1)
|
||||
{
|
||||
// do nothing
|
||||
@ -72,12 +72,9 @@ namespace ObservableCollections.Internal
|
||||
listView.Insert(e.NewViewIndex, e.NewView);
|
||||
}
|
||||
break;
|
||||
case NotifyViewChangedAction.Reset: // Clear
|
||||
case NotifyCollectionChangedAction.Reset: // Clear or drastic changes
|
||||
listView.Clear();
|
||||
break;
|
||||
case NotifyViewChangedAction.FilterReset:
|
||||
listView.Clear();
|
||||
foreach (var item in parent)
|
||||
foreach (var item in parent) // refresh
|
||||
{
|
||||
listView.Add(item);
|
||||
}
|
||||
@ -163,7 +160,7 @@ namespace ObservableCollections.Internal
|
||||
|
||||
switch (args.Action)
|
||||
{
|
||||
case NotifyViewChangedAction.Add:
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Add, args.NewView, args.NewViewIndex)
|
||||
{
|
||||
Collection = this,
|
||||
@ -172,7 +169,7 @@ namespace ObservableCollections.Internal
|
||||
IsInvokePropertyChanged = true
|
||||
});
|
||||
break;
|
||||
case NotifyViewChangedAction.Remove:
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Remove, args.OldView, args.OldViewIndex)
|
||||
{
|
||||
Collection = this,
|
||||
@ -181,7 +178,7 @@ namespace ObservableCollections.Internal
|
||||
IsInvokePropertyChanged = true
|
||||
});
|
||||
break;
|
||||
case NotifyViewChangedAction.Reset:
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Reset)
|
||||
{
|
||||
Collection = this,
|
||||
@ -190,7 +187,7 @@ namespace ObservableCollections.Internal
|
||||
IsInvokePropertyChanged = true
|
||||
});
|
||||
break;
|
||||
case NotifyViewChangedAction.Replace:
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Replace, args.NewView, args.OldView, args.NewViewIndex)
|
||||
{
|
||||
Collection = this,
|
||||
@ -199,7 +196,7 @@ namespace ObservableCollections.Internal
|
||||
IsInvokePropertyChanged = false
|
||||
});
|
||||
break;
|
||||
case NotifyViewChangedAction.Move:
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Move, args.NewView, args.NewViewIndex, args.OldViewIndex)
|
||||
{
|
||||
Collection = this,
|
||||
@ -245,7 +242,7 @@ namespace ObservableCollections.Internal
|
||||
|
||||
static bool IsCompatibleObject(object? value)
|
||||
{
|
||||
return (value is T) || (value == null && default(T) == null);
|
||||
return value is T || value == null && default(T) == null;
|
||||
}
|
||||
|
||||
public bool IsReadOnly => true;
|
@ -24,7 +24,6 @@ namespace ObservableCollections.Tests
|
||||
void Equal(params int[] expected)
|
||||
{
|
||||
dict.Select(x => x.Value).OrderByDescending(x => x).Should().Equal(expected);
|
||||
view.Select(x => x.Value.Value).OrderByDescending(x => x).Should().Equal(expected);
|
||||
}
|
||||
|
||||
Equal(-10, -20, -30, -40, -50);
|
||||
@ -42,146 +41,6 @@ namespace ObservableCollections.Tests
|
||||
Equal(new int[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ViewSorted()
|
||||
{
|
||||
var dict = new ObservableDictionary<int, int>();
|
||||
var view1 = dict.CreateSortedView(x => x.Key, x => new ViewContainer<int>(x.Value), x => x.Value, true);
|
||||
var view2 = dict.CreateSortedView(x => x.Key, x => new ViewContainer<int>(x.Value), x => x.Value, false);
|
||||
|
||||
dict.Add(10, 10); // 0
|
||||
dict.Add(50, 50); // 1
|
||||
dict.Add(30, 30); // 2
|
||||
dict.Add(20, 20); // 3
|
||||
dict.Add(40, 40); // 4
|
||||
|
||||
void Equal(params int[] expected)
|
||||
{
|
||||
dict.Select(x => x.Value).OrderBy(x => x).Should().Equal(expected);
|
||||
view1.Select(x => x.Value.Value).Should().Equal(expected);
|
||||
view2.Select(x => x.Value.Value).Should().Equal(expected.OrderByDescending(x => x));
|
||||
}
|
||||
|
||||
Equal(10, 20, 30, 40, 50);
|
||||
|
||||
dict[99] = 100;
|
||||
Equal(10, 20, 30, 40, 50, 100);
|
||||
|
||||
dict[10] = -5;
|
||||
Equal(-5, 20, 30, 40, 50, 100);
|
||||
|
||||
dict.Remove(20);
|
||||
Equal(-5, 30, 40, 50, 100);
|
||||
|
||||
dict.Clear();
|
||||
Equal(new int[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Freezed()
|
||||
{
|
||||
var dict = new FreezedDictionary<int, int>(new Dictionary<int, int>
|
||||
{
|
||||
[10] = 10,
|
||||
[50] = 50,
|
||||
[30] = 30,
|
||||
[20] = 20,
|
||||
[40] = 40,
|
||||
[60] = 60
|
||||
});
|
||||
|
||||
var view = dict.CreateSortableView(x => new ViewContainer<int>(x.Value));
|
||||
|
||||
view.Sort(x => x.Key, true);
|
||||
view.Select(x => x.Value.Value).Should().Equal(10, 20, 30, 40, 50, 60);
|
||||
view.Select(x => x.View).Should().Equal(10, 20, 30, 40, 50, 60);
|
||||
|
||||
view.Sort(x => x.Key, false);
|
||||
view.Select(x => x.Value.Value).Should().Equal(60, 50, 40, 30, 20, 10);
|
||||
view.Select(x => x.View).Should().Equal(60, 50, 40, 30, 20, 10);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FilterTest()
|
||||
{
|
||||
var dict = new ObservableDictionary<int, int>();
|
||||
var view1 = dict.CreateView(x => new ViewContainer<int>(x.Value));
|
||||
var view2 = dict.CreateSortedView(x => x.Key, x => new ViewContainer<int>(x.Value), x => x.Value, true);
|
||||
var view3 = dict.CreateSortedView(x => new ViewContainer<int>(x.Value), x => x.Value, viewComparer: Comparer<ViewContainer<int>>.Default);
|
||||
var filter1 = new TestFilter2<int>((x, v) => x.Value % 2 == 0);
|
||||
var filter2 = new TestFilter2<int>((x, v) => x.Value % 2 == 0);
|
||||
var filter3 = new TestFilter2<int>((x, v) => x.Value % 2 == 0);
|
||||
|
||||
dict.Add(10, -12); // 0
|
||||
dict.Add(50, -53); // 1
|
||||
dict.Add(30, -34); // 2
|
||||
dict.Add(20, -25); // 3
|
||||
dict.Add(40, -40); // 4
|
||||
|
||||
view1.AttachFilter(filter1);
|
||||
view2.AttachFilter(filter2);
|
||||
view3.AttachFilter(filter3);
|
||||
|
||||
filter1.CalledWhenTrue.Select(x => x.Item1.Value).Should().Equal(-12, -34, -40);
|
||||
filter2.CalledWhenTrue.Select(x => x.Item1.Value).Should().Equal(-40, -34, -12);
|
||||
filter3.CalledWhenTrue.Select(x => x.Item1.Value).Should().Equal(-40, -34, -12);
|
||||
|
||||
dict.Add(99, -100);
|
||||
filter1.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue.Value)).Should().Equal((NotifyCollectionChangedAction.Add, -100));
|
||||
filter2.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue.Value)).Should().Equal((NotifyCollectionChangedAction.Add, -100));
|
||||
filter3.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue.Value)).Should().Equal((NotifyCollectionChangedAction.Add, -100));
|
||||
foreach (var item in new[] { filter1, filter2, filter3 }) item.CalledOnCollectionChanged.Clear();
|
||||
|
||||
dict[10] = -1090;
|
||||
filter1.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue.Value, x.OldValue.Value)).Should().Equal((NotifyCollectionChangedAction.Replace, -1090, -12));
|
||||
filter2.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue.Value, x.OldValue.Value)).Should().Equal((NotifyCollectionChangedAction.Replace, -1090, -12));
|
||||
filter3.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue.Value, x.OldValue.Value)).Should().Equal((NotifyCollectionChangedAction.Replace, -1090, -12));
|
||||
foreach (var item in new[] { filter1, filter2, filter3 }) item.CalledOnCollectionChanged.Clear();
|
||||
|
||||
dict.Remove(20);
|
||||
filter1.CalledOnCollectionChanged.Select(x => (x.Action, x.OldValue.Value)).Should().Equal((NotifyCollectionChangedAction.Remove, -25));
|
||||
filter2.CalledOnCollectionChanged.Select(x => (x.Action, x.OldValue.Value)).Should().Equal((NotifyCollectionChangedAction.Remove, -25));
|
||||
filter3.CalledOnCollectionChanged.Select(x => (x.Action, x.OldValue.Value)).Should().Equal((NotifyCollectionChangedAction.Remove, -25));
|
||||
foreach (var item in new[] { filter1, filter2, filter3 }) item.CalledOnCollectionChanged.Clear();
|
||||
|
||||
dict.Clear();
|
||||
filter1.CalledOnCollectionChanged.Select(x => x.Action)
|
||||
.Should().Equal(NotifyCollectionChangedAction.Reset);
|
||||
filter2.CalledOnCollectionChanged.Select(x => x.Action)
|
||||
.Should().Equal(NotifyCollectionChangedAction.Reset);
|
||||
filter3.CalledOnCollectionChanged.Select(x => x.Action)
|
||||
.Should().Equal(NotifyCollectionChangedAction.Reset);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FilterAndInvokeAddEvent()
|
||||
{
|
||||
var dict = new ObservableDictionary<int, int>();
|
||||
var view1 = dict.CreateView(x => new ViewContainer<int>(x.Value));
|
||||
var filter1 = new TestFilter2<int>((x, v) => x.Value % 2 == 0);
|
||||
|
||||
dict.Add(10, -12); // 0
|
||||
dict.Add(50, -53); // 1
|
||||
dict.Add(30, -34); // 2
|
||||
dict.Add(20, -25); // 3
|
||||
dict.Add(40, -40); // 4
|
||||
|
||||
view1.AttachFilter(filter1, true);
|
||||
|
||||
filter1.CalledOnCollectionChanged.Count.Should().Be(5);
|
||||
filter1.CalledOnCollectionChanged[0].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter1.CalledOnCollectionChanged[0].NewValue.Key.Should().Be(10);
|
||||
filter1.CalledOnCollectionChanged[1].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter1.CalledOnCollectionChanged[1].NewValue.Key.Should().Be(50);
|
||||
filter1.CalledOnCollectionChanged[2].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter1.CalledOnCollectionChanged[2].NewValue.Key.Should().Be(30);
|
||||
filter1.CalledOnCollectionChanged[3].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter1.CalledOnCollectionChanged[3].NewValue.Key.Should().Be(20);
|
||||
filter1.CalledOnCollectionChanged[4].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter1.CalledOnCollectionChanged[4].NewValue.Key.Should().Be(40);
|
||||
|
||||
filter1.CalledWhenTrue.Count.Should().Be(3);
|
||||
filter1.CalledWhenFalse.Count.Should().Be(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ namespace ObservableCollections.Tests
|
||||
{
|
||||
set.Should().BeEquivalentTo(expected);
|
||||
view.Select(x => x.Value).Should().BeEquivalentTo(expected);
|
||||
view.Select(x => x.View.Value).Should().BeEquivalentTo(expected);
|
||||
}
|
||||
|
||||
Equal(10, 50, 30, 20, 40);
|
||||
@ -46,67 +45,6 @@ namespace ObservableCollections.Tests
|
||||
Equal();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Filter()
|
||||
{
|
||||
var set = new ObservableHashSet<int>();
|
||||
var view = set.CreateView(x => new ViewContainer<int>(x));
|
||||
var filter = new TestFilter<int>((x, v) => x % 3 == 0);
|
||||
|
||||
set.Add(10);
|
||||
set.Add(50);
|
||||
set.Add(30);
|
||||
set.Add(20);
|
||||
set.Add(40);
|
||||
|
||||
view.AttachFilter(filter);
|
||||
filter.CalledWhenTrue.Select(x => x.Item1).Should().Equal(30);
|
||||
filter.CalledWhenFalse.Select(x => x.Item1).Should().Equal(10, 50, 20, 40);
|
||||
|
||||
view.Select(x => x.Value).Should().Equal(30);
|
||||
|
||||
filter.Clear();
|
||||
|
||||
set.Add(33);
|
||||
set.AddRange(new[] { 98 });
|
||||
|
||||
filter.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue)).Should().Equal((NotifyCollectionChangedAction.Add, 33), (NotifyCollectionChangedAction.Add, 98));
|
||||
filter.Clear();
|
||||
|
||||
set.Remove(10);
|
||||
set.RemoveRange(new[] { 50, 30 });
|
||||
filter.CalledOnCollectionChanged.Select(x => (x.Action, x.OldValue)).Should().Equal((NotifyCollectionChangedAction.Remove, 10), (NotifyCollectionChangedAction.Remove, 50), (NotifyCollectionChangedAction.Remove, 30));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FilterAndInvokeAddEvent()
|
||||
{
|
||||
var set = new ObservableHashSet<int>();
|
||||
var view = set.CreateView(x => new ViewContainer<int>(x));
|
||||
var filter = new TestFilter<int>((x, v) => x % 3 == 0);
|
||||
|
||||
set.Add(10);
|
||||
set.Add(50);
|
||||
set.Add(30);
|
||||
set.Add(20);
|
||||
set.Add(40);
|
||||
|
||||
view.AttachFilter(filter, true);
|
||||
filter.CalledOnCollectionChanged.Count.Should().Be(5);
|
||||
filter.CalledOnCollectionChanged[0].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[0].NewValue.Should().Be(10);
|
||||
filter.CalledOnCollectionChanged[1].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[1].NewValue.Should().Be(50);
|
||||
filter.CalledOnCollectionChanged[2].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[2].NewValue.Should().Be(30);
|
||||
filter.CalledOnCollectionChanged[3].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[3].NewValue.Should().Be(20);
|
||||
filter.CalledOnCollectionChanged[4].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[4].NewValue.Should().Be(40);
|
||||
|
||||
filter.CalledWhenTrue.Count.Should().Be(1);
|
||||
filter.CalledWhenFalse.Count.Should().Be(4);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IndexOutOfRange()
|
||||
|
@ -27,14 +27,14 @@ namespace ObservableCollections.Tests
|
||||
reference.Should().Equal(expected);
|
||||
list.Should().Equal(expected);
|
||||
view.Select(x => x.Value).Should().Equal(expected);
|
||||
view.Select(x => x.View).Should().Equal(expected.Select(x => new ViewContainer<int>(x)));
|
||||
view.Filtered.Select(x => x.View).Should().Equal(expected.Select(x => new ViewContainer<int>(x)));
|
||||
}
|
||||
|
||||
void Equal2(params int[] expected)
|
||||
{
|
||||
list.Should().Equal(expected);
|
||||
view.Select(x => x.Value).Should().Equal(expected);
|
||||
view.Select(x => x.View).Should().Equal(expected.Select(x => new ViewContainer<int>(x)));
|
||||
view.Filtered.Select(x => x.View).Should().Equal(expected.Select(x => new ViewContainer<int>(x)));
|
||||
}
|
||||
|
||||
Equal(10, 50, 30, 20, 40);
|
||||
@ -69,178 +69,107 @@ namespace ObservableCollections.Tests
|
||||
Equal2(100, 400, 200, 300);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ViewSorted()
|
||||
{
|
||||
var list = new ObservableList<int>();
|
||||
var view1 = list.CreateSortedView(x => x, x => new ViewContainer<int>(x), comparer: Comparer<int>.Default);
|
||||
var view2 = list.CreateSortedView(x => x, x => new ViewContainer<int>(x), viewComparer: Comparer<ViewContainer<int>>.Default);
|
||||
var view3 = list.CreateSortedView(x => x, x => new ViewContainer<int>(x), x => x, ascending: true);
|
||||
var view4 = list.CreateSortedView(x => x, x => new ViewContainer<int>(x), x => x, ascending: false);
|
||||
//[Fact]
|
||||
//public void FilterTest()
|
||||
//{
|
||||
// var list = new ObservableList<int>();
|
||||
// var view1 = list.CreateView(x => new ViewContainer<int>(x));
|
||||
// list.AddRange(new[] { 10, 21, 30, 44, 45, 66, 90 });
|
||||
|
||||
list.Add(10); // 0
|
||||
list.Add(50); // 1
|
||||
list.Add(30); // 2
|
||||
list.Add(20); // 3
|
||||
list.Add(40); // 4
|
||||
// var filter1 = new TestFilter<int>((x, v) => x % 2 == 0);
|
||||
// var filter2 = new TestFilter<int>((x, v) => x % 2 == 0);
|
||||
// var filter3 = new TestFilter<int>((x, v) => x % 2 == 0);
|
||||
// view1.AttachFilter(filter1);
|
||||
// view2.AttachFilter(filter2);
|
||||
// view3.AttachFilter(filter3);
|
||||
|
||||
void Equal(params int[] expected)
|
||||
{
|
||||
list.Should().Equal(expected);
|
||||
// filter1.CalledWhenTrue.Select(x => x.Item1).Should().Equal(10, 30, 44, 66, 90);
|
||||
// filter2.CalledWhenTrue.Select(x => x.Item1).Should().Equal(10, 30, 44, 66, 90);
|
||||
// filter3.CalledWhenTrue.Select(x => x.Item1).Should().Equal(10, 30, 44, 66, 90);
|
||||
|
||||
var sorted = expected.OrderBy(x => x).ToArray();
|
||||
view1.Select(x => x.Value).Should().Equal(sorted);
|
||||
view2.Select(x => x.View).Should().Equal(sorted.Select(x => new ViewContainer<int>(x)));
|
||||
view3.Select(x => x.Value).Should().Equal(sorted);
|
||||
view4.Select(x => x.Value).Should().Equal(expected.OrderByDescending(x => x).ToArray());
|
||||
}
|
||||
// filter1.CalledWhenFalse.Select(x => x.Item1).Should().Equal(21, 45);
|
||||
// filter2.CalledWhenFalse.Select(x => x.Item1).Should().Equal(21, 45);
|
||||
// filter3.CalledWhenFalse.Select(x => x.Item1).Should().Equal(21, 45);
|
||||
|
||||
Equal(10, 50, 30, 20, 40);
|
||||
// view1.Select(x => x.Value).Should().Equal(10, 30, 44, 66, 90);
|
||||
// view2.Select(x => x.Value).Should().Equal(10, 30, 44, 66, 90);
|
||||
// view3.Select(x => x.Value).Should().Equal(10, 30, 44, 66, 90);
|
||||
|
||||
list.Move(3, 1);
|
||||
Equal(10, 20, 50, 30, 40);
|
||||
// filter1.Clear();
|
||||
// filter2.Clear();
|
||||
// filter3.Clear();
|
||||
|
||||
list.Insert(2, 99);
|
||||
Equal(10, 20, 99, 50, 30, 40);
|
||||
// list.Add(100);
|
||||
// list.AddRange(new[] { 101 });
|
||||
// filter1.CalledWhenTrue.Select(x => x.Item1).Should().Equal(100);
|
||||
// filter2.CalledWhenTrue.Select(x => x.Item1).Should().Equal(100);
|
||||
// filter3.CalledWhenTrue.Select(x => x.Item1).Should().Equal(100);
|
||||
// filter1.CalledWhenFalse.Select(x => x.Item1).Should().Equal(101);
|
||||
// filter2.CalledWhenFalse.Select(x => x.Item1).Should().Equal(101);
|
||||
// filter3.CalledWhenFalse.Select(x => x.Item1).Should().Equal(101);
|
||||
|
||||
list.RemoveAt(2);
|
||||
Equal(10, 20, 50, 30, 40);
|
||||
// filter1.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex)).Should().Equal((NotifyCollectionChangedAction.Add, 100, 7), (NotifyCollectionChangedAction.Add, 101, 8));
|
||||
// filter2.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex)).Should().Equal((NotifyCollectionChangedAction.Add, 100, 7), (NotifyCollectionChangedAction.Add, 101, 8));
|
||||
// filter3.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex)).Should().Equal((NotifyCollectionChangedAction.Add, 100, 7), (NotifyCollectionChangedAction.Add, 101, 8));
|
||||
|
||||
list[3] = 88;
|
||||
Equal(10, 20, 50, 88, 40);
|
||||
// foreach (var item in new[] { filter1, filter2, filter3 }) item.CalledOnCollectionChanged.Clear();
|
||||
|
||||
list.Clear();
|
||||
Equal(new int[0]);
|
||||
// list.Insert(0, 1000);
|
||||
// list.InsertRange(0, new[] { 999 });
|
||||
|
||||
list.AddRange(new[] { 100, 200, 300 });
|
||||
Equal(100, 200, 300);
|
||||
// filter1.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex)).Should().Equal((NotifyCollectionChangedAction.Add, 1000, 0), (NotifyCollectionChangedAction.Add, 999, 0));
|
||||
// filter2.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex)).Should().Equal((NotifyCollectionChangedAction.Add, 1000, 9), (NotifyCollectionChangedAction.Add, 999, 9)); // sorted index
|
||||
// filter3.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex)).Should().Equal((NotifyCollectionChangedAction.Add, 1000, 9), (NotifyCollectionChangedAction.Add, 999, 9)); // sorted index
|
||||
// foreach (var item in new[] { filter1, filter2, filter3 }) item.CalledOnCollectionChanged.Clear();
|
||||
|
||||
list.InsertRange(1, new[] { 400, 500, 600 });
|
||||
Equal(100, 400, 500, 600, 200, 300);
|
||||
// list.RemoveAt(0);
|
||||
// list.RemoveRange(0, 1);
|
||||
|
||||
list.RemoveRange(2, 2);
|
||||
Equal(100, 400, 200, 300);
|
||||
}
|
||||
// filter1.CalledOnCollectionChanged.Select(x => (x.Action, x.OldValue, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Remove, 999, 0), (NotifyCollectionChangedAction.Remove, 1000, 0));
|
||||
// filter2.CalledOnCollectionChanged.Select(x => (x.Action, x.OldValue, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Remove, 999, 9), (NotifyCollectionChangedAction.Remove, 1000, 9));
|
||||
// filter3.CalledOnCollectionChanged.Select(x => (x.Action, x.OldValue, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Remove, 999, 9), (NotifyCollectionChangedAction.Remove, 1000, 9));
|
||||
// foreach (var item in new[] { filter1, filter2, filter3 }) item.CalledOnCollectionChanged.Clear();
|
||||
|
||||
[Fact]
|
||||
public void Freezed()
|
||||
{
|
||||
var list = new FreezedList<int>(new[] { 10, 20, 50, 30, 40, 60 });
|
||||
// list[0] = 9999;
|
||||
|
||||
var view = list.CreateSortableView(x => new ViewContainer<int>(x));
|
||||
// filter1.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Replace, 9999, 0, 0));
|
||||
// filter2.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Replace, 9999, 8, 0));
|
||||
// filter3.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Replace, 9999, 8, 0));
|
||||
// foreach (var item in new[] { filter1, filter2, filter3 }) item.CalledOnCollectionChanged.Clear();
|
||||
|
||||
view.Sort(x => x, true);
|
||||
view.Select(x => x.Value).Should().Equal(10, 20, 30, 40, 50, 60);
|
||||
view.Select(x => x.View).Should().Equal(10, 20, 30, 40, 50, 60);
|
||||
// list.Move(3, 0);
|
||||
// filter1.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Move, 44, 0, 3));
|
||||
// filter2.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Move, 44, 2, 2));
|
||||
// filter3.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Move, 44, 2, 2));
|
||||
// foreach (var item in new[] { filter1, filter2, filter3 }) item.CalledOnCollectionChanged.Clear();
|
||||
|
||||
view.Sort(x => x, false);
|
||||
view.Select(x => x.Value).Should().Equal(60, 50, 40, 30, 20, 10);
|
||||
view.Select(x => x.View).Should().Equal(60, 50, 40, 30, 20, 10);
|
||||
}
|
||||
// list.Clear();
|
||||
// filter1.CalledOnCollectionChanged.Select(x => x.Action).Should().Equal(NotifyCollectionChangedAction.Reset);
|
||||
// filter2.CalledOnCollectionChanged.Select(x => x.Action).Should().Equal(NotifyCollectionChangedAction.Reset);
|
||||
// filter3.CalledOnCollectionChanged.Select(x => x.Action).Should().Equal(NotifyCollectionChangedAction.Reset);
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public void FilterTest()
|
||||
{
|
||||
var list = new ObservableList<int>();
|
||||
var view1 = list.CreateView(x => new ViewContainer<int>(x));
|
||||
var view2 = list.CreateSortedView(x => x, x => new ViewContainer<int>(x), comparer: Comparer<int>.Default);
|
||||
var view3 = list.CreateSortedView(x => x, x => new ViewContainer<int>(x), viewComparer: Comparer<ViewContainer<int>>.Default);
|
||||
list.AddRange(new[] { 10, 21, 30, 44, 45, 66, 90 });
|
||||
//[Fact]
|
||||
//public void FilterAndInvokeAddEvent()
|
||||
//{
|
||||
// var list = new ObservableList<int>();
|
||||
// var view1 = list.CreateView(x => new ViewContainer<int>(x));
|
||||
// list.AddRange(new[] { 10, 21, 30, 44 });
|
||||
|
||||
var filter1 = new TestFilter<int>((x, v) => x % 2 == 0);
|
||||
var filter2 = new TestFilter<int>((x, v) => x % 2 == 0);
|
||||
var filter3 = new TestFilter<int>((x, v) => x % 2 == 0);
|
||||
view1.AttachFilter(filter1);
|
||||
view2.AttachFilter(filter2);
|
||||
view3.AttachFilter(filter3);
|
||||
// var filter1 = new TestFilter<int>((x, v) => x % 2 == 0);
|
||||
// view1.AttachFilter(filter1);
|
||||
|
||||
filter1.CalledWhenTrue.Select(x => x.Item1).Should().Equal(10, 30, 44, 66, 90);
|
||||
filter2.CalledWhenTrue.Select(x => x.Item1).Should().Equal(10, 30, 44, 66, 90);
|
||||
filter3.CalledWhenTrue.Select(x => x.Item1).Should().Equal(10, 30, 44, 66, 90);
|
||||
// filter1.CalledOnCollectionChanged[0].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
// filter1.CalledOnCollectionChanged[0].NewValue.Should().Be(10);
|
||||
// filter1.CalledOnCollectionChanged[1].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
// filter1.CalledOnCollectionChanged[1].NewValue.Should().Be(21);
|
||||
// filter1.CalledOnCollectionChanged[2].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
// filter1.CalledOnCollectionChanged[2].NewValue.Should().Be(30);
|
||||
// filter1.CalledOnCollectionChanged[3].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
// filter1.CalledOnCollectionChanged[3].NewValue.Should().Be(44);
|
||||
|
||||
filter1.CalledWhenFalse.Select(x => x.Item1).Should().Equal(21, 45);
|
||||
filter2.CalledWhenFalse.Select(x => x.Item1).Should().Equal(21, 45);
|
||||
filter3.CalledWhenFalse.Select(x => x.Item1).Should().Equal(21, 45);
|
||||
|
||||
view1.Select(x => x.Value).Should().Equal(10, 30, 44, 66, 90);
|
||||
view2.Select(x => x.Value).Should().Equal(10, 30, 44, 66, 90);
|
||||
view3.Select(x => x.Value).Should().Equal(10, 30, 44, 66, 90);
|
||||
|
||||
filter1.Clear();
|
||||
filter2.Clear();
|
||||
filter3.Clear();
|
||||
|
||||
list.Add(100);
|
||||
list.AddRange(new[] { 101 });
|
||||
filter1.CalledWhenTrue.Select(x => x.Item1).Should().Equal(100);
|
||||
filter2.CalledWhenTrue.Select(x => x.Item1).Should().Equal(100);
|
||||
filter3.CalledWhenTrue.Select(x => x.Item1).Should().Equal(100);
|
||||
filter1.CalledWhenFalse.Select(x => x.Item1).Should().Equal(101);
|
||||
filter2.CalledWhenFalse.Select(x => x.Item1).Should().Equal(101);
|
||||
filter3.CalledWhenFalse.Select(x => x.Item1).Should().Equal(101);
|
||||
|
||||
filter1.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex)).Should().Equal((NotifyCollectionChangedAction.Add, 100, 7), (NotifyCollectionChangedAction.Add, 101, 8));
|
||||
filter2.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex)).Should().Equal((NotifyCollectionChangedAction.Add, 100, 7), (NotifyCollectionChangedAction.Add, 101, 8));
|
||||
filter3.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex)).Should().Equal((NotifyCollectionChangedAction.Add, 100, 7), (NotifyCollectionChangedAction.Add, 101, 8));
|
||||
|
||||
foreach (var item in new[] { filter1, filter2, filter3 }) item.CalledOnCollectionChanged.Clear();
|
||||
|
||||
list.Insert(0, 1000);
|
||||
list.InsertRange(0, new[] { 999 });
|
||||
|
||||
filter1.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex)).Should().Equal((NotifyCollectionChangedAction.Add, 1000, 0), (NotifyCollectionChangedAction.Add, 999, 0));
|
||||
filter2.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex)).Should().Equal((NotifyCollectionChangedAction.Add, 1000, 9), (NotifyCollectionChangedAction.Add, 999, 9)); // sorted index
|
||||
filter3.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex)).Should().Equal((NotifyCollectionChangedAction.Add, 1000, 9), (NotifyCollectionChangedAction.Add, 999, 9)); // sorted index
|
||||
foreach (var item in new[] { filter1, filter2, filter3 }) item.CalledOnCollectionChanged.Clear();
|
||||
|
||||
list.RemoveAt(0);
|
||||
list.RemoveRange(0, 1);
|
||||
|
||||
filter1.CalledOnCollectionChanged.Select(x => (x.Action, x.OldValue, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Remove, 999, 0), (NotifyCollectionChangedAction.Remove, 1000, 0));
|
||||
filter2.CalledOnCollectionChanged.Select(x => (x.Action, x.OldValue, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Remove, 999, 9), (NotifyCollectionChangedAction.Remove, 1000, 9));
|
||||
filter3.CalledOnCollectionChanged.Select(x => (x.Action, x.OldValue, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Remove, 999, 9), (NotifyCollectionChangedAction.Remove, 1000, 9));
|
||||
foreach (var item in new[] { filter1, filter2, filter3 }) item.CalledOnCollectionChanged.Clear();
|
||||
|
||||
list[0] = 9999;
|
||||
|
||||
filter1.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Replace, 9999, 0, 0));
|
||||
filter2.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Replace, 9999, 8, 0));
|
||||
filter3.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Replace, 9999, 8, 0));
|
||||
foreach (var item in new[] { filter1, filter2, filter3 }) item.CalledOnCollectionChanged.Clear();
|
||||
|
||||
list.Move(3, 0);
|
||||
filter1.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Move, 44, 0, 3));
|
||||
filter2.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Move, 44, 2, 2));
|
||||
filter3.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Move, 44, 2, 2));
|
||||
foreach (var item in new[] { filter1, filter2, filter3 }) item.CalledOnCollectionChanged.Clear();
|
||||
|
||||
list.Clear();
|
||||
filter1.CalledOnCollectionChanged.Select(x => x.Action).Should().Equal(NotifyCollectionChangedAction.Reset);
|
||||
filter2.CalledOnCollectionChanged.Select(x => x.Action).Should().Equal(NotifyCollectionChangedAction.Reset);
|
||||
filter3.CalledOnCollectionChanged.Select(x => x.Action).Should().Equal(NotifyCollectionChangedAction.Reset);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FilterAndInvokeAddEvent()
|
||||
{
|
||||
var list = new ObservableList<int>();
|
||||
var view1 = list.CreateView(x => new ViewContainer<int>(x));
|
||||
list.AddRange(new[] { 10, 21, 30, 44 });
|
||||
|
||||
var filter1 = new TestFilter<int>((x, v) => x % 2 == 0);
|
||||
view1.AttachFilter(filter1, true);
|
||||
|
||||
filter1.CalledOnCollectionChanged[0].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter1.CalledOnCollectionChanged[0].NewValue.Should().Be(10);
|
||||
filter1.CalledOnCollectionChanged[1].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter1.CalledOnCollectionChanged[1].NewValue.Should().Be(21);
|
||||
filter1.CalledOnCollectionChanged[2].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter1.CalledOnCollectionChanged[2].NewValue.Should().Be(30);
|
||||
filter1.CalledOnCollectionChanged[3].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter1.CalledOnCollectionChanged[3].NewValue.Should().Be(44);
|
||||
|
||||
filter1.CalledWhenTrue.Count.Should().Be(3);
|
||||
filter1.CalledWhenFalse.Count.Should().Be(1);
|
||||
}
|
||||
// filter1.CalledWhenTrue.Count.Should().Be(3);
|
||||
// filter1.CalledWhenFalse.Count.Should().Be(1);
|
||||
//}
|
||||
}
|
||||
}
|
@ -27,7 +27,6 @@ namespace ObservableCollections.Tests
|
||||
{
|
||||
queue.Should().Equal(expected);
|
||||
view.Select(x => x.Value).Should().Equal(expected);
|
||||
view.Select(x => x.View).Should().Equal(expected.Select(x => new ViewContainer<int>(x)));
|
||||
}
|
||||
|
||||
Equal(10, 50, 30, 20, 40);
|
||||
@ -50,72 +49,72 @@ namespace ObservableCollections.Tests
|
||||
Equal();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Filter()
|
||||
{
|
||||
var queue = new ObservableQueue<int>();
|
||||
var view = queue.CreateView(x => new ViewContainer<int>(x));
|
||||
var filter = new TestFilter<int>((x, v) => x % 3 == 0);
|
||||
//[Fact]
|
||||
//public void Filter()
|
||||
//{
|
||||
// var queue = new ObservableQueue<int>();
|
||||
// var view = queue.CreateView(x => new ViewContainer<int>(x));
|
||||
// var filter = new TestFilter<int>((x, v) => x % 3 == 0);
|
||||
|
||||
queue.Enqueue(10);
|
||||
queue.Enqueue(50);
|
||||
queue.Enqueue(30);
|
||||
queue.Enqueue(20);
|
||||
queue.Enqueue(40);
|
||||
// queue.Enqueue(10);
|
||||
// queue.Enqueue(50);
|
||||
// queue.Enqueue(30);
|
||||
// queue.Enqueue(20);
|
||||
// queue.Enqueue(40);
|
||||
|
||||
view.AttachFilter(filter);
|
||||
filter.CalledWhenTrue.Select(x => x.Item1).Should().Equal(30);
|
||||
filter.CalledWhenFalse.Select(x => x.Item1).Should().Equal(10, 50, 20, 40);
|
||||
// view.AttachFilter(filter);
|
||||
// filter.CalledWhenTrue.Select(x => x.Item1).Should().Equal(30);
|
||||
// filter.CalledWhenFalse.Select(x => x.Item1).Should().Equal(10, 50, 20, 40);
|
||||
|
||||
view.Select(x => x.Value).Should().Equal(30);
|
||||
// view.Select(x => x.Value).Should().Equal(30);
|
||||
|
||||
filter.Clear();
|
||||
// filter.Clear();
|
||||
|
||||
queue.Enqueue(33);
|
||||
queue.EnqueueRange(new[] { 98 });
|
||||
// queue.Enqueue(33);
|
||||
// queue.EnqueueRange(new[] { 98 });
|
||||
|
||||
filter.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex)).Should().Equal((NotifyCollectionChangedAction.Add, 33, 5), (NotifyCollectionChangedAction.Add, 98, 6));
|
||||
filter.Clear();
|
||||
// filter.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex)).Should().Equal((NotifyCollectionChangedAction.Add, 33, 5), (NotifyCollectionChangedAction.Add, 98, 6));
|
||||
// filter.Clear();
|
||||
|
||||
queue.Dequeue();
|
||||
queue.DequeueRange(2);
|
||||
filter.CalledOnCollectionChanged.Select(x => (x.Action, x.OldValue, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Remove, 10, 0), (NotifyCollectionChangedAction.Remove, 50, 0), (NotifyCollectionChangedAction.Remove, 30, 0));
|
||||
}
|
||||
// queue.Dequeue();
|
||||
// queue.DequeueRange(2);
|
||||
// filter.CalledOnCollectionChanged.Select(x => (x.Action, x.OldValue, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Remove, 10, 0), (NotifyCollectionChangedAction.Remove, 50, 0), (NotifyCollectionChangedAction.Remove, 30, 0));
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public void FilterAndInvokeAddEvent()
|
||||
{
|
||||
var queue = new ObservableQueue<int>();
|
||||
var view = queue.CreateView(x => new ViewContainer<int>(x));
|
||||
var filter = new TestFilter<int>((x, v) => x % 3 == 0);
|
||||
//[Fact]
|
||||
//public void FilterAndInvokeAddEvent()
|
||||
//{
|
||||
// var queue = new ObservableQueue<int>();
|
||||
// var view = queue.CreateView(x => new ViewContainer<int>(x));
|
||||
// var filter = new TestFilter<int>((x, v) => x % 3 == 0);
|
||||
|
||||
queue.Enqueue(10);
|
||||
queue.Enqueue(50);
|
||||
queue.Enqueue(30);
|
||||
queue.Enqueue(20);
|
||||
queue.Enqueue(40);
|
||||
// queue.Enqueue(10);
|
||||
// queue.Enqueue(50);
|
||||
// queue.Enqueue(30);
|
||||
// queue.Enqueue(20);
|
||||
// queue.Enqueue(40);
|
||||
|
||||
view.AttachFilter(filter, true);
|
||||
// view.AttachFilter(filter, true);
|
||||
|
||||
filter.CalledOnCollectionChanged.Count.Should().Be(5);
|
||||
filter.CalledOnCollectionChanged[0].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[0].NewValue.Should().Be(10);
|
||||
filter.CalledOnCollectionChanged[0].NewViewIndex.Should().Be(0);
|
||||
filter.CalledOnCollectionChanged[1].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[1].NewValue.Should().Be(50);
|
||||
filter.CalledOnCollectionChanged[1].NewViewIndex.Should().Be(1);
|
||||
filter.CalledOnCollectionChanged[2].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[2].NewValue.Should().Be(30);
|
||||
filter.CalledOnCollectionChanged[2].NewViewIndex.Should().Be(2);
|
||||
filter.CalledOnCollectionChanged[3].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[3].NewValue.Should().Be(20);
|
||||
filter.CalledOnCollectionChanged[3].NewViewIndex.Should().Be(3);
|
||||
filter.CalledOnCollectionChanged[4].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[4].NewValue.Should().Be(40);
|
||||
filter.CalledOnCollectionChanged[4].NewViewIndex.Should().Be(4);
|
||||
// filter.CalledOnCollectionChanged.Count.Should().Be(5);
|
||||
// filter.CalledOnCollectionChanged[0].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
// filter.CalledOnCollectionChanged[0].NewValue.Should().Be(10);
|
||||
// filter.CalledOnCollectionChanged[0].NewViewIndex.Should().Be(0);
|
||||
// filter.CalledOnCollectionChanged[1].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
// filter.CalledOnCollectionChanged[1].NewValue.Should().Be(50);
|
||||
// filter.CalledOnCollectionChanged[1].NewViewIndex.Should().Be(1);
|
||||
// filter.CalledOnCollectionChanged[2].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
// filter.CalledOnCollectionChanged[2].NewValue.Should().Be(30);
|
||||
// filter.CalledOnCollectionChanged[2].NewViewIndex.Should().Be(2);
|
||||
// filter.CalledOnCollectionChanged[3].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
// filter.CalledOnCollectionChanged[3].NewValue.Should().Be(20);
|
||||
// filter.CalledOnCollectionChanged[3].NewViewIndex.Should().Be(3);
|
||||
// filter.CalledOnCollectionChanged[4].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
// filter.CalledOnCollectionChanged[4].NewValue.Should().Be(40);
|
||||
// filter.CalledOnCollectionChanged[4].NewViewIndex.Should().Be(4);
|
||||
|
||||
filter.CalledWhenTrue.Count.Should().Be(1);
|
||||
filter.CalledWhenFalse.Count.Should().Be(4);
|
||||
}
|
||||
// filter.CalledWhenTrue.Count.Should().Be(1);
|
||||
// filter.CalledWhenFalse.Count.Should().Be(4);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ namespace ObservableCollections.Tests
|
||||
{
|
||||
buf.Should().Equal(expected);
|
||||
view.Select(x => x.Value).Should().Equal(expected);
|
||||
view.Select(x => x.View).Should().Equal(expected.Select(x => new ViewContainer<int>(x)));
|
||||
}
|
||||
|
||||
Equal(10, 50, 30, 20, 40);
|
||||
@ -65,7 +64,6 @@ namespace ObservableCollections.Tests
|
||||
{
|
||||
buf.Should().Equal(expected);
|
||||
view.Select(x => x.Value).Should().Equal(expected);
|
||||
view.Select(x => x.View).Should().Equal(expected.Select(x => new ViewContainer<int>(x)));
|
||||
}
|
||||
|
||||
buf.AddLast(10);
|
||||
|
@ -27,7 +27,6 @@ namespace ObservableCollections.Tests
|
||||
{
|
||||
stack.Should().Equal(expected);
|
||||
view.Select(x => x.Value).Should().Equal(expected);
|
||||
view.Select(x => x.View).Should().Equal(expected.Select(x => new ViewContainer<int>(x)));
|
||||
}
|
||||
|
||||
Equal(40, 20, 30, 50, 10);
|
||||
@ -50,72 +49,6 @@ namespace ObservableCollections.Tests
|
||||
Equal();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Filter()
|
||||
{
|
||||
var stack = new ObservableStack<int>();
|
||||
var view = stack.CreateView(x => new ViewContainer<int>(x));
|
||||
var filter = new TestFilter<int>((x, v) => x % 3 == 0);
|
||||
|
||||
stack.Push(10);
|
||||
stack.Push(50);
|
||||
stack.Push(30);
|
||||
stack.Push(20);
|
||||
stack.Push(40);
|
||||
|
||||
view.AttachFilter(filter);
|
||||
filter.CalledWhenTrue.Select(x => x.Item1).Should().Equal(30);
|
||||
filter.CalledWhenFalse.Select(x => x.Item1).Should().Equal(40, 20, 50, 10);
|
||||
|
||||
view.Select(x => x.Value).Should().Equal(30);
|
||||
|
||||
filter.Clear();
|
||||
|
||||
stack.Push(33);
|
||||
stack.PushRange(new[] { 98 });
|
||||
|
||||
filter.CalledOnCollectionChanged.Select(x => (x.Action, x.NewValue, x.NewViewIndex)).Should().Equal((NotifyCollectionChangedAction.Add, 33, 0), (NotifyCollectionChangedAction.Add, 98, 0));
|
||||
filter.Clear();
|
||||
|
||||
stack.Pop();
|
||||
stack.PopRange(2);
|
||||
filter.CalledOnCollectionChanged.Select(x => (x.Action, x.OldValue, x.OldViewIndex)).Should().Equal((NotifyCollectionChangedAction.Remove, 98, 0), (NotifyCollectionChangedAction.Remove, 33, 0), (NotifyCollectionChangedAction.Remove, 40, 0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FilterAndInvokeAddEvent()
|
||||
{
|
||||
var stack = new ObservableStack<int>();
|
||||
var view = stack.CreateView(x => new ViewContainer<int>(x));
|
||||
var filter = new TestFilter<int>((x, v) => x % 3 == 0);
|
||||
|
||||
stack.Push(10);
|
||||
stack.Push(50);
|
||||
stack.Push(30);
|
||||
stack.Push(20);
|
||||
stack.Push(40);
|
||||
|
||||
view.AttachFilter(filter, true);
|
||||
filter.CalledOnCollectionChanged.Count.Should().Be(5);
|
||||
filter.CalledOnCollectionChanged[4].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[4].NewValue.Should().Be(10);
|
||||
filter.CalledOnCollectionChanged[4].NewViewIndex.Should().Be(0);
|
||||
filter.CalledOnCollectionChanged[3].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[3].NewValue.Should().Be(50);
|
||||
filter.CalledOnCollectionChanged[3].NewViewIndex.Should().Be(0);
|
||||
filter.CalledOnCollectionChanged[2].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[2].NewValue.Should().Be(30);
|
||||
filter.CalledOnCollectionChanged[2].NewViewIndex.Should().Be(0);
|
||||
filter.CalledOnCollectionChanged[1].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[1].NewValue.Should().Be(20);
|
||||
filter.CalledOnCollectionChanged[1].NewViewIndex.Should().Be(0);
|
||||
filter.CalledOnCollectionChanged[0].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[0].NewValue.Should().Be(40);
|
||||
filter.CalledOnCollectionChanged[0].NewViewIndex.Should().Be(0);
|
||||
|
||||
filter.CalledWhenTrue.Count.Should().Be(1);
|
||||
filter.CalledWhenFalse.Count.Should().Be(4);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,70 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace ObservableCollections.Tests;
|
||||
|
||||
public class SortedViewTest
|
||||
{
|
||||
[Fact]
|
||||
public void Sort()
|
||||
{
|
||||
var list = new ObservableList<int>();
|
||||
var sortedView = list.CreateSortedView(
|
||||
x => x,
|
||||
x => new ViewContainer<int>(x),
|
||||
Comparer<int>.Default);
|
||||
|
||||
list.Add(10);
|
||||
list.Add(50);
|
||||
list.Add(30);
|
||||
list.Add(20);
|
||||
list.Add(40);
|
||||
|
||||
using var e = sortedView.GetEnumerator();
|
||||
e.MoveNext().Should().BeTrue();
|
||||
e.Current.Value.Should().Be(10);
|
||||
e.MoveNext().Should().BeTrue();
|
||||
e.Current.Value.Should().Be(20);
|
||||
e.MoveNext().Should().BeTrue();
|
||||
e.Current.Value.Should().Be(30);
|
||||
e.MoveNext().Should().BeTrue();
|
||||
e.Current.Value.Should().Be(40);
|
||||
e.MoveNext().Should().BeTrue();
|
||||
e.Current.Value.Should().Be(50);
|
||||
e.MoveNext().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ObserveIndex()
|
||||
{
|
||||
var list = new ObservableList<int>();
|
||||
var sortedView = list.CreateSortedView(
|
||||
x => x,
|
||||
x => new ViewContainer<int>(x),
|
||||
Comparer<int>.Default);
|
||||
|
||||
var filter = new TestFilter<int>((value, view) => value % 2 == 0);
|
||||
list.Add(50);
|
||||
list.Add(10);
|
||||
|
||||
sortedView.AttachFilter(filter);
|
||||
|
||||
list.Add(20);
|
||||
filter.CalledOnCollectionChanged[0].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[0].NewValue.Should().Be(20);
|
||||
filter.CalledOnCollectionChanged[0].NewView.Should().Be(new ViewContainer<int>(20));
|
||||
filter.CalledOnCollectionChanged[0].NewViewIndex.Should().Be(1);
|
||||
|
||||
list.Remove(20);
|
||||
filter.CalledOnCollectionChanged[1].Action.Should().Be(NotifyCollectionChangedAction.Remove);
|
||||
filter.CalledOnCollectionChanged[1].OldValue.Should().Be(20);
|
||||
filter.CalledOnCollectionChanged[1].OldView.Should().Be(new ViewContainer<int>(20));
|
||||
filter.CalledOnCollectionChanged[1].OldViewIndex.Should().Be(1);
|
||||
|
||||
list[1] = 999; // from 10(at 0 in original) to 999
|
||||
filter.CalledOnCollectionChanged[2].Action.Should().Be(NotifyCollectionChangedAction.Replace);
|
||||
filter.CalledOnCollectionChanged[2].NewValue.Should().Be(999);
|
||||
filter.CalledOnCollectionChanged[2].OldValue.Should().Be(10);
|
||||
filter.CalledOnCollectionChanged[2].NewViewIndex.Should().Be(1);
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace ObservableCollections.Tests;
|
||||
|
||||
public class SortedViewViewComparerTest
|
||||
{
|
||||
[Fact]
|
||||
public void Sort()
|
||||
{
|
||||
var list = new ObservableList<int>();
|
||||
var sortedView = list.CreateSortedView(
|
||||
x => x,
|
||||
x => new ViewContainer<int>(x),
|
||||
Comparer<ViewContainer<int>>.Default);
|
||||
|
||||
list.Add(10);
|
||||
list.Add(50);
|
||||
list.Add(30);
|
||||
list.Add(20);
|
||||
list.Add(40);
|
||||
|
||||
using var e = sortedView.GetEnumerator();
|
||||
e.MoveNext().Should().BeTrue();
|
||||
e.Current.Value.Should().Be(10);
|
||||
e.MoveNext().Should().BeTrue();
|
||||
e.Current.Value.Should().Be(20);
|
||||
e.MoveNext().Should().BeTrue();
|
||||
e.Current.Value.Should().Be(30);
|
||||
e.MoveNext().Should().BeTrue();
|
||||
e.Current.Value.Should().Be(40);
|
||||
e.MoveNext().Should().BeTrue();
|
||||
e.Current.Value.Should().Be(50);
|
||||
e.MoveNext().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ObserveIndex()
|
||||
{
|
||||
var list = new ObservableList<int>();
|
||||
var sortedView = list.CreateSortedView(
|
||||
x => x,
|
||||
x => new ViewContainer<int>(x),
|
||||
Comparer<ViewContainer<int>>.Default);
|
||||
|
||||
var filter = new TestFilter<int>((value, view) => value % 2 == 0);
|
||||
list.Add(50);
|
||||
list.Add(10);
|
||||
|
||||
sortedView.AttachFilter(filter);
|
||||
|
||||
list.Add(20);
|
||||
filter.CalledOnCollectionChanged[0].Action.Should().Be(NotifyCollectionChangedAction.Add);
|
||||
filter.CalledOnCollectionChanged[0].NewValue.Should().Be(20);
|
||||
filter.CalledOnCollectionChanged[0].NewView.Should().Be(new ViewContainer<int>(20));
|
||||
filter.CalledOnCollectionChanged[0].NewViewIndex.Should().Be(1);
|
||||
|
||||
list.Remove(20);
|
||||
filter.CalledOnCollectionChanged[1].Action.Should().Be(NotifyCollectionChangedAction.Remove);
|
||||
filter.CalledOnCollectionChanged[1].OldValue.Should().Be(20);
|
||||
filter.CalledOnCollectionChanged[1].OldView.Should().Be(new ViewContainer<int>(20));
|
||||
filter.CalledOnCollectionChanged[1].OldViewIndex.Should().Be(1);
|
||||
|
||||
list[1] = 999; // from 10(at 0 in original) to 999
|
||||
filter.CalledOnCollectionChanged[2].Action.Should().Be(NotifyCollectionChangedAction.Replace);
|
||||
filter.CalledOnCollectionChanged[2].NewValue.Should().Be(999);
|
||||
filter.CalledOnCollectionChanged[2].OldValue.Should().Be(10);
|
||||
filter.CalledOnCollectionChanged[2].NewView.Should().Be(new ViewContainer<int>(999));
|
||||
filter.CalledOnCollectionChanged[2].OldView.Should().Be(new ViewContainer<int>(10));
|
||||
filter.CalledOnCollectionChanged[2].NewViewIndex.Should().Be(1);
|
||||
filter.CalledOnCollectionChanged[2].OldViewIndex.Should().Be(0);
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
using ObservableCollections;
|
||||
|
||||
namespace ObservableCollections.Tests;
|
||||
|
||||
public class ToNotifyCollectionChangedTest
|
||||
@ -43,7 +45,7 @@ public class ToNotifyCollectionChangedTest
|
||||
var view = list.CreateView(x => $"${x}");
|
||||
var notify = view.ToNotifyCollectionChanged();
|
||||
|
||||
view.AttachFilter((value, view) => value % 2 == 0);
|
||||
view.AttachFilter((value) => value % 2 == 0);
|
||||
|
||||
list.Add(4);
|
||||
|
||||
|
@ -31,83 +31,81 @@ namespace ObservableCollections.Tests
|
||||
}
|
||||
}
|
||||
|
||||
public class TestFilter<T> : ISynchronizedViewFilter<T, ViewContainer<T>>
|
||||
{
|
||||
readonly Func<T, ViewContainer<T>, bool> filter;
|
||||
public List<(T, ViewContainer<T>)> CalledWhenTrue = new();
|
||||
public List<(T, ViewContainer<T>)> CalledWhenFalse = new();
|
||||
public List<SynchronizedViewChangedEventArgs<T, ViewContainer<T>>> CalledOnCollectionChanged = new();
|
||||
//public class TestFilter<T> : ISynchronizedViewFilter<T>
|
||||
//{
|
||||
// readonly Func<T, bool> filter;
|
||||
// public List<SynchronizedViewChangedEventArgs<T, ViewContainer<T>>> CalledOnCollectionChanged = new();
|
||||
|
||||
public TestFilter(Func<T, ViewContainer<T>, bool> filter)
|
||||
{
|
||||
this.filter = filter;
|
||||
}
|
||||
// public TestFilter(Func<T, bool> filter)
|
||||
// {
|
||||
// this.filter = filter;
|
||||
// }
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
CalledWhenTrue.Clear();
|
||||
CalledWhenFalse.Clear();
|
||||
CalledOnCollectionChanged.Clear();
|
||||
}
|
||||
// public void Clear()
|
||||
// {
|
||||
// CalledWhenTrue.Clear();
|
||||
// CalledWhenFalse.Clear();
|
||||
// CalledOnCollectionChanged.Clear();
|
||||
// }
|
||||
|
||||
public bool IsMatch(T value, ViewContainer<T> view)
|
||||
{
|
||||
return this.filter.Invoke(value, view);
|
||||
}
|
||||
// public bool IsMatch(T value)
|
||||
// {
|
||||
// return this.filter.Invoke(value);
|
||||
// }
|
||||
|
||||
public void OnCollectionChanged(in SynchronizedViewChangedEventArgs<T, ViewContainer<T>> args)
|
||||
{
|
||||
CalledOnCollectionChanged.Add(args);
|
||||
}
|
||||
// public void OnCollectionChanged(in SynchronizedViewChangedEventArgs<T, ViewContainer<T>> args)
|
||||
// {
|
||||
// CalledOnCollectionChanged.Add(args);
|
||||
// }
|
||||
|
||||
public void WhenTrue(T value, ViewContainer<T> view)
|
||||
{
|
||||
CalledWhenTrue.Add((value, view));
|
||||
}
|
||||
// public void WhenTrue(T value, ViewContainer<T> view)
|
||||
// {
|
||||
// CalledWhenTrue.Add((value, view));
|
||||
// }
|
||||
|
||||
public void WhenFalse(T value, ViewContainer<T> view)
|
||||
{
|
||||
CalledWhenFalse.Add((value, view));
|
||||
}
|
||||
}
|
||||
// public void WhenFalse(T value, ViewContainer<T> view)
|
||||
// {
|
||||
// CalledWhenFalse.Add((value, view));
|
||||
// }
|
||||
//}
|
||||
|
||||
public class TestFilter2<T> : ISynchronizedViewFilter<KeyValuePair<T, T>, ViewContainer<T>>
|
||||
{
|
||||
readonly Func<KeyValuePair<T, T>, ViewContainer<T>, bool> filter;
|
||||
public List<(KeyValuePair<T, T>, ViewContainer<T>)> CalledWhenTrue = new();
|
||||
public List<(KeyValuePair<T, T>, ViewContainer<T>)> CalledWhenFalse = new();
|
||||
public List<SynchronizedViewChangedEventArgs<KeyValuePair<T, T>, ViewContainer<T>>> CalledOnCollectionChanged = new();
|
||||
//public class TestFilter2<T> : ISynchronizedViewFilter<KeyValuePair<T, T>, ViewContainer<T>>
|
||||
//{
|
||||
// readonly Func<KeyValuePair<T, T>, ViewContainer<T>, bool> filter;
|
||||
// public List<(KeyValuePair<T, T>, ViewContainer<T>)> CalledWhenTrue = new();
|
||||
// public List<(KeyValuePair<T, T>, ViewContainer<T>)> CalledWhenFalse = new();
|
||||
// public List<SynchronizedViewChangedEventArgs<KeyValuePair<T, T>, ViewContainer<T>>> CalledOnCollectionChanged = new();
|
||||
|
||||
public TestFilter2(Func<KeyValuePair<T, T>, ViewContainer<T>, bool> filter)
|
||||
{
|
||||
this.filter = filter;
|
||||
}
|
||||
// public TestFilter2(Func<KeyValuePair<T, T>, ViewContainer<T>, bool> filter)
|
||||
// {
|
||||
// this.filter = filter;
|
||||
// }
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
CalledWhenTrue.Clear();
|
||||
CalledWhenFalse.Clear();
|
||||
CalledOnCollectionChanged.Clear();
|
||||
}
|
||||
// public void Clear()
|
||||
// {
|
||||
// CalledWhenTrue.Clear();
|
||||
// CalledWhenFalse.Clear();
|
||||
// CalledOnCollectionChanged.Clear();
|
||||
// }
|
||||
|
||||
public bool IsMatch(KeyValuePair<T, T> value, ViewContainer<T> view)
|
||||
{
|
||||
return this.filter.Invoke(value, view);
|
||||
}
|
||||
// public bool IsMatch(KeyValuePair<T, T> value, ViewContainer<T> view)
|
||||
// {
|
||||
// return this.filter.Invoke(value, view);
|
||||
// }
|
||||
|
||||
public void OnCollectionChanged(in SynchronizedViewChangedEventArgs<KeyValuePair<T, T>, ViewContainer<T>> args)
|
||||
{
|
||||
CalledOnCollectionChanged.Add(args);
|
||||
}
|
||||
// public void OnCollectionChanged(in SynchronizedViewChangedEventArgs<KeyValuePair<T, T>, ViewContainer<T>> args)
|
||||
// {
|
||||
// CalledOnCollectionChanged.Add(args);
|
||||
// }
|
||||
|
||||
public void WhenTrue(KeyValuePair<T, T> value, ViewContainer<T> view)
|
||||
{
|
||||
CalledWhenTrue.Add((value, view));
|
||||
}
|
||||
// public void WhenTrue(KeyValuePair<T, T> value, ViewContainer<T> view)
|
||||
// {
|
||||
// CalledWhenTrue.Add((value, view));
|
||||
// }
|
||||
|
||||
public void WhenFalse(KeyValuePair<T, T> value, ViewContainer<T> view)
|
||||
{
|
||||
CalledWhenFalse.Add((value, view));
|
||||
}
|
||||
}
|
||||
// public void WhenFalse(KeyValuePair<T, T> value, ViewContainer<T> view)
|
||||
// {
|
||||
// CalledWhenFalse.Add((value, view));
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user