Merge pull request #82 from zerodev1200/Change-WritableView-implementation
Change WritableView implementation
This commit is contained in:
commit
b000cc8521
@ -5,29 +5,62 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:local="clr-namespace:WpfApp"
|
xmlns:local="clr-namespace:WpfApp"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="MainWindow" Height="450" Width="800">
|
Title="MainWindow" Height="800" Width="800">
|
||||||
<!--<Grid>
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition />
|
<ColumnDefinition Width="*" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<ListView ItemsSource="{Binding ItemsView}"></ListView>
|
<!-- original -->
|
||||||
|
<StackPanel Grid.Row="0" Grid.ColumnSpan="2" Orientation="Vertical">
|
||||||
<Button Grid.Column="1" Click="Button_Click">Insert</Button>
|
<ListBox ItemsSource="{Binding ItemsView}" />
|
||||||
|
<Button Content="Add" Command="{Binding AddCommand}" />
|
||||||
|
<Button Content="AddRange" Command="{Binding AddRangeCommand}" />
|
||||||
|
<Button Content="Insert" Command="{Binding InsertAtRandomCommand}" />
|
||||||
|
<Button Content="Remove" Command="{Binding RemoveAtRandomCommand}" />
|
||||||
|
<Button Content="RemoveRange" Command="{Binding RemoveRangeCommand}" />
|
||||||
|
<Button Content="Clear" Command="{Binding ClearCommand}" />
|
||||||
|
<Button Content="Reverse" Command="{Binding ReverseCommand}" />
|
||||||
|
<Button Content="Sort" Command="{Binding SortCommand}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
</Grid>-->
|
<!-- Upper left (NotWritable, NonFilter) -->
|
||||||
<StackPanel>
|
<GroupBox Grid.Row="1" Grid.Column="0" Header="NotWritable, NonFilter">
|
||||||
<ListBox ItemsSource="{Binding ItemsView}" />
|
<StackPanel Orientation="Vertical">
|
||||||
<Button Content="Add" Command="{Binding AddCommand}" />
|
<DataGrid ItemsSource="{Binding NotWritableNonFilterView}" />
|
||||||
<Button Content="AddRange" Command="{Binding AddRangeCommand}" />
|
</StackPanel>
|
||||||
<Button Content="Insert" Command="{Binding InsertAtRandomCommand}" />
|
</GroupBox>
|
||||||
<Button Content="Remove" Command="{Binding RemoveAtRandomCommand}" />
|
|
||||||
<Button Content="RemoveRange" Command="{Binding RemoveRangeCommand}" />
|
<!-- Upper right (Writable, NonFilter) -->
|
||||||
<Button Content="Clear" Command="{Binding ClearCommand}" />
|
<GroupBox Grid.Row="1" Grid.Column="1" Header="Writable, NonFilter">
|
||||||
<Button Content="Reverse" Command="{Binding ReverseCommand}" />
|
<StackPanel Orientation="Vertical">
|
||||||
<Button Content="Sort" Command="{Binding SortCommand}" />
|
<DataGrid ItemsSource="{Binding WritableNonFilterPersonView}" />
|
||||||
<Button Content="AttachFilter" Command="{Binding AttachFilterCommand}" />
|
</StackPanel>
|
||||||
<Button Content="ResetFilter" Command="{Binding ResetFilterCommand}" />
|
</GroupBox>
|
||||||
</StackPanel>
|
|
||||||
</Window>
|
<!-- Lower left (NotWritable, Filter) -->
|
||||||
|
<GroupBox Grid.Row="2" Grid.Column="0" Header="NotWritable, Filter">
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<DataGrid ItemsSource="{Binding NotWritableFilterView}" />
|
||||||
|
<Button Content="AttachFilter" Command="{Binding AttachFilterCommand2}" />
|
||||||
|
<Button Content="ResetFilter" Command="{Binding ResetFilterCommand2}" />
|
||||||
|
</StackPanel>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<!-- Lower right (Writable, Filter) -->
|
||||||
|
<GroupBox Grid.Row="2" Grid.Column="1" Header="Writable, Filter">
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<DataGrid ItemsSource="{Binding WritableFilterPersonView}" />
|
||||||
|
<Button Content="AttachFilter" Command="{Binding AttachFilterCommand3}" />
|
||||||
|
<Button Content="ResetFilter" Command="{Binding ResetFilterCommand3}" />
|
||||||
|
</StackPanel>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
@ -87,6 +87,18 @@ namespace WpfApp
|
|||||||
public ReactiveCommand<Unit> AttachFilterCommand { get; } = new ReactiveCommand<Unit>();
|
public ReactiveCommand<Unit> AttachFilterCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
public ReactiveCommand<Unit> ResetFilterCommand { get; } = new ReactiveCommand<Unit>();
|
public ReactiveCommand<Unit> ResetFilterCommand { get; } = new ReactiveCommand<Unit>();
|
||||||
|
|
||||||
|
private ObservableList<Person> SourceList { get; } = [];
|
||||||
|
private IWritableSynchronizedView<Person, Person> writableFilter;
|
||||||
|
private ISynchronizedView<Person, Person> notWritableFilter;
|
||||||
|
public NotifyCollectionChangedSynchronizedViewList<Person> NotWritableNonFilterView { get; }
|
||||||
|
public NotifyCollectionChangedSynchronizedViewList<Person> NotWritableFilterView { get; }
|
||||||
|
public NotifyCollectionChangedSynchronizedViewList<Person> WritableNonFilterPersonView { get; }
|
||||||
|
public NotifyCollectionChangedSynchronizedViewList<Person> WritableFilterPersonView { get; }
|
||||||
|
public ReactiveCommand<Unit> AttachFilterCommand2 { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> ResetFilterCommand2 { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> AttachFilterCommand3 { get; } = new ReactiveCommand<Unit>();
|
||||||
|
public ReactiveCommand<Unit> ResetFilterCommand3 { get; } = new ReactiveCommand<Unit>();
|
||||||
|
|
||||||
public ViewModel()
|
public ViewModel()
|
||||||
{
|
{
|
||||||
observableList.Add(1);
|
observableList.Add(1);
|
||||||
@ -156,8 +168,97 @@ namespace WpfApp
|
|||||||
{
|
{
|
||||||
view.ResetFilter();
|
view.ResetFilter();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
SourceList.Add(new() { Name = "a", Age = 1 });
|
||||||
|
SourceList.Add(new() { Name = "b", Age = 2 });
|
||||||
|
SourceList.Add(new() { Name = "c", Age = 3 });
|
||||||
|
SourceList.Add(new() { Name = "d", Age = 4 });
|
||||||
|
//NotWritable, NonFilter
|
||||||
|
NotWritableNonFilterView = SourceList.ToNotifyCollectionChanged();
|
||||||
|
|
||||||
|
//NotWritable, Filter
|
||||||
|
notWritableFilter = SourceList.CreateView(x => x);
|
||||||
|
NotWritableFilterView = notWritableFilter.ToNotifyCollectionChanged();
|
||||||
|
|
||||||
|
//Writable, NonFilter
|
||||||
|
WritableNonFilterPersonView = SourceList.ToWritableNotifyCollectionChanged();
|
||||||
|
|
||||||
|
//WritableNonFilterPersonView = SourceList.ToWritableNotifyCollectionChanged(x => x, (Person newView, Person original, ref bool setValue) =>
|
||||||
|
//{
|
||||||
|
// if (setValue)
|
||||||
|
// {
|
||||||
|
// // default setValue == true is Set operation
|
||||||
|
// original.Name = newView.Name;
|
||||||
|
// original.Age = newView.Age;
|
||||||
|
|
||||||
|
// // You can modify setValue to false, it does not set original collection to new value.
|
||||||
|
// // For mutable reference types, when there is only a single,
|
||||||
|
// // bound View and to avoid recreating the View, setting false is effective.
|
||||||
|
// // Otherwise, keeping it true will set the value in the original collection as well,
|
||||||
|
// // and change notifications will be sent to lower-level Views(the delegate for View generation will also be called anew).
|
||||||
|
// setValue = false;
|
||||||
|
// return original;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// // default setValue == false is Add operation
|
||||||
|
// return new Person { Age = newView.Age, Name = newView.Name };
|
||||||
|
// }
|
||||||
|
//}, null);
|
||||||
|
|
||||||
|
//Writable, Filter
|
||||||
|
writableFilter = SourceList.CreateWritableView(x => x);
|
||||||
|
WritableFilterPersonView = writableFilter.ToWritableNotifyCollectionChanged();
|
||||||
|
|
||||||
|
//WritableFilterPersonView = writableFilter.ToWritableNotifyCollectionChanged((Person newView, Person original, ref bool setValue) =>
|
||||||
|
//{
|
||||||
|
// if (setValue)
|
||||||
|
// {
|
||||||
|
// // default setValue == true is Set operation
|
||||||
|
// original.Name = newView.Name;
|
||||||
|
// original.Age = newView.Age;
|
||||||
|
|
||||||
|
// // You can modify setValue to false, it does not set original collection to new value.
|
||||||
|
// // For mutable reference types, when there is only a single,
|
||||||
|
// // bound View and to avoid recreating the View, setting false is effective.
|
||||||
|
// // Otherwise, keeping it true will set the value in the original collection as well,
|
||||||
|
// // and change notifications will be sent to lower-level Views(the delegate for View generation will also be called anew).
|
||||||
|
// setValue = false;
|
||||||
|
// return original;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// // default setValue == false is Add operation
|
||||||
|
// return new Person { Age = newView.Age, Name = newView.Name };
|
||||||
|
// }
|
||||||
|
//});
|
||||||
|
|
||||||
|
AttachFilterCommand2.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
notWritableFilter.AttachFilter(x => x.Age % 2 == 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
ResetFilterCommand2.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
notWritableFilter.ResetFilter();
|
||||||
|
});
|
||||||
|
|
||||||
|
AttachFilterCommand3.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
writableFilter.AttachFilter(x => x.Age % 2 == 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
ResetFilterCommand3.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
writableFilter.ResetFilter();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public class Person
|
||||||
|
{
|
||||||
|
public int? Age { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class WpfDispatcherCollection(Dispatcher dispatcher) : ICollectionEventDispatcher
|
public class WpfDispatcherCollection(Dispatcher dispatcher) : ICollectionEventDispatcher
|
||||||
{
|
{
|
||||||
|
@ -57,7 +57,12 @@ namespace ObservableCollections
|
|||||||
void SetViewAt(int index, TView view);
|
void SetViewAt(int index, TView view);
|
||||||
void SetToSourceCollection(int index, T value);
|
void SetToSourceCollection(int index, T value);
|
||||||
void AddToSourceCollection(T value);
|
void AddToSourceCollection(T value);
|
||||||
|
void InsertIntoSourceCollection(int index, T value);
|
||||||
|
bool RemoveFromSourceCollection(T value);
|
||||||
|
void RemoveAtSourceCollection(int index);
|
||||||
|
void ClearSourceCollection();
|
||||||
IWritableSynchronizedViewList<TView> ToWritableViewList(WritableViewChangedEventHandler<T, TView> converter);
|
IWritableSynchronizedViewList<TView> ToWritableViewList(WritableViewChangedEventHandler<T, TView> converter);
|
||||||
|
NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged();
|
||||||
NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter);
|
NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter);
|
||||||
NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher);
|
NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher);
|
||||||
NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter, ICollectionEventDispatcher? collectionEventDispatcher);
|
NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter, ICollectionEventDispatcher? collectionEventDispatcher);
|
||||||
@ -99,8 +104,8 @@ namespace ObservableCollections
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract int Count { get; }
|
public abstract int Count { get; }
|
||||||
public bool IsReadOnly => false;
|
public virtual bool IsReadOnly { get; } = true;
|
||||||
public bool IsFixedSize => false;
|
public bool IsFixedSize => IsReadOnly;
|
||||||
public bool IsSynchronized => true;
|
public bool IsSynchronized => true;
|
||||||
public object SyncRoot => gate;
|
public object SyncRoot => gate;
|
||||||
|
|
||||||
@ -112,9 +117,14 @@ namespace ObservableCollections
|
|||||||
int IList.Add(object? value)
|
int IList.Add(object? value)
|
||||||
{
|
{
|
||||||
Add((TView)value!);
|
Add((TView)value!);
|
||||||
return -1; // itself does not add in this collection
|
return Count - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract void Insert(int index, TView item);
|
||||||
|
public abstract bool Remove(TView item);
|
||||||
|
public abstract void RemoveAt(int index);
|
||||||
|
public abstract void Clear();
|
||||||
|
|
||||||
public abstract bool Contains(TView item);
|
public abstract bool Contains(TView item);
|
||||||
|
|
||||||
bool IList.Contains(object? value)
|
bool IList.Contains(object? value)
|
||||||
@ -149,17 +159,43 @@ namespace ObservableCollections
|
|||||||
return value is TView || value == null && default(TView) == null;
|
return value is TView || value == null && default(TView) == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICollection<TView>.Clear() => throw new NotSupportedException();
|
void ICollection<TView>.Clear()
|
||||||
void IList.Clear() => throw new NotSupportedException();
|
{
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
void IList.Clear()
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
void ICollection<TView>.CopyTo(TView[] array, int arrayIndex) => throw new NotSupportedException();
|
void ICollection<TView>.CopyTo(TView[] array, int arrayIndex) => throw new NotSupportedException();
|
||||||
void ICollection.CopyTo(Array array, int index) => throw new NotSupportedException();
|
void ICollection.CopyTo(Array array, int index) => throw new NotSupportedException();
|
||||||
|
|
||||||
void IList<TView>.Insert(int index, TView item) => throw new NotSupportedException();
|
void IList<TView>.Insert(int index, TView item)
|
||||||
void IList.Insert(int index, object? value) => throw new NotSupportedException();
|
{
|
||||||
bool ICollection<TView>.Remove(TView item) => throw new NotSupportedException();
|
Insert(index, item);
|
||||||
void IList.Remove(object? value) => throw new NotSupportedException();
|
}
|
||||||
void IList.RemoveAt(int index) => throw new NotSupportedException();
|
|
||||||
|
void IList.Insert(int index, object? value)
|
||||||
|
{
|
||||||
|
Insert(index, (TView)value!);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ICollection<TView>.Remove(TView item)
|
||||||
|
{
|
||||||
|
return Remove(item!);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IList.Remove(object? value)
|
||||||
|
{
|
||||||
|
Remove((TView)value!);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IList.RemoveAt(int index)
|
||||||
|
{
|
||||||
|
RemoveAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
void IList<TView>.RemoveAt(int index) => throw new NotSupportedException();
|
void IList<TView>.RemoveAt(int index) => throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,25 +212,25 @@ namespace ObservableCollections
|
|||||||
return new NonFilteredSynchronizedViewList<T, TView>(collection.CreateView(transform), isSupportRangeFeature: true, null, null);
|
return new NonFilteredSynchronizedViewList<T, TView>(collection.CreateView(transform), isSupportRangeFeature: true, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static INotifyCollectionChangedSynchronizedViewList<T> ToNotifyCollectionChanged<T>(this IObservableCollection<T> collection)
|
public static NotifyCollectionChangedSynchronizedViewList<T> ToNotifyCollectionChanged<T>(this IObservableCollection<T> collection)
|
||||||
{
|
{
|
||||||
return ToNotifyCollectionChanged(collection, null);
|
return ToNotifyCollectionChanged(collection, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static INotifyCollectionChangedSynchronizedViewList<T> ToNotifyCollectionChanged<T>(this IObservableCollection<T> collection, ICollectionEventDispatcher? collectionEventDispatcher)
|
public static NotifyCollectionChangedSynchronizedViewList<T> ToNotifyCollectionChanged<T>(this IObservableCollection<T> collection, ICollectionEventDispatcher? collectionEventDispatcher)
|
||||||
{
|
{
|
||||||
return ToNotifyCollectionChanged(collection, static x => x, collectionEventDispatcher);
|
return ToNotifyCollectionChanged(collection, static x => x, collectionEventDispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static INotifyCollectionChangedSynchronizedViewList<TView> ToNotifyCollectionChanged<T, TView>(this IObservableCollection<T> collection, Func<T, TView> transform)
|
public static NotifyCollectionChangedSynchronizedViewList<TView> ToNotifyCollectionChanged<T, TView>(this IObservableCollection<T> collection, Func<T, TView> transform)
|
||||||
{
|
{
|
||||||
return ToNotifyCollectionChanged(collection, transform, null!);
|
return ToNotifyCollectionChanged(collection, transform, null!);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static INotifyCollectionChangedSynchronizedViewList<TView> ToNotifyCollectionChanged<T, TView>(this IObservableCollection<T> collection, Func<T, TView> transform, ICollectionEventDispatcher? collectionEventDispatcher)
|
public static NotifyCollectionChangedSynchronizedViewList<TView> ToNotifyCollectionChanged<T, TView>(this IObservableCollection<T> collection, Func<T, TView> transform, ICollectionEventDispatcher? collectionEventDispatcher)
|
||||||
{
|
{
|
||||||
// Optimized for non filtered
|
// Optimized for non filtered
|
||||||
return new NonFilteredSynchronizedViewList<T, TView>(collection.CreateView(transform), isSupportRangeFeature: false, collectionEventDispatcher, null);
|
return new NonFilteredSynchronizedViewList<T, TView>(collection.CreateView(transform), isSupportRangeFeature: false, collectionEventDispatcher, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,6 +189,25 @@ internal sealed class ObservableListSynchronizedViewList<T> : NotifyCollectionCh
|
|||||||
{
|
{
|
||||||
parent.Add(item);
|
parent.Add(item);
|
||||||
}
|
}
|
||||||
|
public override void Insert(int index, T item)
|
||||||
|
{
|
||||||
|
parent.Insert(index, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Remove(T item)
|
||||||
|
{
|
||||||
|
return parent.Remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RemoveAt(int index)
|
||||||
|
{
|
||||||
|
parent.RemoveAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Clear()
|
||||||
|
{
|
||||||
|
parent.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
public override bool Contains(T item)
|
public override bool Contains(T item)
|
||||||
{
|
{
|
||||||
@ -199,4 +218,4 @@ internal sealed class ObservableListSynchronizedViewList<T> : NotifyCollectionCh
|
|||||||
{
|
{
|
||||||
return parent.IndexOf(item);
|
return parent.IndexOf(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,12 +19,12 @@ namespace ObservableCollections
|
|||||||
return new View<TView>(this, transform);
|
return new View<TView>(this, transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedViewList<T> ToWritableNotifyCollectionChanged()
|
public NotifyCollectionChangedSynchronizedViewList<T> ToWritableNotifyCollectionChanged()
|
||||||
{
|
{
|
||||||
return ToWritableNotifyCollectionChanged(null);
|
return ToWritableNotifyCollectionChanged(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedViewList<T> ToWritableNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
|
public NotifyCollectionChangedSynchronizedViewList<T> ToWritableNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
|
||||||
{
|
{
|
||||||
return ToWritableNotifyCollectionChanged(
|
return ToWritableNotifyCollectionChanged(
|
||||||
static x => x,
|
static x => x,
|
||||||
@ -36,12 +36,12 @@ namespace ObservableCollections
|
|||||||
collectionEventDispatcher);
|
collectionEventDispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged<TView>(Func<T, TView> transform, WritableViewChangedEventHandler<T, TView>? converter)
|
public NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged<TView>(Func<T, TView> transform, WritableViewChangedEventHandler<T, TView>? converter)
|
||||||
{
|
{
|
||||||
return ToWritableNotifyCollectionChanged(transform, converter, null!);
|
return ToWritableNotifyCollectionChanged(transform, converter, null!);
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged<TView>(Func<T, TView> transform, WritableViewChangedEventHandler<T, TView>? converter, ICollectionEventDispatcher? collectionEventDispatcher)
|
public NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged<TView>(Func<T, TView> transform, WritableViewChangedEventHandler<T, TView>? converter, ICollectionEventDispatcher? collectionEventDispatcher)
|
||||||
{
|
{
|
||||||
return new NonFilteredSynchronizedViewList<T, TView>(CreateView(transform), isSupportRangeFeature: false, collectionEventDispatcher, converter);
|
return new NonFilteredSynchronizedViewList<T, TView>(CreateView(transform), isSupportRangeFeature: false, collectionEventDispatcher, converter);
|
||||||
}
|
}
|
||||||
@ -367,12 +367,54 @@ namespace ObservableCollections
|
|||||||
source.Add(value);
|
source.Add(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public void InsertIntoSourceCollection(int index, T value)
|
||||||
|
{
|
||||||
|
lock (SyncRoot)
|
||||||
|
{
|
||||||
|
source.Insert(index, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveFromSourceCollection(T value)
|
||||||
|
{
|
||||||
|
lock (SyncRoot)
|
||||||
|
{
|
||||||
|
return source.Remove(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAtSourceCollection(int index)
|
||||||
|
{
|
||||||
|
lock (SyncRoot)
|
||||||
|
{
|
||||||
|
source.RemoveAt(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearSourceCollection()
|
||||||
|
{
|
||||||
|
lock (SyncRoot)
|
||||||
|
{
|
||||||
|
source.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IWritableSynchronizedViewList<TView> ToWritableViewList(WritableViewChangedEventHandler<T, TView> converter)
|
public IWritableSynchronizedViewList<TView> ToWritableViewList(WritableViewChangedEventHandler<T, TView> converter)
|
||||||
{
|
{
|
||||||
return new FiltableSynchronizedViewList<T, TView>(this, isSupportRangeFeature: true, converter: converter);
|
return new FiltableSynchronizedViewList<T, TView>(this, isSupportRangeFeature: true, converter: converter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged()
|
||||||
|
{
|
||||||
|
return new FiltableSynchronizedViewList<T, TView>(this,
|
||||||
|
isSupportRangeFeature: false,
|
||||||
|
converter: static (TView newView, T originalValue, ref bool setValue) =>
|
||||||
|
{
|
||||||
|
setValue = true;
|
||||||
|
return originalValue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter)
|
public NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter)
|
||||||
{
|
{
|
||||||
return new FiltableSynchronizedViewList<T, TView>(this, isSupportRangeFeature: false, converter: converter);
|
return new FiltableSynchronizedViewList<T, TView>(this, isSupportRangeFeature: false, converter: converter);
|
||||||
@ -382,12 +424,12 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
return new FiltableSynchronizedViewList<T, TView>(this,
|
return new FiltableSynchronizedViewList<T, TView>(this,
|
||||||
isSupportRangeFeature: false,
|
isSupportRangeFeature: false,
|
||||||
collectionEventDispatcher,
|
eventDispatcher: collectionEventDispatcher,
|
||||||
static (TView newView, T originalValue, ref bool setValue) =>
|
converter: static (TView newView, T originalValue, ref bool setValue) =>
|
||||||
{
|
{
|
||||||
setValue = true;
|
setValue = true;
|
||||||
return originalValue;
|
return originalValue;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter, ICollectionEventDispatcher? collectionEventDispatcher)
|
public NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter, ICollectionEventDispatcher? collectionEventDispatcher)
|
||||||
|
@ -337,21 +337,22 @@ internal sealed class FiltableSynchronizedViewList<T, TView> : NotifyCollectionC
|
|||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (converter == null || parent is not IWritableSynchronizedView<T, TView> writableView)
|
if (IsReadOnly)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException("This CollectionView does not support set. If base type is ObservableList<T>, you can use ToWritableSynchronizedView and ToWritableNotifyCollectionChanged.");
|
throw new NotSupportedException("This CollectionView does not support Set. If base type is ObservableList<T>, you can use CreateWritableView and ToWritableNotifyCollectionChanged.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var writableView = parent as IWritableSynchronizedView<T, TView>;
|
||||||
var originalIndex = listView.GetAlternateIndex(index);
|
var originalIndex = listView.GetAlternateIndex(index);
|
||||||
var (originalValue, _) = writableView.GetAt(originalIndex);
|
var (originalValue, _) = writableView!.GetAt(originalIndex);
|
||||||
|
|
||||||
// update view
|
// update view
|
||||||
writableView.SetViewAt(originalIndex, value);
|
writableView.SetViewAt(originalIndex, value);
|
||||||
listView[index] = value;
|
listView[index] = value;
|
||||||
|
|
||||||
var setValue = true;
|
var setValue = true;
|
||||||
var newOriginal = converter(value, originalValue, ref setValue);
|
var newOriginal = converter!(value, originalValue, ref setValue);
|
||||||
|
|
||||||
if (setValue)
|
if (setValue)
|
||||||
{
|
{
|
||||||
@ -372,6 +373,8 @@ internal sealed class FiltableSynchronizedViewList<T, TView> : NotifyCollectionC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool IsReadOnly => converter == null || parent is not IWritableSynchronizedView<T, TView>;
|
||||||
|
|
||||||
public override IEnumerator<TView> GetEnumerator()
|
public override IEnumerator<TView> GetEnumerator()
|
||||||
{
|
{
|
||||||
lock (gate)
|
lock (gate)
|
||||||
@ -385,17 +388,91 @@ internal sealed class FiltableSynchronizedViewList<T, TView> : NotifyCollectionC
|
|||||||
|
|
||||||
public override void Add(TView item)
|
public override void Add(TView item)
|
||||||
{
|
{
|
||||||
if (converter == null || parent is not IWritableSynchronizedView<T, TView> writableView)
|
if (IsReadOnly)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException("This CollectionView does not support Add. If base type is ObservableList<T>, you can use ToWritableSynchronizedView and ToWritableNotifyCollectionChanged.");
|
throw new NotSupportedException("This CollectionView does not support Add. If base type is ObservableList<T>, you can use CreateWritableView and ToWritableNotifyCollectionChanged.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var writableView = parent as IWritableSynchronizedView<T, TView>;
|
||||||
|
if (typeof(T) == typeof(TView) && item is T tItem)
|
||||||
|
{
|
||||||
|
writableView!.AddToSourceCollection(tItem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
var setValue = false;
|
var setValue = false;
|
||||||
var newOriginal = converter(item, default!, ref setValue);
|
var newOriginal = converter!(item, default!, ref setValue);
|
||||||
|
|
||||||
// always add
|
// always add
|
||||||
writableView.AddToSourceCollection(newOriginal);
|
writableView!.AddToSourceCollection(newOriginal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Insert(int index, TView item)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("This CollectionView does not support Insert. If base type is ObservableList<T>, you can use CreateWritableView and ToWritableNotifyCollectionChanged.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var writableView = parent as IWritableSynchronizedView<T, TView>;
|
||||||
|
if (typeof(T) == typeof(TView) && item is T tItem)
|
||||||
|
{
|
||||||
|
writableView!.InsertIntoSourceCollection(index, tItem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var setValue = false;
|
||||||
|
var newOriginal = converter!(item, default!, ref setValue);
|
||||||
|
|
||||||
|
writableView!.InsertIntoSourceCollection(index, newOriginal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Remove(TView item)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("This CollectionView does not support Remove. If base type is ObservableList<T>, you can use CreateWritableView and ToWritableNotifyCollectionChanged.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var writableView = parent as IWritableSynchronizedView<T, TView>;
|
||||||
|
if (typeof(T) == typeof(TView) && item is T tItem)
|
||||||
|
{
|
||||||
|
return writableView!.RemoveFromSourceCollection(tItem);
|
||||||
|
}
|
||||||
|
var setValue = false;
|
||||||
|
var newOriginal = converter!(item, default!, ref setValue);
|
||||||
|
|
||||||
|
// always add
|
||||||
|
return writableView!.RemoveFromSourceCollection(newOriginal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RemoveAt(int index)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("This CollectionView does not support RemoveAt. If base type is ObservableList<T>, you can use CreateWritableView and ToWritableNotifyCollectionChanged.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var writableView = parent as IWritableSynchronizedView<T, TView>;
|
||||||
|
writableView!.RemoveAtSourceCollection(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Clear()
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("This CollectionView does not support Clear. If base type is ObservableList<T>, you can use CreateWritableView and ToWritableNotifyCollectionChanged.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var writableView = parent as IWritableSynchronizedView<T, TView>;
|
||||||
|
writableView!.ClearSourceCollection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -762,20 +839,21 @@ internal sealed class NonFilteredSynchronizedViewList<T, TView> : NotifyCollecti
|
|||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (converter == null || parent is not IWritableSynchronizedView<T, TView> writableView)
|
if (IsReadOnly)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException("This CollectionView does not support set. If base type is ObservableList<T>, you can use ToWritableSynchronizedView and ToWritableNotifyCollectionChanged.");
|
throw new NotSupportedException("This CollectionView does not support set. If base type is ObservableList<T>, you can use ToWritableNotifyCollectionChanged.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var (originalValue, _) = writableView.GetAt(index);
|
var writableView = parent as IWritableSynchronizedView<T, TView>;
|
||||||
|
var (originalValue, _) = writableView!.GetAt(index);
|
||||||
|
|
||||||
// update view
|
// update view
|
||||||
writableView.SetViewAt(index, value);
|
writableView.SetViewAt(index, value);
|
||||||
listView[index] = value;
|
listView[index] = value;
|
||||||
|
|
||||||
var setValue = true;
|
var setValue = true;
|
||||||
var newOriginal = converter(value, originalValue, ref setValue);
|
var newOriginal = converter!(value, originalValue, ref setValue);
|
||||||
|
|
||||||
if (setValue)
|
if (setValue)
|
||||||
{
|
{
|
||||||
@ -796,6 +874,8 @@ internal sealed class NonFilteredSynchronizedViewList<T, TView> : NotifyCollecti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool IsReadOnly => converter == null || parent is not IWritableSynchronizedView<T, TView>;
|
||||||
|
|
||||||
public override IEnumerator<TView> GetEnumerator()
|
public override IEnumerator<TView> GetEnumerator()
|
||||||
{
|
{
|
||||||
lock (gate)
|
lock (gate)
|
||||||
@ -809,17 +889,91 @@ internal sealed class NonFilteredSynchronizedViewList<T, TView> : NotifyCollecti
|
|||||||
|
|
||||||
public override void Add(TView item)
|
public override void Add(TView item)
|
||||||
{
|
{
|
||||||
if (converter == null || parent is not IWritableSynchronizedView<T, TView> writableView)
|
if (IsReadOnly)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException("This CollectionView does not support Add. If base type is ObservableList<T>, you can use ToWritableSynchronizedView and ToWritableNotifyCollectionChanged.");
|
throw new NotSupportedException("This CollectionView does not support Add. If base type is ObservableList<T>, you can use ToWritableNotifyCollectionChanged.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var writableView = parent as IWritableSynchronizedView<T, TView>;
|
||||||
|
if (typeof(T) == typeof(TView) && item is T tItem)
|
||||||
|
{
|
||||||
|
writableView!.AddToSourceCollection(tItem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
var setValue = false;
|
var setValue = false;
|
||||||
var newOriginal = converter(item, default!, ref setValue);
|
var newOriginal = converter!(item, default!, ref setValue);
|
||||||
|
|
||||||
// always add
|
// always add
|
||||||
writableView.AddToSourceCollection(newOriginal);
|
writableView!.AddToSourceCollection(newOriginal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Insert(int index, TView item)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("This CollectionView does not support Insert. If base type is ObservableList<T>, you can use ToWritableNotifyCollectionChanged.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var writableView = parent as IWritableSynchronizedView<T, TView>;
|
||||||
|
if (typeof(T) == typeof(TView) && item is T tItem)
|
||||||
|
{
|
||||||
|
writableView!.InsertIntoSourceCollection(index, tItem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var setValue = false;
|
||||||
|
var newOriginal = converter!(item, default!, ref setValue);
|
||||||
|
|
||||||
|
writableView!.InsertIntoSourceCollection(index, newOriginal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Remove(TView item)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("This CollectionView does not support Remove. If base type is ObservableList<T>, you can use ToWritableNotifyCollectionChanged.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var writableView = parent as IWritableSynchronizedView<T, TView>;
|
||||||
|
if (typeof(T) == typeof(TView) && item is T tItem)
|
||||||
|
{
|
||||||
|
return writableView!.RemoveFromSourceCollection(tItem);
|
||||||
|
}
|
||||||
|
var setValue = false;
|
||||||
|
var newOriginal = converter!(item, default!, ref setValue);
|
||||||
|
|
||||||
|
// always add
|
||||||
|
return writableView!.RemoveFromSourceCollection(newOriginal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RemoveAt(int index)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("This CollectionView does not support RemoveAt. If base type is ObservableList<T>, you can use ToWritableNotifyCollectionChanged.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var writableView = parent as IWritableSynchronizedView<T, TView>;
|
||||||
|
writableView!.RemoveAtSourceCollection(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Clear()
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("This CollectionView does not support Clear. If base type is ObservableList<T>, you can use ToWritableNotifyCollectionChanged.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var writableView = parent as IWritableSynchronizedView<T, TView>;
|
||||||
|
writableView!.ClearSourceCollection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -860,4 +1014,4 @@ internal sealed class NonFilteredSynchronizedViewList<T, TView> : NotifyCollecti
|
|||||||
parent.ViewChanged -= Parent_ViewChanged;
|
parent.ViewChanged -= Parent_ViewChanged;
|
||||||
parent.Dispose(); // Dispose parent
|
parent.Dispose(); // Dispose parent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user