xaml-csharp-development-skill-for-avalonia

Architecture and Lifetime Patterns

Goals

Startup Baseline

using System;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;

internal static class Program
{
    [STAThread]
    public static int Main(string[] args)
        => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);

    public static AppBuilder BuildAvaloniaApp()
        => AppBuilder.Configure<App>()
            .UsePlatformDetect();
}

AppBuilder Orchestration Surface

High-value app startup APIs:

Useful diagnostics/introspection properties while composing startup:

Example:

public static AppBuilder BuildAvaloniaApp()
    => AppBuilder.Configure<App>(() => new App())
        .UsePlatformDetect()
        .AfterPlatformServicesSetup(builder =>
        {
            // Platform services are registered here.
            _ = builder.RuntimePlatformServicesName;
        })
        .AfterSetup(builder =>
        {
            // Application instance exists here.
            _ = builder.Instance;
        });

Application Lifecycle Surface

App-level events and collections often needed in production apps:

Pattern:

public override void OnFrameworkInitializationCompleted()
{
    ResourcesChanged += (_, _) => { };
    ActualThemeVariantChanged += (_, _) => { };
    UrlsOpened += (_, e) =>
    {
        // Compatibility path; prefer IActivatableLifetime for new code.
        _navigation.OpenUrls(e.Urls);
    };

    _ = DataTemplates;
    base.OnFrameworkInitializationCompleted();
}

Application identity and root data context are explicit property-system entries:

Example:

public override void Initialize()
{
    // Set root application identity and default binding context intentionally.
    Name = "MyApp";
    DataContext = _rootShellState;
}

Lifetime Selection and Activation

Main lifetime choices:

Compatibility note:

Related public activation types:

Pattern:

if (ApplicationLifetime is IActivatableLifetime activatable)
{
    activatable.Activated += (_, e) =>
    {
        if (e is ProtocolActivatedEventArgs p)
            _navigation.OpenUri(p.Uri);
        else if (e is FileActivatedEventArgs f)
            _navigation.OpenFiles(f.Files);
    };
}

Desktop Lifetime Operational APIs

Desktop-specific useful APIs:

Event arg types used by controlled lifetime:

Pattern:

if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
    desktop.Startup += (_, e) =>
    {
        string[] args = e.Args;
        _ = args.Length;
    };

    desktop.ShutdownRequested += (_, e) =>
    {
        e.Cancel = !CanCloseSafely();
    };

    desktop.Exit += (_, e) =>
    {
        e.ApplicationExitCode = 0;
    };
}

Additional desktop startup helpers:

Root Assignment Baseline

using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;

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

    public override void OnFrameworkInitializationCompleted()
    {
        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            desktop.MainWindow = new MainWindow
            {
                DataContext = new MainWindowViewModel()
            };
        }
        else if (ApplicationLifetime is ISingleViewApplicationLifetime singleView)
        {
            singleView.MainView = new MainView
            {
                DataContext = new MainViewModel()
            };
        }

        base.OnFrameworkInitializationCompleted();
    }
}

Common Architecture Mistakes

  1. Mixing business logic into Program.cs.
    • Fix: keep Program.cs focused on AppBuilder composition.
  2. Assigning MainWindow/MainView outside OnFrameworkInitializationCompleted().
    • Fix: assign roots only after lifetime is established.
  3. Shutdown logic spread across views/viewmodels.
    • Fix: route lifecycle shutdown through one app-level service/lifetime adapter.
  4. Using startup callback hooks without clear ownership.
    • Fix: keep AfterSetup/AfterPlatformServicesSetup callbacks minimal and deterministic.