community. I'm trying to implement data persistence by using next guide from official documentation:

https://docs.avaloniaui.net/docs/next/concepts/reactiveui/data-persistence

I try to implement state persistence for both browser and desktop. If-else construction used for adopt different MVVM implementations of these key types of environments.

public partial class App : Application
{
    public override void Initialize()
    {
        AvaloniaXamlLoader.Load(this);
    }

    public override void OnFrameworkInitializationCompleted()
    {
        IStateStorage<MainViewModel> stateStorage = ApplicationLifetime is IClassicDesktopStyleApplicationLifetime
            ? new LocalFileStateStorage<MainViewModel>("appstate.json")
            : new WebApiStateStorage<MainViewModel>("http://localhost:5173/ClientState");

        var suspensionDriver = new DefaultSuspensionDriver<MainViewModel>(stateStorage);

        var suspension = new AutoSuspendHelper(ApplicationLifetime);
        RxApp.SuspensionHost.CreateNewAppState = () => new MainViewModel();
        RxApp.SuspensionHost.SetupDefaultSuspendResume(suspensionDriver);
        suspension.OnFrameworkInitializationCompleted();

        // Load the saved view model state.
        var state = RxApp.SuspensionHost.GetAppState<MainViewModel>();

        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            desktop.MainWindow = new MainWindow
            {
                DataContext = state
            };
            desktop.MainWindow.Show();
        }
        else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
        {
            singleViewPlatform.MainView = new MainView
            {
                DataContext = state
            };
        }

        base.OnFrameworkInitializationCompleted();
    }

But as a result of running this code in browser I got error:

Don't know how to detect app exit event for Avalonia.BrowserSingleViewLifetime.

Does somebody know how to fix it and make browser load and save the ViewModel state?

1

There are 1 answers

0
David Levin On BEST ANSWER

Avalonia community helped me and gave a hint, that when you close browser tab - it 'just closes', without event and ability to handle it. After living some time with this information, I decided to dig into Avalonia sources and found next code of AutoSuspendHelper constructor:

    public AutoSuspendHelper(IApplicationLifetime lifetime)
    {
        RxApp.SuspensionHost.IsResuming = Observable.Never<Unit>();
        RxApp.SuspensionHost.IsLaunchingNew = _isLaunchingNew;

        if (Avalonia.Controls.Design.IsDesignMode)
        {
            this.Log().Debug("Design mode detected. AutoSuspendHelper won't persist app state.");
            RxApp.SuspensionHost.ShouldPersistState = Observable.Never<IDisposable>();
        }
        else if (lifetime is IControlledApplicationLifetime controlled)
        {
            this.Log().Debug("Using IControlledApplicationLifetime events to handle app exit.");
            controlled.Exit += (sender, args) => OnControlledApplicationLifetimeExit();
            RxApp.SuspensionHost.ShouldPersistState = _shouldPersistState;
        }
        else if (lifetime != null)
        {
            var type = lifetime.GetType().FullName;
            var message = $"Don't know how to detect app exit event for {type}."; // Here exception throws!
            throw new NotSupportedException(message);
        }
        else
        {
            var message = "ApplicationLifetime is null. "
                        + "Ensure you are initializing AutoSuspendHelper "
                        + "after Avalonia application initialization is completed.";
            throw new ArgumentNullException(message);
        }
        
        var errored = new Subject<Unit>();
        AppDomain.CurrentDomain.UnhandledException += (o, e) => errored.OnNext(Unit.Default);
        RxApp.SuspensionHost.ShouldInvalidateState = errored;
    }

Discovering interfaces hierarchy, I found that community gave absolutely correct hint, and decided to rewrite my App class for at least loading of the state from my WebApi server with thoughts to implement some manual state saving in future:

public partial class App : Application
{
    public override void Initialize()
    {
        AvaloniaXamlLoader.Load(this);
    }

    public override void OnFrameworkInitializationCompleted()
    {
        IStateStorage<MainViewModel> stateStorage = ApplicationLifetime is IClassicDesktopStyleApplicationLifetime
            ? new LocalFileStateStorage<MainViewModel>("appstate.json")
            : new WebApiStateStorage<MainViewModel>("http://localhost:5173/ClientState");

        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            // now this code 
            var suspensionDriver = new DefaultSuspensionDriver<MainViewModel>(stateStorage);

            var suspension = new AutoSuspendHelper(ApplicationLifetime);
            RxApp.SuspensionHost.CreateNewAppState = () => new MainViewModel();
            RxApp.SuspensionHost.SetupDefaultSuspendResume(suspensionDriver);
            suspension.OnFrameworkInitializationCompleted();

            // Load the saved view model state.
            var state = RxApp.SuspensionHost.GetAppState<MainViewModel>();

            desktop.MainWindow = new MainWindow
            {
                DataContext = state
            };
            desktop.MainWindow.Show();
        }
        else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
        {
            var state = stateStorage.Load();

            singleViewPlatform.MainView = new MainView
            {
                DataContext = state
            };
        }

        base.OnFrameworkInitializationCompleted();
    }
}

Conclusion I made is better to Ctrl+click on method with exception than immediately try to google it (for such young framework there is very small knowledge base).