Will caliburn.micro do the right thing with async method on ViewModel? Will caliburn.micro do the right thing with async method on ViewModel? wpf wpf

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 than async 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.


The answer is 'yes', starting with Caliburn.Micro 1.5.

See release announcement.


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.