Will caliburn.micro do the right thing with async method on ViewModel?
The support of asynchronous programming model in Caliburn.Micro is pretty good now.
Few things you can do:
- Use async/await in Action method. Be careful, as action methods are technically event handlers, you shoud do
async void
rather thanasync Task
. - Asynchronous event handlers for Screen's events, like Activated, ViewLoaded and other.
- Asynchronous overrides for Screen's methods: OnInitialize, OnActivate, ... You can override then as
protected override async void OnInitialize(){}
and inside you can await another task. - Convert Coroutines to Tasks. Use
ExecuteAsync()
extension method. Coroutines still have some advantages in some scenarios, like execution context. IHandleWithTask<TMessage>
- pretty handy...
There's a blog post desribing some use cases with few code snippets. And a GitHub repository with sample project I've used to play with async/await in Caliburn.
It's safe, but will break your existing global exception handling. After I did the refactoring, I didn't see any error dialogues anymore, to fix that, I had to subscribe to the Coroutine.Completed
event:
Coroutine.Completed += (s, a) =>{ //Do something here ...};
You can do that in your App.xaml.cs
file.
Example from my code on how I handle all possible errors raised in my app:
protected override void OnStartup(StartupEventArgs e){ SetupExceptionHandlers(); base.OnStartup(e);}private void SetupExceptionHandlers(){ AppDomain.CurrentDomain.UnhandledException += (s, a) => { HandleException((Exception)a.ExceptionObject, "AppDomain.CurrentDomain.UnhandledException"); }; Current.DispatcherUnhandledException += (s, a) => { HandleException(a.Exception, "Application.Current.DispatcherUnhandledException"); a.Handled = true; }; TaskScheduler.UnobservedTaskException += (s, a) => { Dispatcher.InvokeAsync(() => HandleException(a.Exception, "TaskScheduler.UnobservedTaskException")); a.SetObserved(); }; Coroutine.Completed += (s, a) => { if (a.Error != null) { HandleException(a.Error, "Coroutine.Completed"); } };}private void HandleException(Exception exception, string source){ logger.Error(exception, "Unhandled exception occured (Source: {0})", source); var msg = new ShowErrorDialogEvent(exception, exception.GetBaseException().Message); eventAggregator.PublishOnUIThread(msg);}
In-case you're wondering, the logger
and eventAggregator
variables are instantiated from the bootstrapper class in the OnStartup
method before calling DisplayRootViewFor
.