ObservableList

This commit is contained in:
neuecc 2024-08-27 15:50:19 +09:00
parent 9ffd0417ba
commit ce624265f3
4 changed files with 139 additions and 153 deletions

View File

@ -45,11 +45,11 @@ namespace ObservableCollections
}
}
internal class DirectCollectionEventDispatcher : ICollectionEventDispatcher
internal class InlineCollectionEventDispatcher : ICollectionEventDispatcher
{
public static readonly ICollectionEventDispatcher Instance = new DirectCollectionEventDispatcher();
public static readonly ICollectionEventDispatcher Instance = new InlineCollectionEventDispatcher();
DirectCollectionEventDispatcher()
InlineCollectionEventDispatcher()
{
}

View File

@ -1,6 +1,5 @@
using ObservableCollections.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
@ -45,6 +44,7 @@ namespace ObservableCollections
void AttachFilter(ISynchronizedViewFilter<T> filter);
void ResetFilter();
ISynchronizedViewList<TView> ToViewList();
INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged();
INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher);
}

View File

@ -10,7 +10,8 @@ namespace ObservableCollections.Internal
internal class SynchronizedViewList<T, TView> : ISynchronizedViewList<TView>
{
readonly ISynchronizedView<T, TView> parent;
readonly List<TView> listView;
protected readonly List<TView> listView;
protected readonly object gate = new object();
public SynchronizedViewList(ISynchronizedView<T, TView> parent)
{
@ -24,8 +25,8 @@ namespace ObservableCollections.Internal
private void Parent_ViewChanged(SynchronizedViewChangedEventArgs<T, TView> e)
{
// event is called inside lock(parent.SyncRoot)
// TODO: invoke in ICollectionEventDispatcher?
lock (gate)
{
switch (e.Action)
{
case NotifyViewChangedAction.Add: // Add or Insert
@ -78,22 +79,52 @@ namespace ObservableCollections.Internal
listView.Clear();
foreach (var item in parent)
{
listView.Add(item.View);
listView.Add(item);
}
break;
default:
break;
}
OnCollectionChanged(e);
}
}
protected virtual void OnCollectionChanged(in SynchronizedViewChangedEventArgs<T, TView> args)
{
}
public TView this[int index] => listView[index];
public TView this[int index]
{
get
{
lock (gate)
{
return listView[index];
}
}
}
public int Count => listView.Count;
public int Count
{
get
{
lock (gate)
{
return listView.Count;
}
}
}
public IEnumerator<TView> GetEnumerator()
{
return listView.GetEnumerator();
lock (gate)
{
foreach (var item in listView)
{
yield return item;
}
}
}
IEnumerator IEnumerable.GetEnumerator()
@ -107,73 +138,32 @@ namespace ObservableCollections.Internal
}
}
internal class NotifyCollectionChangedSynchronizedView<T, TView> :
SynchronizedViewList<T, TView>,
INotifyCollectionChangedSynchronizedView<TView>,
ISynchronizedViewFilter<T, TView>
IList<TView>, IList
{
static readonly PropertyChangedEventArgs CountPropertyChangedEventArgs = new("Count");
static readonly Action<NotifyCollectionChangedEventArgs> raiseChangedEventInvoke = RaiseChangedEvent;
readonly ISynchronizedView<T, TView> parent;
readonly ISynchronizedViewFilter<T, TView> currentFilter;
readonly ICollectionEventDispatcher eventDispatcher;
public NotifyCollectionChangedSynchronizedView(ISynchronizedView<T, TView> parent, ICollectionEventDispatcher? eventDispatcher)
{
this.parent = parent;
this.eventDispatcher = eventDispatcher ?? DirectCollectionEventDispatcher.Instance;
currentFilter = parent.Filter;
parent.AttachFilter(this);
}
public int Count => parent.Count;
public event NotifyCollectionChangedEventHandler? CollectionChanged;
public event PropertyChangedEventHandler? PropertyChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged
public NotifyCollectionChangedSynchronizedView(ISynchronizedView<T, TView> parent, ICollectionEventDispatcher? eventDispatcher)
: base(parent)
{
add { parent.CollectionStateChanged += value; }
remove { parent.CollectionStateChanged -= value; }
this.eventDispatcher = eventDispatcher ?? InlineCollectionEventDispatcher.Instance;
}
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged
protected override void OnCollectionChanged(in SynchronizedViewChangedEventArgs<T, TView> args)
{
add { parent.RoutingCollectionChanged += value; }
remove { parent.RoutingCollectionChanged -= value; }
}
public void Dispose()
{
parent.Dispose();
}
public IEnumerator<TView> GetEnumerator()
{
foreach (var (value, view) in parent)
{
yield return view;
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public bool IsMatch(T value, TView view) => currentFilter.IsMatch(value, view);
public void WhenTrue(T value, TView view) => currentFilter.WhenTrue(value, view);
public void WhenFalse(T value, TView view) => currentFilter.WhenFalse(value, view);
public void OnCollectionChanged(in SynchronizedViewChangedEventArgs<T, TView> args)
{
currentFilter.OnCollectionChanged(args);
if (CollectionChanged == null && PropertyChanged == null) return;
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
case NotifyViewChangedAction.Add:
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Add, args.NewView, args.NewViewIndex)
{
Collection = this,
@ -182,7 +172,7 @@ namespace ObservableCollections.Internal
IsInvokePropertyChanged = true
});
break;
case NotifyCollectionChangedAction.Remove:
case NotifyViewChangedAction.Remove:
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Remove, args.OldView, args.OldViewIndex)
{
Collection = this,
@ -191,7 +181,7 @@ namespace ObservableCollections.Internal
IsInvokePropertyChanged = true
});
break;
case NotifyCollectionChangedAction.Reset:
case NotifyViewChangedAction.Reset:
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Reset)
{
Collection = this,
@ -200,7 +190,7 @@ namespace ObservableCollections.Internal
IsInvokePropertyChanged = true
});
break;
case NotifyCollectionChangedAction.Replace:
case NotifyViewChangedAction.Replace:
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Replace, args.NewView, args.OldView, args.NewViewIndex)
{
Collection = this,
@ -209,7 +199,7 @@ namespace ObservableCollections.Internal
IsInvokePropertyChanged = false
});
break;
case NotifyCollectionChangedAction.Move:
case NotifyViewChangedAction.Move:
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Move, args.NewView, args.NewViewIndex, args.OldViewIndex)
{
Collection = this,
@ -235,30 +225,12 @@ namespace ObservableCollections.Internal
self.PropertyChanged?.Invoke(self, CountPropertyChangedEventArgs);
}
}
}
internal class ListNotifyCollectionChangedSynchronizedView<T, TView>
: NotifyCollectionChangedSynchronizedView<T, TView>
, IList<TView>, IReadOnlyList<TView>
, IList
{
readonly ObservableList<T>.View<TView> view;
// IList<T>, IList implementation
public ListNotifyCollectionChangedSynchronizedView(ObservableList<T>.View<TView> parent, ICollectionEventDispatcher? eventDispatcher)
: base(parent, eventDispatcher)
TView IList<TView>.this[int index]
{
this.view = parent;
}
public TView this[int index]
{
get
{
lock (view.SyncRoot)
{
return view.list[index].Item2;
}
}
get => ((IReadOnlyList<TView>)this)[index];
set => throw new NotSupportedException();
}
@ -282,7 +254,7 @@ namespace ObservableCollections.Internal
public bool IsSynchronized => true;
public object SyncRoot => view.SyncRoot;
public object SyncRoot => gate;
public void Add(TView item)
{
@ -301,11 +273,11 @@ namespace ObservableCollections.Internal
public bool Contains(TView item)
{
lock (view.SyncRoot)
lock (gate)
{
foreach (var listItem in view.list)
foreach (var listItem in listView)
{
if (EqualityComparer<TView>.Default.Equals(listItem.Item2, item))
if (EqualityComparer<TView>.Default.Equals(listItem, item))
{
return true;
}
@ -335,12 +307,12 @@ namespace ObservableCollections.Internal
public int IndexOf(TView item)
{
lock (view.SyncRoot)
lock (gate)
{
var index = 0;
foreach (var listItem in view.list)
foreach (var listItem in listView)
{
if (EqualityComparer<TView>.Default.Equals(listItem.Item2, item))
if (EqualityComparer<TView>.Default.Equals(listItem, item))
{
return index;
}

View File

@ -14,6 +14,21 @@ namespace ObservableCollections
return new View<TView>(this, transform);
}
public ISynchronizedViewList<T> ToViewList()
{
return CreateView(static x => x).ToViewList();
}
public INotifyCollectionChangedSynchronizedView<T> ToNotifyCollectionChanged()
{
return CreateView(static x => x).ToNotifyCollectionChanged();
}
public INotifyCollectionChangedSynchronizedView<T> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
{
return CreateView(static x => x).ToNotifyCollectionChanged(collectionEventDispatcher);
}
internal sealed class View<TView> : ISynchronizedView<T, TView>
{
public ISynchronizedViewFilter<T> Filter
@ -107,20 +122,19 @@ namespace ObservableCollections
}
}
public ISynchronizedViewList<TView> ToViewList()
{
return new SynchronizedViewList<T, TView>(this);
}
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
{
lock (SyncRoot)
{
return new ListNotifyCollectionChangedSynchronizedView<T, TView>(this, null);
}
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, null);
}
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
{
lock (SyncRoot)
{
return new ListNotifyCollectionChangedSynchronizedView<T, TView>(this, collectionEventDispatcher);
}
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, collectionEventDispatcher);
}
public IEnumerator<TView> GetEnumerator()