This commit is contained in:
neuecc 2024-08-26 16:33:36 +09:00
parent 68618fda10
commit 9ffd0417ba
3 changed files with 64 additions and 42 deletions

View File

@ -13,7 +13,7 @@ namespace ObservableCollections
{
event NotifyCollectionChangedEventHandler<T>? CollectionChanged;
object SyncRoot { get; }
ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false);
ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform);
}
public interface IReadOnlyObservableList<T> :
@ -36,7 +36,9 @@ namespace ObservableCollections
{
object SyncRoot { get; }
ISynchronizedViewFilter<T> Filter { get; }
IEnumerable<(T Value, TView View)> Filtered { get; }
IEnumerable<(T Value, TView View)> Unfiltered { get; }
int UnfilteredCount { get; }
event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
event Action<NotifyCollectionChangedAction>? CollectionStateChanged;

View File

@ -15,9 +15,11 @@ namespace ObservableCollections.Internal
public SynchronizedViewList(ISynchronizedView<T, TView> parent)
{
this.parent = parent;
this.listView = parent.Select(x => x.View).ToList(); // need lock
// TODO:add
parent.ViewChanged += Parent_ViewChanged;
lock (parent.SyncRoot)
{
this.listView = parent.ToList();
parent.ViewChanged += Parent_ViewChanged;
}
}
private void Parent_ViewChanged(SynchronizedViewChangedEventArgs<T, TView> e)

View File

@ -9,9 +9,9 @@ namespace ObservableCollections
{
public sealed partial class ObservableList<T> : IList<T>, IReadOnlyObservableList<T>
{
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform)
{
return new View<TView>(this, transform, reverse);
return new View<TView>(this, transform);
}
internal sealed class View<TView> : ISynchronizedView<T, TView>
@ -26,7 +26,6 @@ namespace ObservableCollections
readonly ObservableList<T> source;
readonly Func<T, TView> selector;
readonly bool reverse;
readonly List<(T, TView)> list;
int filteredCount;
@ -37,11 +36,10 @@ namespace ObservableCollections
public object SyncRoot { get; }
public View(ObservableList<T> source, Func<T, TView> selector, bool reverse)
public View(ObservableList<T> source, Func<T, TView> selector)
{
this.source = source;
this.selector = selector;
this.reverse = reverse;
this.filter = SynchronizedViewFilter<T>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
@ -63,6 +61,17 @@ namespace ObservableCollections
}
}
public int UnfilteredCount
{
get
{
lock (SyncRoot)
{
return list.Count;
}
}
}
public void AttachFilter(ISynchronizedViewFilter<T> filter)
{
if (filter.IsNullFilter())
@ -114,25 +123,31 @@ namespace ObservableCollections
}
}
public IEnumerator<(T, TView)> GetEnumerator()
public IEnumerator<TView> GetEnumerator()
{
lock (SyncRoot)
{
if (!reverse)
foreach (var item in list)
{
if (filter.IsMatch(item.Item1))
{
yield return item.Item2;
}
}
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerable<(T Value, TView View)> Filtered
{
get
{
lock (SyncRoot)
{
foreach (var item in list)
{
if (filter.IsMatch(item.Item1, item.Item2))
{
yield return item;
}
}
}
else
{
foreach (var item in list.AsEnumerable().Reverse())
{
if (filter.IsMatch(item.Item1, item.Item2))
if (filter.IsMatch(item.Item1))
{
yield return item;
}
@ -141,7 +156,19 @@ namespace ObservableCollections
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerable<(T Value, TView View)> Unfiltered
{
get
{
lock (SyncRoot)
{
foreach (var item in list)
{
yield return item;
}
}
}
}
public void Dispose()
{
@ -171,11 +198,7 @@ namespace ObservableCollections
{
var v = (item, selector(item));
list.Add(v);
if (filter.IsMatch(v))
{
filteredCount++;
}
filter.InvokeOnAdd(v, i++);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, i++);
}
}
}
@ -186,20 +209,17 @@ namespace ObservableCollections
{
var v = (e.NewItem, selector(e.NewItem));
list.Insert(e.NewStartingIndex, v);
filter.InvokeOnAdd(v, e.NewStartingIndex);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, e.NewStartingIndex);
}
else
{
// inefficient copy, need refactoring
var newArray = new (T, TView)[e.NewItems.Length];
var span = e.NewItems;
for (var i = 0; i < span.Length; i++)
{
var v = (span[i], selector(span[i]));
newArray[i] = v;
filter.InvokeOnAdd(v, e.NewStartingIndex + i);
list.Insert(e.NewStartingIndex + i, v); // should we use InsertRange?
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, e.NewStartingIndex + i);
}
list.InsertRange(e.NewStartingIndex, newArray);
}
}
break;
@ -208,7 +228,7 @@ namespace ObservableCollections
{
var v = list[e.OldStartingIndex];
list.RemoveAt(e.OldStartingIndex);
filter.InvokeOnRemove(v, e.OldStartingIndex);
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, e.OldStartingIndex);
}
else
{
@ -216,10 +236,9 @@ namespace ObservableCollections
for (var i = e.OldStartingIndex; i < len; i++)
{
var v = list[i];
filter.InvokeOnRemove(v, e.OldStartingIndex + i);
list.RemoveAt(e.OldStartingIndex + i); // should we use RemoveRange?
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, e.OldStartingIndex + i);
}
list.RemoveRange(e.OldStartingIndex, e.OldItems.Length);
}
break;
case NotifyCollectionChangedAction.Replace:
@ -228,7 +247,7 @@ namespace ObservableCollections
var v = (e.NewItem, selector(e.NewItem));
var ov = (e.OldItem, list[e.OldStartingIndex].Item2);
list[e.NewStartingIndex] = v;
filter.InvokeOnReplace(v, ov, e.NewStartingIndex);
this.InvokeOnReplace(ref filteredCount, ViewChanged, v, ov, e.NewStartingIndex);
break;
}
case NotifyCollectionChangedAction.Move:
@ -237,18 +256,17 @@ namespace ObservableCollections
list.RemoveAt(e.OldStartingIndex);
list.Insert(e.NewStartingIndex, removeItem);
filter.InvokeOnMove(removeItem, e.NewStartingIndex, e.OldStartingIndex);
this.InvokeOnMove(ref filteredCount, ViewChanged, removeItem, e.NewStartingIndex, e.OldStartingIndex);
}
break;
case NotifyCollectionChangedAction.Reset:
list.Clear();
filter.InvokeOnReset();
this.InvokeOnReset(ref filteredCount, ViewChanged);
break;
default:
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}