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

View File

@ -10,7 +10,8 @@ namespace ObservableCollections.Internal
internal class SynchronizedViewList<T, TView> : ISynchronizedViewList<TView> internal class SynchronizedViewList<T, TView> : ISynchronizedViewList<TView>
{ {
readonly ISynchronizedView<T, TView> parent; 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) public SynchronizedViewList(ISynchronizedView<T, TView> parent)
{ {
@ -24,76 +25,106 @@ namespace ObservableCollections.Internal
private void Parent_ViewChanged(SynchronizedViewChangedEventArgs<T, TView> e) private void Parent_ViewChanged(SynchronizedViewChangedEventArgs<T, TView> e)
{ {
// event is called inside lock(parent.SyncRoot) lock (gate)
// TODO: invoke in ICollectionEventDispatcher?
switch (e.Action)
{ {
case NotifyViewChangedAction.Add: // Add or Insert switch (e.Action)
if (e.NewViewIndex == -1) {
{ case NotifyViewChangedAction.Add: // Add or Insert
listView.Add(e.NewView); if (e.NewViewIndex == -1)
} {
else listView.Add(e.NewView);
{ }
listView.Insert(e.NewViewIndex, e.NewView); else
} {
break; listView.Insert(e.NewViewIndex, e.NewView);
case NotifyViewChangedAction.Remove: // Remove }
if (e.OldViewIndex == -1) // can't gurantee correct remove if index is not provided break;
{ case NotifyViewChangedAction.Remove: // Remove
listView.Remove(e.OldView); if (e.OldViewIndex == -1) // can't gurantee correct remove if index is not provided
} {
else listView.Remove(e.OldView);
{ }
listView.RemoveAt(e.OldViewIndex); else
} {
break; listView.RemoveAt(e.OldViewIndex);
case NotifyViewChangedAction.Replace: // Indexer }
if (e.NewViewIndex == -1) break;
{ case NotifyViewChangedAction.Replace: // Indexer
var index = listView.IndexOf(e.OldView); if (e.NewViewIndex == -1)
listView[index] = e.NewView; {
} var index = listView.IndexOf(e.OldView);
else listView[index] = e.NewView;
{ }
listView[e.NewViewIndex] = e.NewView; else
} {
listView[e.NewViewIndex] = e.NewView;
}
break; break;
case NotifyViewChangedAction.Move: //Remove and Insert case NotifyViewChangedAction.Move: //Remove and Insert
if (e.NewViewIndex == -1) if (e.NewViewIndex == -1)
{ {
// do nothing // do nothing
} }
else else
{ {
listView.RemoveAt(e.OldViewIndex); listView.RemoveAt(e.OldViewIndex);
listView.Insert(e.NewViewIndex, e.NewView); listView.Insert(e.NewViewIndex, e.NewView);
} }
break; break;
case NotifyViewChangedAction.Reset: // Clear case NotifyViewChangedAction.Reset: // Clear
listView.Clear(); listView.Clear();
break; break;
case NotifyViewChangedAction.FilterReset: case NotifyViewChangedAction.FilterReset:
listView.Clear(); listView.Clear();
foreach (var item in parent) foreach (var item in parent)
{ {
listView.Add(item.View); listView.Add(item);
} }
break; break;
default: default:
break; 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() public IEnumerator<TView> GetEnumerator()
{ {
return listView.GetEnumerator(); lock (gate)
{
foreach (var item in listView)
{
yield return item;
}
}
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
@ -107,73 +138,32 @@ namespace ObservableCollections.Internal
} }
} }
internal class NotifyCollectionChangedSynchronizedView<T, TView> : internal class NotifyCollectionChangedSynchronizedView<T, TView> :
SynchronizedViewList<T, TView>,
INotifyCollectionChangedSynchronizedView<TView>, INotifyCollectionChangedSynchronizedView<TView>,
ISynchronizedViewFilter<T, TView> IList<TView>, IList
{ {
static readonly PropertyChangedEventArgs CountPropertyChangedEventArgs = new("Count"); static readonly PropertyChangedEventArgs CountPropertyChangedEventArgs = new("Count");
static readonly Action<NotifyCollectionChangedEventArgs> raiseChangedEventInvoke = RaiseChangedEvent; static readonly Action<NotifyCollectionChangedEventArgs> raiseChangedEventInvoke = RaiseChangedEvent;
readonly ISynchronizedView<T, TView> parent;
readonly ISynchronizedViewFilter<T, TView> currentFilter;
readonly ICollectionEventDispatcher eventDispatcher; 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 NotifyCollectionChangedEventHandler? CollectionChanged;
public event PropertyChangedEventHandler? PropertyChanged; public event PropertyChangedEventHandler? PropertyChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged public NotifyCollectionChangedSynchronizedView(ISynchronizedView<T, TView> parent, ICollectionEventDispatcher? eventDispatcher)
: base(parent)
{ {
add { parent.CollectionStateChanged += value; } this.eventDispatcher = eventDispatcher ?? InlineCollectionEventDispatcher.Instance;
remove { parent.CollectionStateChanged -= value; }
} }
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; if (CollectionChanged == null && PropertyChanged == null) return;
switch (args.Action) switch (args.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyViewChangedAction.Add:
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Add, args.NewView, args.NewViewIndex) eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Add, args.NewView, args.NewViewIndex)
{ {
Collection = this, Collection = this,
@ -182,7 +172,7 @@ namespace ObservableCollections.Internal
IsInvokePropertyChanged = true IsInvokePropertyChanged = true
}); });
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyViewChangedAction.Remove:
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Remove, args.OldView, args.OldViewIndex) eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Remove, args.OldView, args.OldViewIndex)
{ {
Collection = this, Collection = this,
@ -191,7 +181,7 @@ namespace ObservableCollections.Internal
IsInvokePropertyChanged = true IsInvokePropertyChanged = true
}); });
break; break;
case NotifyCollectionChangedAction.Reset: case NotifyViewChangedAction.Reset:
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Reset) eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Reset)
{ {
Collection = this, Collection = this,
@ -200,7 +190,7 @@ namespace ObservableCollections.Internal
IsInvokePropertyChanged = true IsInvokePropertyChanged = true
}); });
break; break;
case NotifyCollectionChangedAction.Replace: case NotifyViewChangedAction.Replace:
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Replace, args.NewView, args.OldView, args.NewViewIndex) eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Replace, args.NewView, args.OldView, args.NewViewIndex)
{ {
Collection = this, Collection = this,
@ -209,7 +199,7 @@ namespace ObservableCollections.Internal
IsInvokePropertyChanged = false IsInvokePropertyChanged = false
}); });
break; break;
case NotifyCollectionChangedAction.Move: case NotifyViewChangedAction.Move:
eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Move, args.NewView, args.NewViewIndex, args.OldViewIndex) eventDispatcher.Post(new CollectionEventDispatcherEventArgs(NotifyCollectionChangedAction.Move, args.NewView, args.NewViewIndex, args.OldViewIndex)
{ {
Collection = this, Collection = this,
@ -235,30 +225,12 @@ namespace ObservableCollections.Internal
self.PropertyChanged?.Invoke(self, CountPropertyChangedEventArgs); self.PropertyChanged?.Invoke(self, CountPropertyChangedEventArgs);
} }
} }
}
internal class ListNotifyCollectionChangedSynchronizedView<T, TView> // IList<T>, IList implementation
: NotifyCollectionChangedSynchronizedView<T, TView>
, IList<TView>, IReadOnlyList<TView>
, IList
{
readonly ObservableList<T>.View<TView> view;
public ListNotifyCollectionChangedSynchronizedView(ObservableList<T>.View<TView> parent, ICollectionEventDispatcher? eventDispatcher) TView IList<TView>.this[int index]
: base(parent, eventDispatcher)
{ {
this.view = parent; get => ((IReadOnlyList<TView>)this)[index];
}
public TView this[int index]
{
get
{
lock (view.SyncRoot)
{
return view.list[index].Item2;
}
}
set => throw new NotSupportedException(); set => throw new NotSupportedException();
} }
@ -282,7 +254,7 @@ namespace ObservableCollections.Internal
public bool IsSynchronized => true; public bool IsSynchronized => true;
public object SyncRoot => view.SyncRoot; public object SyncRoot => gate;
public void Add(TView item) public void Add(TView item)
{ {
@ -301,11 +273,11 @@ namespace ObservableCollections.Internal
public bool Contains(TView item) 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; return true;
} }
@ -335,12 +307,12 @@ namespace ObservableCollections.Internal
public int IndexOf(TView item) public int IndexOf(TView item)
{ {
lock (view.SyncRoot) lock (gate)
{ {
var index = 0; 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; return index;
} }

View File

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