support Range for ToNotifyCollectionChanged #67
This commit is contained in:
parent
7a573289ea
commit
7ad977ffca
@ -355,6 +355,10 @@ public class WpfDispatcherCollection(Dispatcher dispatcher) : ICollectionEventDi
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`ToNotifyCollectionChanged()` can also be called without going through a View. In this case, it's guaranteed that no filters will be applied, making it faster. If you want to apply filters, please generate a View before calling it. Additionally, `ObservableList` has a variation called `ToNotifyCollectionChangedSlim()`. This option doesn't generate a list for the View and shares the actual data, making it the fastest and most memory-efficient option. However, range operations such as `AddRange`, `InsertRange` and `RemoveRange` are not supported by WPF (or Avalonia), so they will throw runtime exceptions.
|
||||||
|
|
||||||
|
Views and ToNotifyCollectionChanged are internally connected by events, so they need to be `Dispose` to release those connections.
|
||||||
|
|
||||||
Unity
|
Unity
|
||||||
---
|
---
|
||||||
In Unity projects, you can installing `ObservableCollections` with [NugetForUnity](https://github.com/GlitchEnzo/NuGetForUnity). If R3 integration is required, similarly install `ObservableCollections.R3` via NuGetForUnity.
|
In Unity projects, you can installing `ObservableCollections` with [NugetForUnity](https://github.com/GlitchEnzo/NuGetForUnity). If R3 integration is required, similarly install `ObservableCollections.R3` via NuGetForUnity.
|
||||||
|
@ -7,7 +7,14 @@
|
|||||||
Title="AvaloniaApp">
|
Title="AvaloniaApp">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<ListBox ItemsSource="{Binding ItemsView}" x:CompileBindings="False" />
|
<ListBox ItemsSource="{Binding ItemsView}" x:CompileBindings="False" />
|
||||||
<Button Content="Add" Command="{Binding AddCommand}" x:CompileBindings="False" />
|
<Button Content="Add" Command="{Binding AddCommand}" x:CompileBindings="False"/>
|
||||||
<Button Content="Clear" Command="{Binding ClearCommand}" x:CompileBindings="False" />
|
<Button Content="AddRange" Command="{Binding AddRangeCommand}" x:CompileBindings="False"/>
|
||||||
|
<Button Content="Insert" Command="{Binding InsertAtRandomCommand}" x:CompileBindings="False" />
|
||||||
|
<Button Content="Remove" Command="{Binding RemoveAtRandomCommand}" x:CompileBindings="False" />
|
||||||
|
<Button Content="Clear" Command="{Binding ClearCommand}" x:CompileBindings="False"/>
|
||||||
|
<Button Content="Reverse" Command="{Binding ReverseCommand}" x:CompileBindings="False" />
|
||||||
|
<Button Content="Sort" Command="{Binding SortCommand}" x:CompileBindings="False"/>
|
||||||
|
<Button Content="AttachFilter" Command="{Binding AttachFilterCommand}" x:CompileBindings="False" />
|
||||||
|
<Button Content="ResetFilter" Command="{Binding ResetFilterCommand}" x:CompileBindings="False" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Window>
|
</Window>
|
||||||
|
@ -26,22 +26,27 @@ namespace AvaloniaApp
|
|||||||
private ObservableList<int> observableList { get; } = new ObservableList<int>();
|
private ObservableList<int> observableList { get; } = new ObservableList<int>();
|
||||||
public INotifyCollectionChangedSynchronizedViewList<int> ItemsView { get; }
|
public INotifyCollectionChangedSynchronizedViewList<int> ItemsView { get; }
|
||||||
public ReactiveCommand<Unit> AddCommand { get; } = new ReactiveCommand<Unit>();
|
public ReactiveCommand<Unit> AddCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> AddRangeCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> InsertAtRandomCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> RemoveAtRandomCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
public ReactiveCommand<Unit> ClearCommand { get; } = new ReactiveCommand<Unit>();
|
public ReactiveCommand<Unit> ClearCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> ReverseCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> SortCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> AttachFilterCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> ResetFilterCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
|
||||||
public ViewModel()
|
public ViewModel()
|
||||||
{
|
{
|
||||||
observableList.Add(1);
|
observableList.Add(1);
|
||||||
observableList.Add(2);
|
observableList.Add(2);
|
||||||
|
|
||||||
//ItemsView = observableList.CreateView(x => x).ToNotifyCollectionChanged(SynchronizationContextCollectionEventDispatcher.Current);
|
var view = observableList.CreateView(x => x);
|
||||||
|
ItemsView = view.ToNotifyCollectionChanged(SynchronizationContextCollectionEventDispatcher.Current);
|
||||||
|
|
||||||
ItemsView = observableList.CreateView(x => x).ToNotifyCollectionChanged();
|
|
||||||
|
|
||||||
//var test = ItemsView.ToArray();
|
// check for optimize list
|
||||||
//INotifyCollectionChangedSynchronizedView<int>
|
// ItemsView = observableList.ToNotifyCollectionChanged(SynchronizationContextCollectionEventDispatcher.Current);
|
||||||
// ItemsView = observableList.CreateView(x => x).ToNotifyCollectionChanged();
|
|
||||||
|
|
||||||
// BindingOperations.EnableCollectionSynchronization(ItemsView, new object());
|
|
||||||
|
|
||||||
AddCommand.Subscribe(_ =>
|
AddCommand.Subscribe(_ =>
|
||||||
{
|
{
|
||||||
@ -51,12 +56,49 @@ namespace AvaloniaApp
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// var iii = 10;
|
AddRangeCommand.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
var xs = Enumerable.Range(1, 5).Select(_ => Random.Shared.Next()).ToArray();
|
||||||
|
observableList.AddRange(xs);
|
||||||
|
});
|
||||||
|
|
||||||
|
InsertAtRandomCommand.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
var from = Random.Shared.Next(0, view.Count);
|
||||||
|
observableList.Insert(from, Random.Shared.Next());
|
||||||
|
});
|
||||||
|
|
||||||
|
RemoveAtRandomCommand.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
var from = Random.Shared.Next(0, view.Count);
|
||||||
|
observableList.RemoveAt(from);
|
||||||
|
});
|
||||||
|
|
||||||
ClearCommand.Subscribe(_ =>
|
ClearCommand.Subscribe(_ =>
|
||||||
{
|
{
|
||||||
// observableList.Add(iii++);
|
|
||||||
observableList.Clear();
|
observableList.Clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
ReverseCommand.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
observableList.Reverse();
|
||||||
|
});
|
||||||
|
|
||||||
|
SortCommand.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
observableList.Sort();
|
||||||
|
});
|
||||||
|
|
||||||
|
AttachFilterCommand.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
view.AttachFilter(x => x % 2 == 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
ResetFilterCommand.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
view.ResetFilter();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -20,8 +20,10 @@
|
|||||||
<StackPanel>
|
<StackPanel>
|
||||||
<ListBox ItemsSource="{Binding ItemsView}" />
|
<ListBox ItemsSource="{Binding ItemsView}" />
|
||||||
<Button Content="Add" Command="{Binding AddCommand}" />
|
<Button Content="Add" Command="{Binding AddCommand}" />
|
||||||
|
<Button Content="AddRange" Command="{Binding AddRangeCommand}" />
|
||||||
<Button Content="Insert" Command="{Binding InsertAtRandomCommand}" />
|
<Button Content="Insert" Command="{Binding InsertAtRandomCommand}" />
|
||||||
<Button Content="Remove" Command="{Binding RemoveAtRandomCommand}" />
|
<Button Content="Remove" Command="{Binding RemoveAtRandomCommand}" />
|
||||||
|
<Button Content="RemoveRange" Command="{Binding RemoveRangeCommand}" />
|
||||||
<Button Content="Clear" Command="{Binding ClearCommand}" />
|
<Button Content="Clear" Command="{Binding ClearCommand}" />
|
||||||
<Button Content="Reverse" Command="{Binding ReverseCommand}" />
|
<Button Content="Reverse" Command="{Binding ReverseCommand}" />
|
||||||
<Button Content="Sort" Command="{Binding SortCommand}" />
|
<Button Content="Sort" Command="{Binding SortCommand}" />
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using R3;
|
using R3;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -76,8 +77,10 @@ namespace WpfApp
|
|||||||
private ObservableList<int> observableList { get; } = new ObservableList<int>();
|
private ObservableList<int> observableList { get; } = new ObservableList<int>();
|
||||||
public INotifyCollectionChangedSynchronizedViewList<int> ItemsView { get; }
|
public INotifyCollectionChangedSynchronizedViewList<int> ItemsView { get; }
|
||||||
public ReactiveCommand<Unit> AddCommand { get; } = new ReactiveCommand<Unit>();
|
public ReactiveCommand<Unit> AddCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> AddRangeCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
public ReactiveCommand<Unit> InsertAtRandomCommand { get; } = new ReactiveCommand<Unit>();
|
public ReactiveCommand<Unit> InsertAtRandomCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
public ReactiveCommand<Unit> RemoveAtRandomCommand { get; } = new ReactiveCommand<Unit>();
|
public ReactiveCommand<Unit> RemoveAtRandomCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> RemoveRangeCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
public ReactiveCommand<Unit> ClearCommand { get; } = new ReactiveCommand<Unit>();
|
public ReactiveCommand<Unit> ClearCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
public ReactiveCommand<Unit> ReverseCommand { get; } = new ReactiveCommand<Unit>();
|
public ReactiveCommand<Unit> ReverseCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
public ReactiveCommand<Unit> SortCommand { get; } = new ReactiveCommand<Unit>();
|
public ReactiveCommand<Unit> SortCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
@ -90,8 +93,8 @@ namespace WpfApp
|
|||||||
observableList.Add(2);
|
observableList.Add(2);
|
||||||
|
|
||||||
var view = observableList.CreateView(x => x);
|
var view = observableList.CreateView(x => x);
|
||||||
ItemsView = view.ToNotifyCollectionChanged(SynchronizationContextCollectionEventDispatcher.Current);
|
//ItemsView = view.ToNotifyCollectionChanged();
|
||||||
|
ItemsView = observableList.ToNotifyCollectionChanged();
|
||||||
|
|
||||||
// check for optimize list
|
// check for optimize list
|
||||||
// ItemsView = observableList.ToNotifyCollectionChanged(SynchronizationContextCollectionEventDispatcher.Current);
|
// ItemsView = observableList.ToNotifyCollectionChanged(SynchronizationContextCollectionEventDispatcher.Current);
|
||||||
@ -99,10 +102,16 @@ namespace WpfApp
|
|||||||
|
|
||||||
AddCommand.Subscribe(_ =>
|
AddCommand.Subscribe(_ =>
|
||||||
{
|
{
|
||||||
ThreadPool.QueueUserWorkItem(_ =>
|
// ThreadPool.QueueUserWorkItem(_ =>
|
||||||
{
|
{
|
||||||
observableList.Add(Random.Shared.Next());
|
observableList.Add(Random.Shared.Next());
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AddRangeCommand.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
var xs = Enumerable.Range(1, 5).Select(_ => Random.Shared.Next()).ToArray();
|
||||||
|
observableList.AddRange(xs);
|
||||||
});
|
});
|
||||||
|
|
||||||
InsertAtRandomCommand.Subscribe(_ =>
|
InsertAtRandomCommand.Subscribe(_ =>
|
||||||
@ -117,6 +126,11 @@ namespace WpfApp
|
|||||||
observableList.RemoveAt(from);
|
observableList.RemoveAt(from);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
RemoveRangeCommand.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
observableList.RemoveRange(2, 5);
|
||||||
|
});
|
||||||
|
|
||||||
ClearCommand.Subscribe(_ =>
|
ClearCommand.Subscribe(_ =>
|
||||||
{
|
{
|
||||||
observableList.Clear();
|
observableList.Clear();
|
||||||
|
@ -178,7 +178,7 @@ public class AlternateIndexList<T> : IEnumerable<T>
|
|||||||
|
|
||||||
object IEnumerator.Current => Current;
|
object IEnumerator.Current => Current;
|
||||||
|
|
||||||
public void Dispose() => iter.Reset();
|
public void Dispose() => iter.Dispose();
|
||||||
|
|
||||||
public bool MoveNext()
|
public bool MoveNext()
|
||||||
{
|
{
|
||||||
@ -191,7 +191,7 @@ public class AlternateIndexList<T> : IEnumerable<T>
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset() => iter.Reset();
|
public void Reset() { }
|
||||||
|
|
||||||
public IEnumerator<IndexedValue> GetEnumerator() => this;
|
public IEnumerator<IndexedValue> GetEnumerator() => this;
|
||||||
|
|
||||||
|
@ -21,12 +21,18 @@ public sealed partial class ObservableList<T> : IList<T>, IReadOnlyObservableLis
|
|||||||
// return new NonFilteredSynchronizedViewList<T, TView>(collection.CreateView(transform));
|
// return new NonFilteredSynchronizedViewList<T, TView>(collection.CreateView(transform));
|
||||||
//}
|
//}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedViewList<T> ToNotifyCollectionChanged()
|
/// <summary>
|
||||||
|
/// Create faster, compact INotifyCollectionChanged view, however it does not support ***Range.
|
||||||
|
/// </summary>
|
||||||
|
public INotifyCollectionChangedSynchronizedViewList<T> ToNotifyCollectionChangedSlim()
|
||||||
{
|
{
|
||||||
return new ObservableListSynchronizedViewList<T>(this, null);
|
return new ObservableListSynchronizedViewList<T>(this, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedViewList<T> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
|
/// <summary>
|
||||||
|
/// Create faster, compact INotifyCollectionChanged view, however it does not support ***Range.
|
||||||
|
/// </summary>
|
||||||
|
public INotifyCollectionChangedSynchronizedViewList<T> ToNotifyCollectionChangedSlim(ICollectionEventDispatcher? collectionEventDispatcher)
|
||||||
{
|
{
|
||||||
return new ObservableListSynchronizedViewList<T>(this, collectionEventDispatcher);
|
return new ObservableListSynchronizedViewList<T>(this, collectionEventDispatcher);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
@ -16,6 +17,8 @@ internal class FiltableSynchronizedViewList<T, TView> : ISynchronizedViewList<TV
|
|||||||
protected readonly AlternateIndexList<TView> listView;
|
protected readonly AlternateIndexList<TView> listView;
|
||||||
protected readonly object gate = new object();
|
protected readonly object gate = new object();
|
||||||
|
|
||||||
|
protected virtual bool IsSupportRangeFeature => true;
|
||||||
|
|
||||||
public FiltableSynchronizedViewList(ISynchronizedView<T, TView> parent)
|
public FiltableSynchronizedViewList(ISynchronizedView<T, TView> parent)
|
||||||
{
|
{
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
@ -67,9 +70,22 @@ internal class FiltableSynchronizedViewList<T, TView> : ISynchronizedViewList<TV
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
using var array = new CloneCollection<TView>(e.NewViews);
|
if (IsSupportRangeFeature)
|
||||||
var index = listView.InsertRange(e.NewStartingIndex, array.AsEnumerable());
|
{
|
||||||
OnCollectionChanged(e.WithNewStartingIndex(index));
|
using var array = new CloneCollection<TView>(e.NewViews);
|
||||||
|
var index = listView.InsertRange(e.NewStartingIndex, array.AsEnumerable());
|
||||||
|
OnCollectionChanged(e.WithNewStartingIndex(index));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var span = e.NewViews;
|
||||||
|
for (int i = 0; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
var index = listView.Insert(e.NewStartingIndex + i, span[i]);
|
||||||
|
var ev = new SynchronizedViewChangedEventArgs<T, TView>(e.Action, true, newItem: (e.NewValues[i], span[i]), newStartingIndex: index);
|
||||||
|
OnCollectionChanged(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case NotifyCollectionChangedAction.Remove: // Remove
|
case NotifyCollectionChangedAction.Remove: // Remove
|
||||||
@ -99,7 +115,21 @@ internal class FiltableSynchronizedViewList<T, TView> : ISynchronizedViewList<TV
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
index = listView.RemoveRange(e.OldStartingIndex, e.OldViews.Length);
|
if (IsSupportRangeFeature)
|
||||||
|
{
|
||||||
|
index = listView.RemoveRange(e.OldStartingIndex, e.OldViews.Length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var span = e.OldViews;
|
||||||
|
for (int i = 0; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
index = listView.RemoveAt(e.OldStartingIndex); // when removed, next remove index is same.
|
||||||
|
var ev = new SynchronizedViewChangedEventArgs<T, TView>(e.Action, true, oldItem: (e.OldValues[i], span[i]), oldStartingIndex: index);
|
||||||
|
OnCollectionChanged(ev);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OnCollectionChanged(e.WithOldStartingIndex(index));
|
OnCollectionChanged(e.WithOldStartingIndex(index));
|
||||||
@ -192,7 +222,7 @@ internal class FiltableSynchronizedViewList<T, TView> : ISynchronizedViewList<TV
|
|||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
{
|
{
|
||||||
return listView.GetEnumerator();
|
return GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@ -207,6 +237,8 @@ internal class NonFilteredSynchronizedViewList<T, TView> : ISynchronizedViewList
|
|||||||
protected readonly List<TView> listView; // no filter can be faster
|
protected readonly List<TView> listView; // no filter can be faster
|
||||||
protected readonly object gate = new object();
|
protected readonly object gate = new object();
|
||||||
|
|
||||||
|
protected virtual bool IsSupportRangeFeature => true;
|
||||||
|
|
||||||
public NonFilteredSynchronizedViewList(ISynchronizedView<T, TView> parent)
|
public NonFilteredSynchronizedViewList(ISynchronizedView<T, TView> parent)
|
||||||
{
|
{
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
@ -231,12 +263,27 @@ internal class NonFilteredSynchronizedViewList<T, TView> : ISynchronizedViewList
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (IsSupportRangeFeature)
|
||||||
|
{
|
||||||
#if NET8_0_OR_GREATER
|
#if NET8_0_OR_GREATER
|
||||||
listView.InsertRange(e.NewStartingIndex, e.NewViews);
|
listView.InsertRange(e.NewStartingIndex, e.NewViews);
|
||||||
#else
|
#else
|
||||||
using var array = new CloneCollection<TView>(e.NewViews);
|
using var array = new CloneCollection<TView>(e.NewViews);
|
||||||
listView.InsertRange(e.NewStartingIndex, array.AsEnumerable());
|
listView.InsertRange(e.NewStartingIndex, array.AsEnumerable());
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var span = e.NewViews;
|
||||||
|
for (int i = 0; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
var index = e.NewStartingIndex + i;
|
||||||
|
listView.Insert(index, span[i]);
|
||||||
|
var ev = new SynchronizedViewChangedEventArgs<T, TView>(e.Action, true, newItem: (e.NewValues[i], span[i]), newStartingIndex: index);
|
||||||
|
OnCollectionChanged(ev);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Remove: // Remove
|
case NotifyCollectionChangedAction.Remove: // Remove
|
||||||
@ -269,7 +316,21 @@ internal class NonFilteredSynchronizedViewList<T, TView> : ISynchronizedViewList
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
listView.RemoveRange(e.OldStartingIndex, e.OldViews.Length);
|
if (IsSupportRangeFeature)
|
||||||
|
{
|
||||||
|
listView.RemoveRange(e.OldStartingIndex, e.OldViews.Length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var span = e.OldViews;
|
||||||
|
for (int i = 0; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
listView.RemoveAt(e.OldStartingIndex); // when removed, next remove index is same.
|
||||||
|
var ev = new SynchronizedViewChangedEventArgs<T, TView>(e.Action, true, oldItem: (e.OldValues[i], span[i]), oldStartingIndex: e.OldStartingIndex);
|
||||||
|
OnCollectionChanged(ev);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -320,9 +381,6 @@ internal class NonFilteredSynchronizedViewList<T, TView> : ISynchronizedViewList
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if NET6_0_OR_GREATER
|
#if NET6_0_OR_GREATER
|
||||||
#pragma warning disable CS0436
|
#pragma warning disable CS0436
|
||||||
if (parent is ObservableList<T>.View<TView> observableListView && typeof(T) == typeof(TView))
|
if (parent is ObservableList<T>.View<TView> observableListView && typeof(T) == typeof(TView))
|
||||||
@ -408,7 +466,7 @@ internal class NonFilteredSynchronizedViewList<T, TView> : ISynchronizedViewList
|
|||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
{
|
{
|
||||||
return listView.GetEnumerator();
|
return GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@ -428,6 +486,8 @@ internal class NotifyCollectionChangedSynchronizedViewList<T, TView> :
|
|||||||
|
|
||||||
readonly ICollectionEventDispatcher eventDispatcher;
|
readonly ICollectionEventDispatcher eventDispatcher;
|
||||||
|
|
||||||
|
protected override bool IsSupportRangeFeature => false; // WPF, Avalonia etc does not support range notification
|
||||||
|
|
||||||
public event NotifyCollectionChangedEventHandler? CollectionChanged;
|
public event NotifyCollectionChangedEventHandler? CollectionChanged;
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
@ -676,6 +736,8 @@ internal class NonFilteredNotifyCollectionChangedSynchronizedViewList<T, TView>
|
|||||||
public event NotifyCollectionChangedEventHandler? CollectionChanged;
|
public event NotifyCollectionChangedEventHandler? CollectionChanged;
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
|
protected override bool IsSupportRangeFeature => false; // WPF, Avalonia etc does not support range notification
|
||||||
|
|
||||||
public NonFilteredNotifyCollectionChangedSynchronizedViewList(ISynchronizedView<T, TView> parent, ICollectionEventDispatcher? eventDispatcher)
|
public NonFilteredNotifyCollectionChangedSynchronizedViewList(ISynchronizedView<T, TView> parent, ICollectionEventDispatcher? eventDispatcher)
|
||||||
: base(parent)
|
: base(parent)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user