cysharp / observablecollections Goto Github PK
View Code? Open in Web Editor NEWHigh performance observable collections and synchronized views, for WPF, Blazor, Unity.
License: MIT License
High performance observable collections and synchronized views, for WPF, Blazor, Unity.
License: MIT License
I am getting an exception with the code below.
using ObservableCollections;
var seq1 = Enumerable.Range(0, 1);
var seq2 = Enumerable.Range(0, 1);
var zippedSeq = seq1.Zip(seq2, (num1, num2) => (num1, num2));
var list = new ObservableList<(int,int)>();
list.AddRange(zippedSeq); // IndexOutOfRangeException raised.
I think there's something wrong with the CloneCollection construction process when the enumerator doesn't support NonEnumeratedCount.
CloneCollection.TryEnsureCapacity seems to be returning an empty array.
I have two requests.
The IReadonlyReactiveDictionary
that existed in UniRx was easy to use, so we request a function equivalent to it.
If I tried to handle the current ObservableDictionary
Readonly, I would have to divide it into two parts, IReadonlyDictionary
and IObservableCollection<KeyValuePair>
, which is not easy to handle.
When dealing with IObservableCollection in R3, I feel uncomfortable that it is not possible to unsubscribe from the collection side.
For example, could it be possible to make IObservableCollection inherit IDisposable and issue OnCompleted on Dispose()?
Hi,
because filter is attached to the view after creation it is not run for the elements that already in the collection when the view is being created. This breaks Unity example that sets parent in the filer Add event.
What is the correct solution?
I discovered this while using ObvervableList.AddRange(IEnumerable<T> items)
, which uses CloneCollection
to turn the IEnumerable
into a known-length collection as efficiently as possible.
In CloneCollection
there is a bug inside the filling loop when it calls TryEnsureCapacity(ref array, i)
on the array being filled.
Here TryEnsureCapacity()
allocates a larger array without copying the old array items into the new one. As a result, the previous array contents are lost and are null in the final result.
Workaround: it works if I call ToArray()
on my IEnumerable
so that it will call ObservableList.AddRange(T[] items)
instead.
In the Getting Started section of the README it says to read the Unity section, but the section does not mention how to install. I do not believe it's possible to install it through nuget in the package manager, and there isn't a upm branch.
Are we meant to clone the repo and extract the Unity code into a project as a local package, or is there a way to add the package via Package Manager?
Apologies if this is a silly question! Thanks for making something a slick package - I just want to make sure I'm using it correctly :)
I am currently facing a type conversion issue where I am unable to cast ObservableList<Class>
to IObservableCollection<IReadOnlyClass>
. Below is the relevant code snippet that demonstrates the issue:
using ObservableCollections;
public interface IReadOnlyClass
{
int Value { get; }
}
public class Class : IReadOnlyClass
{
public int Value { get; set; }
}
public interface IReadOnlyExample
{
IObservableCollection<IReadOnlyClass> List { get; }
}
public class Example : IReadOnlyExample
{
private ObservableList<Class> _list;
// CS0029: Cannot implicitly convert type 'ObservableList<Class>' to 'IObservableCollection<IReadOnlyClass>'
public IObservableCollection<IReadOnlyClass> List => _list;
}
I am looking for a solution or workaround that would allow this type of conversion, ideally without having to manually create a new collection or extensively modify the existing data structures. Any suggestions or insights would be greatly appreciated.
SerializableObservableList<T>
SerializableObservableDictionary<TKey, TValue>
SerializableObservableHashSet<T>
SerializableObservableQueue<T>
SerializableObservableStack<T>
SerializableObservableRingBuffer<T>
SerializableObservableFixedSizeRingBuffer<T>
Hi, components like Vertical/HorizontalLayoutGroup arrange their elements based on relative order of children inside parent GameObjects. This means game objects has to be moved too when it moves in underlying ObservableList. I am not sure tho how to do it when ChangeKind.Moved happened as there are no index parameters.
[Edit] Very sorry for all the edits. lost connection and things got messed up.
I'm interested in integrating ObservableCollection interfaces with QuikGraph to support the use of a directed graph as a model with MVVM architecture, but the complexity of creating an ISynchronizedView
or ISortableSynchronizedView
for graphs has left me puzzled. In QuikGraph, there are multiple types of collections used to store vertex and edge information, but the most common structure can be summed up as:
<TVertex>
<TVertex>
to List<TEdge> where TEdge : IEdge<TVertex>
. IEdge guarantees that TEdge has TVertex Source and TVertex Target properties.QuikGraph provides an interface with events for vertex addition / removal and edge addition / removal. We can interpret this to mean that one graph has two "observable" views: the vertex list, and all edges regardless of vertex. We could also view the graph to be a Dictionary<TVertex, List<TEdge>>
, but this would imply an ObservableDictionary<TVertex, ObservableList<TEdge>>
... and this brings us back to the integration problem.
Putting aside the QuikGraph dependency for a moment, what would be a valid synchronized view for a graph? Some ideas...
<TVertex>
and <TEdge>
, and require CreateView to be used via IObservableCollection<>.CreateViewIObservableCollection<KeyValuePair<TVertex, IObservableCollection<TEdge>>>
. (big code smell here)I'd be thrilled if I could get some advice on observing these sorts of complex collections.
-[ ] GroupedView
-[ ] LinkedList
I clone the repo and edit the wpf test like below:
// ObservableCollections\sandbox\WpfApp\MainWindow.xaml.cs
// ...
public class ViewModel
{
private ObservableDictionary<int, int> observableList { get; } = [];
public INotifyCollectionChangedSynchronizedView<int> ItemsView { get; }
public ReactiveCommand<Unit> ClearCommand { get; } = new ReactiveCommand<Unit>();
public ReactiveCommand<Unit> RemoveCommand { get; } = new ReactiveCommand<Unit>();
public ViewModel()
{
observableList.Add(1, 1);
observableList.Add(2, 2);
ItemsView = observableList.CreateView(x => x.Value).ToNotifyCollectionChanged();
BindingOperations.EnableCollectionSynchronization(ItemsView, new object());
// var iii = 10;
ClearCommand.Subscribe(_ =>
{
// observableList.Add(iii++);
observableList.Clear();
});
RemoveCommand.Subscribe(_ =>
{
observableList.Remove(1);
});
}
}
// ...
// ObservableCollections\sandbox\WpfApp\MainWindow.xaml
// ...
<StackPanel>
<ListBox ItemsSource="{Binding ItemsView}"/>
<Button Content="Clear" Command="{Binding ClearCommand}" />
<Button Content="Remove 1" Command="{Binding RemoveCommand}" />
</StackPanel>
// ...
Run the application, and click the remove button. It thows InvalidOperation
with message Collection Remove event must specify item position
.
Stack trace below:
at MS.Internal.Data.EnumerableCollectionView.ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)
at System.Windows.Data.CollectionView.PostChange(NotifyCollectionChangedEventArgs args)
at ObservableCollections.Internal.NotifyCollectionChangedSynchronizedView2.OnCollectionChanged(SynchronizedViewChangedEventArgs
2& args) in D:\Documents\ObservableCollections\src\ObservableCollections\Internal\NotifyCollectionChangedSynchronizedView.cs:line 72
at ObservableCollections.Internal.NotifyCollectionChangedSynchronizedView2.ObservableCollections.ISynchronizedViewFilter<T,TView>.OnCollectionChanged(SynchronizedViewChangedEventArgs
2& eventArgs)
at ObservableCollections.SynchronizedViewFilterExtensions.InvokeOnRemove[T,TView](ISynchronizedViewFilter2 filter, T value, TView view, Int32 oldIndex) in D:\Documents\ObservableCollections\src\ObservableCollections\ISynchronizedViewFilter.cs:line 103 at ObservableCollections.ObservableDictionary
2.View1.SourceCollectionChanged(NotifyCollectionChangedEventArgs
1& e) in D:\Documents\ObservableCollections\src\ObservableCollections\ObservableDictionary.Views.cs:line 145
at ObservableCollections.ObservableDictionary2.Remove(TKey key) in D:\Documents\ObservableCollections\src\ObservableCollections\ObservableDictionary.cs:line 176 at WpfApp.ViewModel.<.ctor>b__12_2(Unit _) in D:\Documents\ObservableCollections\sandbox\WpfApp\MainWindow.xaml.cs:line 100 at R3.AnonymousObserver
1.OnNextCore(T value)
Hi, I'm trying to implement a looping scroll view in Unity with an ObservableRingBuffer to recycle views and an ObservableList to maintain the data. Something like this:
`
protected internal ObservableList<IDataContainer> Containers = new();
protected internal ObservableFixedSizeRingBuffer<View> ViewCache = new();
ISynchronizedView<IDataContainer, View> SynchronizedViews = Containers.CreateView(EnsureView);
protected View EnsureView(IDataContainer dataContainer)
{
var view = ViewCache.RemoveFirst();
if (!ViewFactory.Verify(dataContainer, view))
{
view.Dispose();
view = viewPoolManager.GetView(ViewFactory.GetViewPrefab(dataContainer), parent);
}
ViewCache.AddLast(view);
return view;
}
`
This setup works mostly fine until I begin removing dataContainers at which point it becomes really difficult to maintain order.
I've tried using a filter and rotating the indexes of containers as containers are added and removed but I'm getting a lot of Null references when trying to utilize the SynchronizedView.
Id appreciate any advice or suggestions from anyone on how I can get this to work.
Thanks!
Hi there,
First let me thank you for the massive and impressive work you did with this library! Observable collections are really something that should get more love in the .Net ecosystem :)
I noticed something strange with the WPF example that you provide. When I run it locally and press a few times on the "Insert" button, I get the following :
The first 3 lines (in red) correspond to the initial items inserted via AddRange
and is the tuple (value, view)
, while the rest (in green) has been added by pressing the "Insert" button and is only the value.
I do not understand why there is a difference, do you have any idea what could be the reason?
I'm also questioning yielding tuples (value, view)
when enumerating, instead of only views. Tuples cannot be used directly for <ListView ItemSource={...} />
. Is there a reason for this choice?
// WPF simple sample.
ObservableList<int> list;
public ISynchronizedView<int, int> ItemsView { get; set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
list = new ObservableList<int>();
ItemsView = list.CreateView(x => x).ToNotifyCollectionChanged();
BindingOperations.EnableCollectionSynchronization(ItemsView, new object()); // for ui synchronization safety of viewmodel
}
protected override void OnClosed(EventArgs e)
{
ItemsView.Dispose();
}
private void DoStuff()
{
list = GetData();
// ๐??? Should I always Clear the list and then AddRange the data?
ItemsView = list.CreateView(x => x).ToNotifyCollectionChanged();
BindingOperations.EnableCollectionSynchronization(ItemsView, new object()); // for ui synchronization safety of viewmodel
}
The following code will cause an InvalidOperationException
when ObservableList.Clear()
is executed.
<Window x:Class="WpfApp1.MainWindow" ...>
<StackPanel>
<ListBox ItemsSource="{Binding ItemsView}" />
<Button Content="Clear" Command="{Binding ClearCommand}" />
</StackPanel>
</Window>
using ObservableCollections;
using R3;
using System.Windows.Data;
namespace WpfApp1
{
class ViewModel
{
private ObservableList<int> observableList { get; } = new ObservableList<int>();
public INotifyCollectionChangedSynchronizedView<int> ItemsView { get; }
public ReactiveCommand<Unit> ClearCommand { get; } = new ReactiveCommand<Unit>();
public ViewModel()
{
observableList.Add(1);
observableList.Add(2);
ItemsView = observableList.CreateView(x => x).ToNotifyCollectionChanged();
BindingOperations.EnableCollectionSynchronization(ItemsView, new object());
ClearCommand.Subscribe(_ => observableList.Clear());
}
}
}
R3 UnhandledException:System.InvalidOperationException: Collection Remove event must specify item position.
at MS.Internal.Data.EnumerableCollectionView.ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)
at System.Windows.Data.CollectionView.PostChange(NotifyCollectionChangedEventArgs args)
at ObservableCollections.Internal.NotifyCollectionChangedSynchronizedView`2.OnCollectionChanged(ChangedKind changedKind, T value, TView view, NotifyCollectionChangedEventArgs`1& eventArgs) in C:\Source\ObservableCollections-2.0.2\src\ObservableCollections\Internal\NotifyCollectionChangedSynchronizedView.cs:line 72
at ObservableCollections.Internal.NotifyCollectionChangedSynchronizedView`2.ObservableCollections.ISynchronizedViewFilter<T,TView>.OnCollectionChanged(ChangedKind changedKind, T value, TView view, NotifyCollectionChangedEventArgs`1& eventArgs)
at ObservableCollections.SynchronizedViewFilterExtensions.InvokeOnRemove[T,TView](ISynchronizedViewFilter`2 filter, T value, TView view, NotifyCollectionChangedEventArgs`1& eventArgs) in C:\Source\ObservableCollections-2.0.2\src\ObservableCollections\ISynchronizedViewFilter.cs:line 96
at ObservableCollections.SynchronizedViewFilterExtensions.InvokeOnRemove[T,TView](ISynchronizedViewFilter`2 filter, ValueTuple`2 value, NotifyCollectionChangedEventArgs`1& eventArgs) in C:\Source\ObservableCollections-2.0.2\src\ObservableCollections\ISynchronizedViewFilter.cs:line 91
at ObservableCollections.ObservableList`1.View`1.SourceCollectionChanged(NotifyCollectionChangedEventArgs`1& e) in C:\Source\ObservableCollections-2.0.2\src\ObservableCollections\ObservableList.Views.cs:line 237
at ObservableCollections.ObservableList`1.Clear() in C:\Source\ObservableCollections-2.0.2\src\ObservableCollections\ObservableList.cs:line 118
at WpfApp1.ViewModel.<.ctor>b__9_1(Unit _) in C:\Source\WpfApp1\ViewModel.cs:line 22
at R3.AnonymousObserver`1.OnNextCore(T value)
at R3.Observer`1.OnNext(T value)
It occurs because eventArgs.OldStartingIndex
is -1
, which is created by NotifyCollectionChangedEventArgs<T>.Reset()
.
ObservableCollections/src/ObservableCollections/ObservableList.Views.cs
Lines 232 to 241 in bdbb5c0
I tried fixing ObservableList.View, but could not figure out how to fix ObservableDictionary, ObservableHashSet... that does not have Index.
--- a/src/ObservableCollections/ObservableList.Views.cs
+++ b/src/ObservableCollections/ObservableList.Views.cs
@@ -232,9 +232,10 @@ namespace ObservableCollections
case NotifyCollectionChangedAction.Reset:
if (!filter.IsNullFilter())
{
- foreach (var item in list)
+ for (var i = list.Count - 1; i >= 0; i--)
{
- filter.InvokeOnRemove(item, e);
+ var item = list[i];
+ filter.InvokeOnRemove(item, NotifyCollectionChangedEventArgs<T>.Remove(item.Item1, i));
}
}
list.Clear();
dotnet/efcore#7397 Same problem encountered with non-indexed collections and ListBox.
public CloneCollection(T item)
{
this.array = ArrayPool<T>.Shared.Rent(1); // And I do not think rent the array which the lenght is one form pool is a good way.
this.length = 1;
// Miss: this.array[0] = item;
}
Did you forget to assign a value to the variable?
Thank you,
Is there a way to incapsulate any of Observable collections?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.