Skip to content

Commit

Permalink
Merge pull request #82 from zerodev1200/Change-WritableView-implement…
Browse files Browse the repository at this point in the history
…ation

Change WritableView implementation
  • Loading branch information
neuecc authored Oct 18, 2024
2 parents 8913192 + 2e678c8 commit b000cc8
Show file tree
Hide file tree
Showing 6 changed files with 450 additions and 65 deletions.
77 changes: 55 additions & 22 deletions sandbox/WpfApp/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,62 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<!--<Grid>
Title="MainWindow" Height="800" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<ListView ItemsSource="{Binding ItemsView}"></ListView>
<Button Grid.Column="1" Click="Button_Click">Insert</Button>
<!-- original -->
<StackPanel Grid.Row="0" Grid.ColumnSpan="2" Orientation="Vertical">
<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>-->
<StackPanel>
<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}" />
<Button Content="AttachFilter" Command="{Binding AttachFilterCommand}" />
<Button Content="ResetFilter" Command="{Binding ResetFilterCommand}" />
</StackPanel>
</Window>
<!-- Upper left (NotWritable, NonFilter) -->
<GroupBox Grid.Row="1" Grid.Column="0" Header="NotWritable, NonFilter">
<StackPanel Orientation="Vertical">
<DataGrid ItemsSource="{Binding NotWritableNonFilterView}" />
</StackPanel>
</GroupBox>

<!-- Upper right (Writable, NonFilter) -->
<GroupBox Grid.Row="1" Grid.Column="1" Header="Writable, NonFilter">
<StackPanel Orientation="Vertical">
<DataGrid ItemsSource="{Binding WritableNonFilterPersonView}" />
</StackPanel>
</GroupBox>

<!-- 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>
101 changes: 101 additions & 0 deletions sandbox/WpfApp/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,18 @@ public class ViewModel
public ReactiveCommand<Unit> AttachFilterCommand { 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()
{
observableList.Add(1);
Expand Down Expand Up @@ -156,8 +168,97 @@ public ViewModel()
{
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
{
Expand Down
66 changes: 51 additions & 15 deletions src/ObservableCollections/IObservableCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,12 @@ public interface IWritableSynchronizedView<T, TView> : ISynchronizedView<T, TVie
void SetViewAt(int index, TView view);
void SetToSourceCollection(int index, 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);
NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged();
NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter);
NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher);
NotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter, ICollectionEventDispatcher? collectionEventDispatcher);
Expand Down Expand Up @@ -99,8 +104,8 @@ public abstract class NotifyCollectionChangedSynchronizedViewList<TView> :
}

public abstract int Count { get; }
public bool IsReadOnly => false;
public bool IsFixedSize => false;
public virtual bool IsReadOnly { get; } = true;
public bool IsFixedSize => IsReadOnly;
public bool IsSynchronized => true;
public object SyncRoot => gate;

Expand All @@ -112,9 +117,14 @@ public abstract class NotifyCollectionChangedSynchronizedViewList<TView> :
int IList.Add(object? 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);

bool IList.Contains(object? value)
Expand Down Expand Up @@ -149,17 +159,43 @@ static bool IsCompatibleObject(object? value)
return value is TView || value == null && default(TView) == null;
}

void ICollection<TView>.Clear() => throw new NotSupportedException();
void IList.Clear() => throw new NotSupportedException();
void ICollection<TView>.Clear()
{
Clear();
}
void IList.Clear()
{
Clear();
}

void ICollection<TView>.CopyTo(TView[] array, int arrayIndex) => 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.Insert(int index, object? value) => throw new NotSupportedException();
bool ICollection<TView>.Remove(TView item) => throw new NotSupportedException();
void IList.Remove(object? value) => throw new NotSupportedException();
void IList.RemoveAt(int index) => throw new NotSupportedException();
void IList<TView>.Insert(int index, TView item)
{
Insert(index, item);
}

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();
}

Expand All @@ -176,25 +212,25 @@ public static ISynchronizedViewList<TView> ToViewList<T, TView>(this IObservable
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);
}

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);
}

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!);
}

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
return new NonFilteredSynchronizedViewList<T, TView>(collection.CreateView(transform), isSupportRangeFeature: false, collectionEventDispatcher, null);
}
}
}
}
21 changes: 20 additions & 1 deletion src/ObservableCollections/ObservableList.OptimizeView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,25 @@ public override void Add(T 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)
{
Expand All @@ -199,4 +218,4 @@ public override int IndexOf(T item)
{
return parent.IndexOf(item);
}
}
}
Loading

0 comments on commit b000cc8

Please sign in to comment.