ObservableList
This commit is contained in:
parent
9ffd0417ba
commit
ce624265f3
@ -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()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user