10

I'm facing a strange behavior by my asny ICommand implementation when I tried to disable the command while it's executing by default (even when no CanExecute predicate was passed to it).

public bool CanExecute(object parameter)
{
  if (CanExecutePredicate == null)
  {
    return !mIsExecuting;
  }

  return !mIsExecuting && CanExecutePredicate(parameter);
}

public async void Execute(object parameter)
{
  mIsExecuting = true;
  await ExecuteAsync(parameter);
  mIsExecuting = false;
}

I tried to introduce a private bool, which I set to true just before executing and to false afterwards. When execution is finished the bool is set, but CanExecute is only called after I click a mousebutton or move the mouse or w/e.

Now I tried to call

CanExecute(null);

after

mIsExecuting = false;

but that doesn't help neither. I dont know what I'm missing.

Thanks for your help

EDIT:

To clarify I add the constructors for this class aswell:

 public AsyncRelayCommand(Func<object, Task> execute)
  : this(execute, null)
{
}

public AsyncRelayCommand(Func<object, Task> asyncExecute,
               Predicate<object> canExecutePredicate)
{
  AsyncExecute = asyncExecute;
  CanExecutePredicate = canExecutePredicate;
}

protected virtual async Task ExecuteAsync(object parameter)
{
  await AsyncExecute(parameter);
}
user3292642
  • 711
  • 2
  • 8
  • 32
  • Where does `Execute` come into play? – BytesOfMetal Mar 10 '17 at 07:42
  • When and where you set `CanExecutePredicate`? – Alessandro D'Andria Mar 10 '17 at 07:45
  • @AlessandroD'Andria I don't set it, it is always null for now – user3292642 Mar 10 '17 at 07:46
  • @user3292642 Sorry but it's not clear to me what's your problem, only thing I can see it's a possible race condition with `CanExecutePredicate`. – Alessandro D'Andria Mar 10 '17 at 07:49
  • @AlessandroD'Andria Problem is my GUI is not updated, even tho I call `CanExecute(null);`. Can you elaborate on that possible race condition? – user3292642 Mar 10 '17 at 07:52
  • @user3292642 if you share `CanExecute Predicate` between different threads you can find that `CanExecutePredicate` is null even after passing the `if`, but that's not your problem. Are you awaiting the call to `Execute`? – Alessandro D'Andria Mar 10 '17 at 07:58
  • @AlessandroD'Andria I edited my post. The predicate is either passed to the constructor of my Command or null. Someone who is using this command can decide whether he wants to await it or not. – user3292642 Mar 10 '17 at 08:02
  • You can check this for another approach: [https://github.com/StephenCleary/Mvvm.Async/blob/master/src/Nito.Mvvm.Async/AsyncCommand.cs](https://github.com/StephenCleary/Mvvm.Async/blob/master/src/Nito.Mvvm.Async/AsyncCommand.cs) – Fabio Mar 10 '17 at 08:05

1 Answers1

11

In async scenarios, WPF tends not to know when to check CanExecute, that's why you have the "CanExecuteChanged" event in the Icommand interface.

You should have something like this in your command implementation:

public event EventHandler CanExecuteChanged
{
    add { CommandManager.RequerySuggested += value; }

    remove { CommandManager.RequerySuggested -= value; }
}

public void RaiseCanExecuteChanged()
{
    CommandManager.InvalidateRequerySuggested();
}

With the code above you can now do this:

public async void Execute(object parameter)
{
    mIsExecuting = true;

    RaiseCanExecuteChanged ( ); // Not necessary if Execute is not called locally

    await ExecuteAsync(parameter);
    mIsExecuting = false;

    RaiseCanExecuteChanged ( );
}

This will tell WPF you want to refresh the CanExecute state of command.

Seb
  • 620
  • 1
  • 5
  • 11
  • 2
    Not related to the question, but why(what actual purpose) of using `CommandManager` here? – Fabio Mar 10 '17 at 10:30
  • 1
    It is used to make wpf aware of your concrete commands and tell it to refresh all the CanExecute if need be. It's what the framework Prism uses, though I do find it a bit clunky at the moment. Here's a better explanation : https://joshsmithonwpf.wordpress.com/2008/06/17/allowing-commandmanager-to-query-your-icommand-objects/ – Seb Mar 10 '17 at 10:41