Skip to content

Commit

Permalink
Merge pull request #20 from Cysharp/hadashiA/initial-elements-add
Browse files Browse the repository at this point in the history
Support for firing add event for initial elements
  • Loading branch information
neuecc authored Feb 15, 2024
2 parents 9c1ee7b + 7b75bf2 commit eff61e9
Show file tree
Hide file tree
Showing 30 changed files with 501 additions and 153 deletions.
76 changes: 59 additions & 17 deletions sandbox/ConsoleApp/Program.cs
Original file line number Diff line number Diff line change
@@ -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));

var viewModels = models.CreateView(x => new ViewModel
{
Id = x,
Value = "@" + x
});

viewModels.AttachFilter(new HogeFilter(), true);

// Basic sample, use like ObservableCollection<T>.
// CollectionChanged observes all collection modification
var list = new ObservableList<int>();
var view = list.CreateView(x => x.ToString() + "$");
models.Add(100);

list.Add(10);
list.Add(20);
list.AddRange(new[] { 30, 40, 50 });
list[1] = 60;
list.RemoveAt(3);
foreach (var (x, xs) in viewModels)
{
System.Console.WriteLine(xs.Value);
}

foreach (var (_, v) in view)
class ViewModel
{
// 10$, 60$, 30$, 50$
Console.WriteLine(v);
public int Id { get; set; }
public string Value { get; set; }
}

// Dispose view is unsubscribe collection changed event.
view.Dispose();
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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public interface ISynchronizedView<T, TView> : IReadOnlyCollection<(T Value, TVi
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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public CloneCollection(IEnumerable<T> source)
}
else
{
var array = ArrayPool<T>.Shared.Rent(count);
var array = ArrayPool<T>.Shared.Rent(16);

var i = 0;
foreach (var item in source)
Expand All @@ -75,8 +75,8 @@ static void TryEnsureCapacity(ref T[] array, int index)
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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,22 @@ public int Count
}
}

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);
}
}
}
}
Expand Down Expand Up @@ -133,14 +141,22 @@ public int Count
}
}

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);
}
}
}
}
Expand Down Expand Up @@ -178,7 +194,6 @@ public void ResetFilter(Action<T, TView> resetAction)

public void Dispose()
{

}

public void Sort(IComparer<T> comparer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged
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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,21 @@ public int Count
}
}

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);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,21 @@ public int Count
}
}

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);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,23 @@ public void Dispose()
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);
}
}
}
}
Expand Down Expand Up @@ -118,44 +127,44 @@ private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<KeyValu
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:
Expand All @@ -168,4 +177,4 @@ private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<KeyValu
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,21 @@ public int Count
}
}

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);
}
}
}
}
Expand Down
Loading

0 comments on commit eff61e9

Please sign in to comment.