Jerrie Pelser
Jerrie Pelser

Reputation: 548

ReactiveCommand with combined criteria for CanExecute

I have just started with ReactiveUI. I have the following class:

public class MainViewModel : ReactiveObject, IRoutableViewModel 
{
   private string shareText;

   public ReactiveCollection<SharingAccountViewModel> SelectedAccounts { get; private set; }

   public string ShareText 
   { 
      get { return shareText; }
      set
      {
          this.RaiseAndSetIfChanged(ref shareText, value);
      }
   }

   public IReactiveCommand ShareCommand { get; private set; }
}

What I would like to do is to be allow the command to execute when the following criteria is true:

  1. The ShareText property is not null or empty
  2. The SelectedAccounts collection contains at least one value

I have tried the following but it does not work as the button which is hooked up to the command never gets enabled:

ShareCommand = new ReactiveCommand(this.WhenAny(viewModel => viewModel.ShareText,
  viewModel => viewModel.SelectedAccounts,
  (x, y) => !String.IsNullOrEmpty(x.Value) && y.Value.Count > 0));

If I just check for the ShareText property however, it works fine:

  ShareCommand = new ReactiveCommand(this.WhenAny(viewModel => viewModel.ShareText,
            (x) => !String.IsNullOrEmpty(x.Value)));

I looked at the answer for the question ReactiveUI: Using CanExecute with a ReactiveCommand

Based on that I tried the following:

var accountsSelected = SelectedAccounts.CollectionCountChanged.Select(count => count > 0);
ShareCommand = new ReactiveCommand(accountsSelected);

This is working for the second part of my execution criteria because as soon as items are added or removed from the collection, the button which is hooked up to the command gets enabled or disabled correctly.

My question is how do I now combine that with checking that the ShareText property is not null or empty?

I cannot use the this.WhenAny(..) method anymore as the accountsSelected variable is not a property.

Thanks

Upvotes: 1

Views: 2527

Answers (2)

Ana Betts
Ana Betts

Reputation: 74652

Using WhenAny with an IObservable is a bit tricky. Here's how I would do it:

var canShare = Observable.CombineLatest(
    this.WhenAnyObservable(x => x.SelectedAccounts.CollectionCountChanged),
    this.WhenAny(x => x.ShareText, x => x.Value),
    (count, text) => count > 0 && !String.IsNullOrWhitespace(text));

The advantage of WhenAnyObservable here, is that if you decided to reassign SelectedAccounts (i.e. SelectedAccounts = new ReactiveCollection(...);, the above statement would still work, whereas the one above would still be listening to the old collection.

Upvotes: 8

Doug Mitchell
Doug Mitchell

Reputation: 192

I believe that there are different ways to get the expected result and this is just one way.

var canExecute = this.ObservableForProperty(v => v.ShareText)
                         .Select(_ => Unit.Default)
                  .Merge(SelectedAccounts.CollectionCountChanged
                                     .Select(_ => Unit.Default))
                  .Select(_ => !String.IsNullOrEmpty(ShareText)
                                    && SelectedAccounts.Any())
                  .StartWith(false);

ShareCommand = new ReactiveCommand(canExecute);

Upvotes: 1

Related Questions