Search code examples
mvvmreactive-programmingreactiveui

ReactiveCommand CanExecute reacting to changes in a collection


I have a ReactiveCollection filled with Items (that are ReactiveObjects as well).

I want to create a ReactiveCommand that should be enabled only when any of the items in the collection has some property set to true, something like:

MyCommand = ReactiveCommand.Create( watch items in collection to see if item.MyProp == true ) 

So anytime there is one of the items with the property set to true, the command should be enabled.

Edit

Thanks to the answerer. The resulting code is this:

public MainViewModel()
{
    Items = new ReactiveList<ItemViewModel>
                {
                    new ItemViewModel("Engine"),
                    new ItemViewModel("Turbine"),
                    new ItemViewModel("Landing gear"),
                    new ItemViewModel("Wings"),
                };

    Items.ChangeTrackingEnabled = true;
        
    var shouldBeEnabled = Items.CreateDerivedCollection(x => x.IsAdded);

    var shouldRecheck = Observable.Merge(
        // When items are added / removed / whatever
        shouldBeEnabled.Changed.Select(_ => Unit.Default),
        // When any of the bools in the coll change
        shouldBeEnabled.ItemChanged.Select(_ => Unit.Default));

    ClearCommand = ReactiveCommand.Create(shouldRecheck.Select(_ => shouldBeEnabled.Any(x => x)));
}

Edit 2

I've discovered a trap! If you modify this line:

new ItemViewModel("Engine");

and set the IsAdded = true like this

new ItemViewModel("Engine") { IsAdded = true };

… when you run the button is disabled when the application starts and it should be enabled. It seems like the expression doesn't evaluate after some change occurs. How can I solve it?


Solution

  • How about this

    mySourceCollection.ChangeTrackingEnabled = true;
    shouldBeEnabled = mySourceCollection.CreateDerivedCollection(x => x.MyProp);
    
    var shouldRecheck = Observable.Merge(
        // When items are added / removed / whatever
        shouldBeEnabled.Changed.Select(_ => Unit.Default),
    
        // When any of the bools in the coll change
        shouldBeEnabled.ItemChanged.Select(_ => Unit.Default));
    
    // Kick off a check on startup
    shouldRecheck = shouldRecheck.StartWith(Unit.Default);
    
    myCmd = ReactiveCommand.Create(shouldRecheck.Select(_ => shouldBeEnabled.All(x => x));