In MVVM where the ViewModels update the Views via INotifyPropertyChanged events, there doesn't seem to be much room for the cool feature of async/await; executing the continuation on the captured synchronization context of the invoker.
So, if this is the case, then who will actually use the features of async/await in a modern UI based app? In this context, "who" can also mean what pattern, e.g. MVC-variation.
I can think of the following as a good way of using TAP
ViewModel.Age
{
set {
await Model.SetAge(value);
NotifyPropertyChanged("Age");
}
}
however, having this run on the captured syncContext, does not really help out much. Actually, we could put all this in the model instead.
Model.Age
{
set {
await SetAge(value);
NotifyPropertyChanged("Age");
}
}
and now, we would really like the syncContext NOT to be the captured one.
Actually, having INotifyPropertyChanged.PropertyChanged
raised in the UI synchronization context is required for data binding.
async
/ await
do force you to make a distinction between properties (which represent the current state and are always synchronous) and commands (which represent actions and may be synchronous or asynchronous). Property getters and setters cannot be async
, so your example code with an "async set" is not a possible approach.
async
enables asynchronous commands. You can use command binding to asynchronously handle routed commands, or pass an async
delegate to a DelegateCommand
, or use your own ICommand
implementation. Whichever way you'll do it, you end up with an async void
command event handler.
A realistic example is to have the VM properties set the M properties in-memory, and have a SaveCommand
with an async
handler. It's common to have async
handlers interact with an additional VM property (SaveInProgress
or maybe a common Busy
shared with other async
handlers) so that the UI can respond appropriately when the command is in progress (usually at least causing CanExecute
to return false
).
So your async
handler ends up looking something like:
private async void SaveCommandExecute()
{
try
{
// Set VM property; updates View appropriately.
Busy = true;
// Do the actual saving asynchronously.
await Model.SaveAsync();
}
catch (Exception ex)
{
// Update the VM with error information.
Error = ex.Message;
}
finally
{
// Let the VM know we're done.
Busy = false;
}
}
private void SaveCommandCanExecute()
{
return !Busy;
}
Note that the VM properties (Error
and Busy
) are updated in the captured UI synchronization context.
This illustrates the central concept of async
MVVM: commands may be async
, but the properties (such as Busy
) always represent the current state.
If you're adding async
to an existing MVVM app, you'll find yourself with several additional properties indicating business and possibly also progress updates (e.g., percent complete). Depending on your application, you may permit multiple asynchronous operations at the same time. You'll need to think of good ways to add this information to your Views; I find this to be the most challenging part of async
MVVM apps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With