Search code examples
c#wpfcaliburn.microdisposeapplication-shutdown

Caliburn Micro: Ensure that all OnDeactivates were called before app's shutdown


I've a WPF/Caliburn app which has a ShellView which includes TabControl where each TabItem corresponds to individual ViewModel (they all inherit Screen).
Some of those ViewModels have dispose logic implemented in OnDeactivate:

protected override void OnDeactivate(bool close)
{
    if (close)
        _disposeList.Dispose();

    base.OnDeactivate(close);
}

ShellViewModel overrides TryClose:

public override async void TryClose(bool? dialogResult = default(bool?))
{
    // some dispose logic here
    base.TryClose(dialogResult);
    await Task.Run(() =>
    {
        // show closing message for 2 sec
        using (StartBusy(ViewModelBusyReason.Closing))
        {
            Thread.Sleep(2000);
        }
    });
    Application.Current.Shutdown();
    // Environment.Exit(0);
}

When app closes it only calls OnDeactivate on 1 or 2 VMs, then app shuts down.
How to guarantee OnDeactivate to be called on all VMs before app close?
I tried to add Sleep in TryClose and it didn't work.

And also: is there a canonical Caliburn way to implement TryClose on ShellViewModel?
I.e. do I have to call Application.Current.Shutdown() or Environment.Exit(0) or neither?

ShellViewModel definition:

public class ShellViewModel : Conductor<Screen>.Collection.OneActive
{
    public ShellViewModel(/*some params here*/)
    {
        Items.Add(Bootstrapper.Resolve<ViewModelOne>());
        Items.Add(Bootstrapper.Resolve<ViewModelTwo>());
        Items.Add(Bootstrapper.Resolve<ViewModelThree>());
        // ...more VMs here
    }

Solution

  • Ok, here is a solution which I finally came up with:

    // ShellViewModel
    public override void TryClose(bool? dialogResult = default(bool?))
    {
        _disposeList.Dispose();
    
        while (Items.Any())
           DeactivateItem(Items.First(), true);
    
        base.TryClose(dialogResult);
        Application.Current.Shutdown();
    }
    

    And override OnDeactivate in each individual VM:

    protected override void OnDeactivate(bool close)
    {
        _disposeList.Dispose();
        base.OnDeactivate(close);
    }