Reputation: 1234
We are relatively new to ReactiveUI so this may explain why we are having some issues with getting a view model working.
In our view model we have a ReactiveList of a class , for which there is a 'selected' in the class.
In the view model we want to have a 'AnySelected' property such that if there is at least 1 item in the list marked as selected then AnySelected is true.
We are having difficulty in getting this work.
As a small test application, with just strings, we have tried this, but messages around changes occurring don't appear.
public class TestRx : ReactiveObject
{
private ReactiveList<string> mySelectedItems;
public ReactiveList<string> MySelectedItems
{
get { return mySelectedItems; }
set { this.RaiseAndSetIfChanged(ref mySelectedItems, value); }
}
public TestRx()
{
var firstList = new ReactiveList<string>();
var t = this.WhenAnyValue(x => x.MySelectedItems);
var t1 = t.Select(x => x ?? new ReactiveList<string>());
var changed = t1.Select(x => x.Changed.Select(_ => Unit.Default));
var itemChanged = t1.Select(x => x.ItemChanged.Select(_ => Unit.Default));
var countChanged = t1.Select(x => x.CountChanged.Select(_ => Unit.Default));
t.Subscribe(x => Debug.WriteLine("T HAS CHANGED {0}", x == firstList));
t1.Subscribe(z => Debug.WriteLine("T1 Changed {0}", z == firstList));
changed.Subscribe(x => Debug.WriteLine("Changed :"));
itemChanged.Subscribe(x => Debug.WriteLine("Item Changed :"));
var replacementList = new ReactiveList<SelItem>(new[] {
new SelItem() { Selected = false }
});
Debug.WriteLine("***********************Assign 1st list");
MySelectedItems = firstList;
Thread.Sleep(100);
Debug.WriteLine("***********************Adding item 2 list");
MySelectedItems.Add("a new string");
// we don't get any debug messages as a result of the above
Thread.Sleep(100);
Debug.WriteLine("***********************Assign null");
MySelectedItems = null;
Thread.Sleep(100);
}
}
What are we doing wrong ?
Upvotes: 3
Views: 2665
Reputation: 74702
This is a common pattern, but it's a bit tricky to implement, because you have to handle all of the following scenarios:
Here's one way to do it. It's complicated, and that hints at a place where future versions of RxUI could make things Better, but here's what you can do for now.
IObservable<bool> WhenAnyAreTrue(IEnumerable<ViewModel> currentElements)
{
// NB: 'Unit' here means, we don't care about the actual value, just
// that something changed
var notifyWhenAnySelectedItemChanges = currentElements
.Select(x => x.WhenAny(y => y.Selected, _ => Unit.Default).Skip(1))
.Merge();
return notifyWhenAnySelectedItemChanges
.StartWith(Unit.Default)
.Select(_ => currentElements.Any(x => x.Selected));
}
// Any time MySelectedItems change or when the items in it change,
// create a new WhenAnyAreTrue and switch to it
this.WhenAnyObservable(x => x.MySelectedItems.ItemsChanged)
.Select(_ => WhenAnyAreTrue(MySelectedItems))
.Switch()
.ToProperty(this, x => x.AnySelected, out anySelected);
Upvotes: 3