Merge pull request #20 from Cysharp/hadashiA/initial-elements-add

Support for firing add event for initial elements
This commit is contained in:
Yoshifumi Kawai 2024-02-15 10:41:14 +09:00 committed by GitHub
commit eff61e9f76
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 504 additions and 156 deletions

View File

@ -1,24 +1,66 @@
using ObservableCollections;
using System;
using System.Collections.Specialized;
using System;
using System.Linq;
using ObservableCollections;
var models = new ObservableList<int>(Enumerable.Range(0, 10));
// Basic sample, use like ObservableCollection<T>.
// CollectionChanged observes all collection modification
var list = new ObservableList<int>();
var view = list.CreateView(x => x.ToString() + "$");
list.Add(10);
list.Add(20);
list.AddRange(new[] { 30, 40, 50 });
list[1] = 60;
list.RemoveAt(3);
foreach (var (_, v) in view)
var viewModels = models.CreateView(x => new ViewModel
{
// 10$, 60$, 30$, 50$
Console.WriteLine(v);
Id = x,
Value = "@" + x
});
viewModels.AttachFilter(new HogeFilter(), true);
models.Add(100);
foreach (var (x, xs) in viewModels)
{
System.Console.WriteLine(xs.Value);
}
// Dispose view is unsubscribe collection changed event.
view.Dispose();
class ViewModel
{
public int Id { get; set; }
public string Value { get; set; }
}
class HogeFilter : ISynchronizedViewFilter<int, ViewModel>
{
public bool IsMatch(int value, ViewModel view)
{
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(
ChangedKind changedKind,
int value,
ViewModel view,
in NotifyCollectionChangedEventArgs<int> eventArgs)
{
switch (changedKind)
{
case ChangedKind.Add:
view.Value += " Add";
break;
case ChangedKind.Remove:
view.Value += " Remove";
break;
case ChangedKind.Move:
view.Value += $" Move {eventArgs.OldStartingIndex} {eventArgs.NewStartingIndex}";
break;
default:
throw new ArgumentOutOfRangeException(nameof(changedKind), changedKind, null);
}
}
}

View File

@ -28,7 +28,7 @@ namespace ObservableCollections
event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged;
event Action<NotifyCollectionChangedAction> CollectionStateChanged;
void AttachFilter(ISynchronizedViewFilter<T, TView> filter);
void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForInitialElements = false);
void ResetFilter(Action<T, TView> resetAction);
INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged();
}

View File

@ -49,7 +49,7 @@ namespace ObservableCollections.Internal
}
else
{
var array = ArrayPool<T>.Shared.Rent(count);
var array = ArrayPool<T>.Shared.Rent(16);
var i = 0;
foreach (var item in source)
@ -75,8 +75,8 @@ namespace ObservableCollections.Internal
if (array.Length == index)
{
ArrayPool<T>.Shared.Return(array, RuntimeHelpersEx.IsReferenceOrContainsReferences<T>());
array = ArrayPool<T>.Shared.Rent(index * 2);
}
array = ArrayPool<T>.Shared.Rent(index * 2);
}
public void Dispose()

View File

@ -38,14 +38,22 @@ namespace ObservableCollections.Internal
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in list)
for (var i = 0; i < list.Count; i++)
{
filter.InvokeOnAttach(value, view);
var (value, view) = list[i];
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
}
}
}
@ -133,14 +141,22 @@ namespace ObservableCollections.Internal
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in array)
for (var i = 0; i < array.Length; i++)
{
filter.InvokeOnAttach(value, view);
var (value, view) = array[i];
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
}
}
}
@ -178,7 +194,6 @@ namespace ObservableCollections.Internal
public void Dispose()
{
}
public void Sort(IComparer<T> comparer)

View File

@ -55,7 +55,7 @@ namespace ObservableCollections.Internal
remove { parent.RoutingCollectionChanged -= value; }
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) => parent.AttachFilter(filter);
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false) => parent.AttachFilter(filter, invokeAddEventForCurrentElements);
public void ResetFilter(Action<T, TView> resetAction) => parent.ResetFilter(resetAction);
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged() => this;
public void Dispose()

View File

@ -51,14 +51,21 @@ namespace ObservableCollections.Internal
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (_, (value, view)) in dict)
{
filter.InvokeOnAttach(value, view);
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, -1));
}
else
{
filter.InvokeOnAttach(value, view);
}
}
}
}

View File

@ -55,14 +55,21 @@ namespace ObservableCollections.Internal
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (_, (value, view)) in dict)
{
filter.InvokeOnAttach(value, view);
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, -1));
}
else
{
filter.InvokeOnAttach(value, view);
}
}
}
}

View File

@ -55,14 +55,23 @@ namespace ObservableCollections
this.source.CollectionChanged -= SourceCollectionChanged;
}
public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var v in dict)
{
filter.InvokeOnAttach(new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1), v.Value.Item2);
var value = new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1);
var view = v.Value.Item2;
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Add(value, -1));
}
else
{
filter.InvokeOnAttach(value, view);
}
}
}
}
@ -118,44 +127,44 @@ namespace ObservableCollections
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
{
var v = selector(e.NewItem);
dict.Add(e.NewItem.Key, (e.NewItem.Value, v));
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e);
}
{
var v = selector(e.NewItem);
dict.Add(e.NewItem.Key, (e.NewItem.Value, v));
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e);
}
break;
case NotifyCollectionChangedAction.Remove:
{
if (dict.Remove(e.OldItem.Key, out var v))
{
if (dict.Remove(e.OldItem.Key, out var v))
{
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, v.Item1), v.Item2), e);
}
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, v.Item1), v.Item2), e);
}
}
break;
case NotifyCollectionChangedAction.Replace:
{
if (dict.Remove(e.OldItem.Key, out var oldView))
{
if (dict.Remove(e.OldItem.Key, out var oldView))
{
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, oldView.Item1), oldView.Item2), e);
}
var v = selector(e.NewItem);
dict[e.NewItem.Key] = (e.NewItem.Value, v);
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e);
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, oldView.Item1), oldView.Item2), e);
}
var v = selector(e.NewItem);
dict[e.NewItem.Key] = (e.NewItem.Value, v);
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e);
}
break;
case NotifyCollectionChangedAction.Reset:
{
if (!filter.IsNullFilter())
{
if (!filter.IsNullFilter())
foreach (var item in dict)
{
foreach (var item in dict)
{
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2), e);
}
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2), e);
}
dict.Clear();
}
dict.Clear();
}
break;
case NotifyCollectionChangedAction.Move: // ObservableDictionary have no Move operation.
default:
@ -168,4 +177,4 @@ namespace ObservableCollections
}
}
}
}
}

View File

@ -51,14 +51,21 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (_, (value, view)) in dict)
{
filter.InvokeOnAttach(value, view);
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd((value, view), NotifyCollectionChangedEventArgs<T>.Add(value, -1));
}
else
{
filter.InvokeOnAttach(value, view);
}
}
}
}

View File

@ -53,14 +53,23 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in list)
for (var i = 0; i < list.Count; i++)
{
filter.InvokeOnAttach(value, view);
var (value, view) = list[i];
if (invokeAddEventForCurrentElements)
{
var eventArgs = NotifyCollectionChangedEventArgs<T>.Add(value, i);
filter.InvokeOnAdd(value, view, eventArgs);
}
else
{
filter.InvokeOnAttach(value, view);
}
}
}
}
@ -193,24 +202,24 @@ namespace ObservableCollections
break;
case NotifyCollectionChangedAction.Replace:
// ObservableList does not support replace range
{
var v = (e.NewItem, selector(e.NewItem));
{
var v = (e.NewItem, selector(e.NewItem));
var oldItem = list[e.NewStartingIndex];
list[e.NewStartingIndex] = v;
var oldItem = list[e.NewStartingIndex];
list[e.NewStartingIndex] = v;
filter.InvokeOnRemove(oldItem, e);
filter.InvokeOnAdd(v, e);
break;
}
filter.InvokeOnRemove(oldItem, e);
filter.InvokeOnAdd(v, e);
break;
}
case NotifyCollectionChangedAction.Move:
{
var removeItem = list[e.OldStartingIndex];
list.RemoveAt(e.OldStartingIndex);
list.Insert(e.NewStartingIndex, removeItem);
{
var removeItem = list[e.OldStartingIndex];
list.RemoveAt(e.OldStartingIndex);
list.Insert(e.NewStartingIndex, removeItem);
filter.InvokeOnMove(removeItem, e);
}
filter.InvokeOnMove(removeItem, e);
}
break;
case NotifyCollectionChangedAction.Reset:
if (!filter.IsNullFilter())

View File

@ -53,14 +53,23 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
var i = 0;
foreach (var (value, view) in queue)
{
filter.InvokeOnAttach(value, view);
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
i++;
}
}
}

View File

@ -54,14 +54,22 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in ringBuffer)
for (var i = 0; i < ringBuffer.Count; i++)
{
filter.InvokeOnAttach(value, view);
var (value, view) = ringBuffer[i];
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
}
}
}

View File

@ -53,14 +53,23 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
var i = 0;
foreach (var (value, view) in stack)
{
filter.InvokeOnAttach(value, view);
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
i++;
}
}
}

View File

@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.Text;
#if (NETSTANDARD2_0 || NET_STANDARD_2_0 || NET_4_6) && !UNITY_2021_1_OR_NEWER
#if NETSTANDARD2_0 || NET_STANDARD_2_0 || NET_4_6
namespace System.Diagnostics.CodeAnalysis
{

View File

@ -28,7 +28,7 @@ namespace ObservableCollections
event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
void AttachFilter(ISynchronizedViewFilter<T, TView> filter);
void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForInitialElements = false);
void ResetFilter(Action<T, TView>? resetAction);
INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged();
}

View File

@ -38,14 +38,22 @@ namespace ObservableCollections.Internal
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in list)
for (var i = 0; i < list.Count; i++)
{
filter.InvokeOnAttach(value, view);
var (value, view) = list[i];
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
}
}
}
@ -133,14 +141,22 @@ namespace ObservableCollections.Internal
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in array)
for (var i = 0; i < array.Length; i++)
{
filter.InvokeOnAttach(value, view);
var (value, view) = array[i];
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
}
}
}
@ -178,7 +194,6 @@ namespace ObservableCollections.Internal
public void Dispose()
{
}
public void Sort(IComparer<T> comparer)

View File

@ -55,7 +55,7 @@ namespace ObservableCollections.Internal
remove { parent.RoutingCollectionChanged -= value; }
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) => parent.AttachFilter(filter);
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false) => parent.AttachFilter(filter, invokeAddEventForCurrentElements);
public void ResetFilter(Action<T, TView>? resetAction) => parent.ResetFilter(resetAction);
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged() => this;
public void Dispose()

View File

@ -51,14 +51,21 @@ namespace ObservableCollections.Internal
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (_, (value, view)) in dict)
{
filter.InvokeOnAttach(value, view);
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, -1));
}
else
{
filter.InvokeOnAttach(value, view);
}
}
}
}

View File

@ -55,14 +55,21 @@ namespace ObservableCollections.Internal
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (_, (value, view)) in dict)
{
filter.InvokeOnAttach(value, view);
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, -1));
}
else
{
filter.InvokeOnAttach(value, view);
}
}
}
}

View File

@ -55,14 +55,23 @@ namespace ObservableCollections
this.source.CollectionChanged -= SourceCollectionChanged;
}
public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var v in dict)
{
filter.InvokeOnAttach(new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1), v.Value.Item2);
var value = new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1);
var view = v.Value.Item2;
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Add(value, -1));
}
else
{
filter.InvokeOnAttach(value, view);
}
}
}
}
@ -118,44 +127,44 @@ namespace ObservableCollections
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
{
var v = selector(e.NewItem);
dict.Add(e.NewItem.Key, (e.NewItem.Value, v));
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e);
}
{
var v = selector(e.NewItem);
dict.Add(e.NewItem.Key, (e.NewItem.Value, v));
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e);
}
break;
case NotifyCollectionChangedAction.Remove:
{
if (dict.Remove(e.OldItem.Key, out var v))
{
if (dict.Remove(e.OldItem.Key, out var v))
{
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, v.Item1), v.Item2), e);
}
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, v.Item1), v.Item2), e);
}
}
break;
case NotifyCollectionChangedAction.Replace:
{
if (dict.Remove(e.OldItem.Key, out var oldView))
{
if (dict.Remove(e.OldItem.Key, out var oldView))
{
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, oldView.Item1), oldView.Item2), e);
}
var v = selector(e.NewItem);
dict[e.NewItem.Key] = (e.NewItem.Value, v);
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e);
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, oldView.Item1), oldView.Item2), e);
}
var v = selector(e.NewItem);
dict[e.NewItem.Key] = (e.NewItem.Value, v);
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e);
}
break;
case NotifyCollectionChangedAction.Reset:
{
if (!filter.IsNullFilter())
{
if (!filter.IsNullFilter())
foreach (var item in dict)
{
foreach (var item in dict)
{
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2), e);
}
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2), e);
}
dict.Clear();
}
dict.Clear();
}
break;
case NotifyCollectionChangedAction.Move: // ObservableDictionary have no Move operation.
default:
@ -168,4 +177,4 @@ namespace ObservableCollections
}
}
}
}
}

View File

@ -51,14 +51,21 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (_, (value, view)) in dict)
{
filter.InvokeOnAttach(value, view);
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd((value, view), NotifyCollectionChangedEventArgs<T>.Add(value, -1));
}
else
{
filter.InvokeOnAttach(value, view);
}
}
}
}

View File

@ -53,14 +53,23 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in list)
for (var i = 0; i < list.Count; i++)
{
filter.InvokeOnAttach(value, view);
var (value, view) = list[i];
if (invokeAddEventForCurrentElements)
{
var eventArgs = NotifyCollectionChangedEventArgs<T>.Add(value, i);
filter.InvokeOnAdd(value, view, eventArgs);
}
else
{
filter.InvokeOnAttach(value, view);
}
}
}
}
@ -193,24 +202,24 @@ namespace ObservableCollections
break;
case NotifyCollectionChangedAction.Replace:
// ObservableList does not support replace range
{
var v = (e.NewItem, selector(e.NewItem));
{
var v = (e.NewItem, selector(e.NewItem));
var oldItem = list[e.NewStartingIndex];
list[e.NewStartingIndex] = v;
var oldItem = list[e.NewStartingIndex];
list[e.NewStartingIndex] = v;
filter.InvokeOnRemove(oldItem, e);
filter.InvokeOnAdd(v, e);
break;
}
filter.InvokeOnRemove(oldItem, e);
filter.InvokeOnAdd(v, e);
break;
}
case NotifyCollectionChangedAction.Move:
{
var removeItem = list[e.OldStartingIndex];
list.RemoveAt(e.OldStartingIndex);
list.Insert(e.NewStartingIndex, removeItem);
{
var removeItem = list[e.OldStartingIndex];
list.RemoveAt(e.OldStartingIndex);
list.Insert(e.NewStartingIndex, removeItem);
filter.InvokeOnMove(removeItem, e);
}
filter.InvokeOnMove(removeItem, e);
}
break;
case NotifyCollectionChangedAction.Reset:
if (!filter.IsNullFilter())

View File

@ -53,14 +53,23 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
var i = 0;
foreach (var (value, view) in queue)
{
filter.InvokeOnAttach(value, view);
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
i++;
}
}
}

View File

@ -54,14 +54,22 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in ringBuffer)
for (var i = 0; i < ringBuffer.Count; i++)
{
filter.InvokeOnAttach(value, view);
var (value, view) = ringBuffer[i];
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
}
}
}

View File

@ -53,14 +53,23 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{
lock (SyncRoot)
{
this.filter = filter;
var i = 0;
foreach (var (value, view) in stack)
{
filter.InvokeOnAttach(value, view);
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
i++;
}
}
}

View File

@ -154,5 +154,36 @@ namespace ObservableCollections.Tests
.OrderBy(x => x.Value)
.Should().Equal((ChangedKind.Remove, -1090), (ChangedKind.Remove, -100), (ChangedKind.Remove, -53), (ChangedKind.Remove, -40), (ChangedKind.Remove, -34));
}
[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].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[0].value.Key.Should().Be(10);
filter1.CalledOnCollectionChanged[1].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[1].value.Key.Should().Be(50);
filter1.CalledOnCollectionChanged[2].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[2].value.Key.Should().Be(30);
filter1.CalledOnCollectionChanged[3].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[3].value.Key.Should().Be(20);
filter1.CalledOnCollectionChanged[4].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[4].value.Key.Should().Be(40);
filter1.CalledWhenTrue.Count.Should().Be(3);
filter1.CalledWhenFalse.Count.Should().Be(2);
}
}
}

View File

@ -76,5 +76,35 @@ namespace ObservableCollections.Tests
set.RemoveRange(new[] { 50, 30 });
filter.CalledOnCollectionChanged.Select(x => (x.changedKind, x.value)).Should().Equal((ChangedKind.Remove, 10), (ChangedKind.Remove, 50), (ChangedKind.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].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[0].value.Should().Be(10);
filter.CalledOnCollectionChanged[1].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[1].value.Should().Be(50);
filter.CalledOnCollectionChanged[2].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[2].value.Should().Be(30);
filter.CalledOnCollectionChanged[3].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[3].value.Should().Be(20);
filter.CalledOnCollectionChanged[4].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[4].value.Should().Be(40);
filter.CalledWhenTrue.Count.Should().Be(1);
filter.CalledWhenFalse.Count.Should().Be(4);
}
}
}

View File

@ -218,5 +218,28 @@ namespace ObservableCollections.Tests
filter2.CalledOnCollectionChanged.Select(x => (x.changedKind, x.value)).Should().Equal((ChangedKind.Remove, 21), (ChangedKind.Remove, 30), (ChangedKind.Remove, 44), (ChangedKind.Remove, 45), (ChangedKind.Remove, 66), (ChangedKind.Remove, 90), (ChangedKind.Remove, 100), (ChangedKind.Remove, 101), (ChangedKind.Remove, 9999));
filter3.CalledOnCollectionChanged.Select(x => (x.changedKind, x.value)).Should().Equal((ChangedKind.Remove, 21), (ChangedKind.Remove, 30), (ChangedKind.Remove, 44), (ChangedKind.Remove, 45), (ChangedKind.Remove, 66), (ChangedKind.Remove, 90), (ChangedKind.Remove, 100), (ChangedKind.Remove, 101), (ChangedKind.Remove, 9999));
}
[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].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[0].value.Should().Be(10);
filter1.CalledOnCollectionChanged[1].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[1].value.Should().Be(21);
filter1.CalledOnCollectionChanged[2].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[2].value.Should().Be(30);
filter1.CalledOnCollectionChanged[3].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[3].value.Should().Be(44);
filter1.CalledWhenTrue.Count.Should().Be(3);
filter1.CalledWhenFalse.Count.Should().Be(1);
}
}
}
}

View File

@ -80,5 +80,36 @@ namespace ObservableCollections.Tests
queue.DequeueRange(2);
filter.CalledOnCollectionChanged.Select(x => (x.changedKind, x.value)).Should().Equal((ChangedKind.Remove, 10), (ChangedKind.Remove, 50), (ChangedKind.Remove, 30));
}
[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);
view.AttachFilter(filter, true);
filter.CalledOnCollectionChanged.Count.Should().Be(5);
filter.CalledOnCollectionChanged[0].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[0].value.Should().Be(10);
filter.CalledOnCollectionChanged[1].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[1].value.Should().Be(50);
filter.CalledOnCollectionChanged[2].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[2].value.Should().Be(30);
filter.CalledOnCollectionChanged[3].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[3].value.Should().Be(20);
filter.CalledOnCollectionChanged[4].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[4].value.Should().Be(40);
filter.CalledWhenTrue.Count.Should().Be(1);
filter.CalledWhenFalse.Count.Should().Be(4);
}
}
}

View File

@ -80,5 +80,36 @@ namespace ObservableCollections.Tests
stack.PopRange(2);
filter.CalledOnCollectionChanged.Select(x => (x.changedKind, x.value)).Should().Equal((ChangedKind.Remove, 98), (ChangedKind.Remove, 33), (ChangedKind.Remove, 40));
}
[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].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[4].value.Should().Be(10);
filter.CalledOnCollectionChanged[3].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[3].value.Should().Be(50);
filter.CalledOnCollectionChanged[2].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[2].value.Should().Be(30);
filter.CalledOnCollectionChanged[1].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[1].value.Should().Be(20);
filter.CalledOnCollectionChanged[0].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[0].value.Should().Be(40);
filter.CalledWhenTrue.Count.Should().Be(1);
filter.CalledWhenFalse.Count.Should().Be(4);
}
}
}