jamie
jamie

Reputation: 1234

ReactiveList problems

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

Answers (1)

Ana Betts
Ana Betts

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:

  1. The list is set
  2. The list items change
  3. The "Selected" property on any items change. Keep in mind, the items you want to watch, change because of #1 or #2.

How do I do it?

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

Related Questions