Reputation: 83
I'm trying to use ReactiveUI to formulate some observables. While some things already work very well, I still wasn't able to find appropriate ReactiveUI methods for other things. I'm still new to ReactiveUI.
public class ViewModel : ReactiveObject {
public ReactiveList<A> aList {get;}
}
public class A : ReactiveObject {
public ReactiveList<B> bList {get;}
}
public class B : ReactiveObject {
//some properties
}
I have a ReactiveList
aList
. It's items are of ReactiveObject
class A
, which contains another ReactiveList
bList
. bList
items as property of ReactiveObject
class B
.
Starting in aList
, how can I react to ANY change inside aList
, bList
and all it's properties?
I was trying something like this:
Observable.Merge(viewModel.aList.Select(x => Observable.Merge(x.bList.Select(y => y.Changed))))
However, this will only observe changes in B
that are already there, when this code is executed. Is there something to automatically observe changes in aList
and bList
and subscribe to the new items aswell?
Or maybe can Observable.Merge
watch a ReactiveList
and automatically subscribe to any new items and unsubscribe from deleted items?
I know, I can do it manually, but that would probably not be using ReactiveUI the way it is intended. Any ideas?
Upvotes: 2
Views: 1327
Reputation: 32162
Instead of using ReactiveList you can try our library ReactiveCompositeCollections which allows you to flatten observable collections using LINQ.
public class ViewModel : ReactiveObject {
public ICompositeSourceList<A> aList {get;}
}
public class A : ReactiveObject {
public ICompositeSourceList<B> bList {get;}
}
public class B : ReactiveObject {
//some properties
}
then
ViewModel vm = ... ;
IComposteList<B> all =
from aItem in vm.aList
from bItem in aItem.bList
select bItem;
// Subscribe to the stream of flattened items
all.Items.Subscribe((ImmutableList<B> bItems)=>DoSometing(bItems));
// or create an INPC object with a property Items
using(var s = allLines.Subscribe()){
RenderLines(s.Items);
}
The nice thing is that this is not LINQ over IEnumerable it is LINQ over ICompositeCollection where ICompositeCollection is a reactive collection. Any changes in the source lists are projected reactively into the result lists.
The internal structures all use immutable lists so performance may be an issue for large collections.
The ICompositeCollection can be converted to an ObservableCollection usable by WPF via
ObservableCollection<B> observableBs =
all.CreateObservableCollection(EqualityComparer<B>.Default)
More details at https://github.com/Weingartner/ReactiveCompositeCollections#using-icompositecollection-with-wpf
Upvotes: 0
Reputation: 3904
The Changed
property on ReactiveList
will let you know when the list has changed. Use this to know when to update the subscription to changes in the contained ReactiveList
s.
To get an observable that ticks every time ReactiveList<A>
and ReactiveList<B>
changes, you can do this:
aList.Changed
.Select(change => Observable.Merge(viewModel.aList.Select(x => Observable.Merge(x.bList.Select(y => y.Changed)))) // Create observable to notify when the sub collections change
.StartWith(change) // Send the notification that aList has changed immediately
)
.Switch() // Only subscribe to the latest change notifications from the sub collections in the most recent aList
.Subscribe(changes => { /* Do something */});
Upvotes: 3