I am learning WPF and MVVM at the moment and I faced a problem when i tried to write unit tests for a viewmodel, whose commands invoke async
methods. That problem is well-described in this question. That question also has a solution: to write a new Command class with an additional awaitable method that can be awaited in unit tests. But since i use MvvmLight, i decided not to write a new class, but to inherit from the built-in RelayCommand
class instead. However, i don't seem to understand how to do it properly. Below is a simplified example that illustrates my problem:
AsyncRelayCommand:
public class AsyncRelayCommand : RelayCommand
{
private readonly Func<Task> _asyncExecute;
public AsyncRelayCommand(Func<Task> asyncExecute)
: base(() => asyncExecute())
{
_asyncExecute = asyncExecute;
}
public AsyncRelayCommand(Func<Task> asyncExecute, Action execute)
: base(execute)
{
_asyncExecute = asyncExecute;
}
public Task ExecuteAsync()
{
return _asyncExecute();
}
//Overriding Execute like this fixes my problem, but the question remains unanswered.
//public override void Execute(object parameter)
//{
// _asyncExecute();
//}
}
My ViewModel (based on the default MvvmLight MainViewModel):
public class MainViewModel : ViewModelBase
{
private string _welcomeTitle = "Welcome!";
public string WelcomeTitle
{
get
{
return _welcomeTitle;
}
set
{
_welcomeTitle = value;
RaisePropertyChanged("WelcomeTitle");
}
}
public AsyncRelayCommand Command { get; private set; }
public MainViewModel(IDataService dataService)
{
Command = new AsyncRelayCommand(CommandExecute); //First variant
Command = new AsyncRelayCommand(CommandExecute, () => CommandExecute()); //Second variant
}
private async Task CommandExecute()
{
WelcomeTitle = "Command in progress";
await Task.Delay(1500);
WelcomeTitle = "Command completed";
}
}
As far as i understand it, both First and Second variants should invoke different constructors, but lead to the same result. However, only the second variant works the way i expect it to. The first one behaves strangely, for example, if i press the button, that is binded to Command
once, it works ok, but if i try to press it a second time a few seconds later, it simply does nothing.
My understanding of async
and await
is far from complete. Please explain me why the two variants of instantiating the Command
property behave so differently.
P.S.: this behavior is noticeable only when i inherit from RelayCommand
. A newly created class that implements ICommand
and has the same two constructors works as expected.