Goal
Program.Main to the first window or view.ClassicDesktopStyleApplicationLifetime, SingleViewApplicationLifetime, BrowserSingleViewLifetime, HeadlessApplicationLifetime) boots and shuts down your app.Why this matters
Prerequisites
Program.cs, App.axaml, and App.axaml.cs.Program.cs (or Program.fs in F#) is the entry point. A typical template looks like this:
using Avalonia;
using Avalonia.ReactiveUI; // optional in ReactiveUI template
internal static class Program
{
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>() // 1. Choose your Application subclass
.UsePlatformDetect() // 2. Detect the right native backend (Win32, macOS, X11, Android, iOS, Browser)
.UseSkia() // 3. Configure the rendering pipeline (Skia GPU/CPU renderer)
.With(new SkiaOptions { // 4. (Optional) tweak renderer settings
MaxGpuResourceSizeBytes = 96 * 1024 * 1024
})
.LogToTrace() // 5. Hook logging before startup completes
.UseReactiveUI(); // 6. (Optional) enable ReactiveUI integration
}
Each call returns the builder so you can chain configuration. Relevant source:
AppBuilder implementation: src/Avalonia.Controls/AppBuilder.cssrc/Skia/Avalonia.Skia/SkiaOptions.csStartWithClassicDesktopLifetime): src/Avalonia.Desktop/AppBuilderDesktopExtensions.csProgram.Main
`--- BuildAvaloniaApp()
|-- Configure<App>() (create Application instance)
|-- UsePlatformDetect() (choose backend)
|-- UseSkia()/UseReactiveUI (features)
|-- LogToTrace()/With(...) (diagnostics/options)
`-- StartWith...Lifetime() (select lifetime and enter main loop)
If anything in the pipeline throws, the process exits before UI renders. Log early to catch those cases.
| Lifetime type | Purpose | Typical targets | Key members |
|---|---|---|---|
ClassicDesktopStyleApplicationLifetime |
Windowed desktop apps with startup/shutdown events and main window | Windows, macOS, Linux | MainWindow, ShutdownMode, Exit, ShutdownRequested, OnExit |
SingleViewApplicationLifetime |
Hosts a single root control (MainView) |
Android, iOS, Embedded | MainView, MainViewClosing, OnMainViewClosed |
BrowserSingleViewLifetime (implements ISingleViewApplicationLifetime) |
Same contract as single view, tuned for WebAssembly | Browser (WASM) | MainView, async app init |
HeadlessApplicationLifetime |
No visible UI; runs for tests or background services | Unit/UI tests | TryGetTopLevel(), manual pumping |
Key interfaces and classes to read:
ClassicDesktopStyleApplicationLifetime.csSingleViewApplicationLifetime.csBrowserSingleViewLifetime.csAvaloniaHeadlessApplicationLifetime.csMainWindow must be assigned before base.OnFrameworkInitializationCompleted() or no window will appear.ShutdownMode controls when the app exits (OnLastWindowClose, OnMainWindowClose, or OnExplicitShutdown).ShutdownRequested to cancel shutdown (e.g., unsaved document prompt). Call e.Cancel = true to keep the app running.Show() / Close().Control via MainView. Navigation stacks switch the child content instead of opening new windows.MainView.StartWithHeadless disables rendering but still runs the dispatcher. Use it for integration tests.Avalonia.Headless.XUnit or Avalonia.Headless.NUnit to drive UI interactions programmatically.| Purpose | Typical targets | Key members |
| --- | --- | --- | --- |
| ClassicDesktopStyleApplicationLifetime | Windowed desktop apps with startup/shutdown events and main window | Windows, macOS, Linux | MainWindow, ShutdownMode, Exit, OnExit |
| SingleViewApplicationLifetime | Hosts a single root control (MainView) | Android, iOS, Embedded | MainView, MainViewClosing, OnMainViewClosed |
| BrowserSingleViewLifetime (implements ISingleViewApplicationLifetime) | Same contract as single view, tuned for WebAssembly | Browser (WASM) | MainView, async app init |
| HeadlessApplicationLifetime | No visible UI; runs for tests or background services | Unit/UI tests | TryGetTopLevel(), manual pumping |
Key interfaces and classes to read:
ClassicDesktopStyleApplicationLifetime.csSingleViewApplicationLifetime.csBrowserSingleViewLifetime.cssrc/Headless/Avalonia.Headless/AvaloniaHeadlessApplicationLifetime.csApp.OnFrameworkInitializationCompletedApp.axaml.cs is the right place to react once the framework is ready:
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Microsoft.Extensions.DependencyInjection; // if using DI
namespace MultiLifetimeSample;
public partial class App : Application
{
private IServiceProvider? _services;
public override void Initialize()
=> AvaloniaXamlLoader.Load(this);
public override void OnFrameworkInitializationCompleted()
{
// Create/register services only once
_services ??= ConfigureServices();
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
var shell = _services.GetRequiredService<MainWindow>();
desktop.MainWindow = shell;
desktop.Exit += (_, _) => _services.Dispose();
}
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleView)
{
singleView.MainView = _services.GetRequiredService<MainView>();
}
else if (ApplicationLifetime is IControlledApplicationLifetime controlled)
{
controlled.Exit += (_, _) => Console.WriteLine("Application exited");
}
base.OnFrameworkInitializationCompleted();
}
private IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddSingleton<MainWindow>();
services.AddSingleton<MainView>();
services.AddSingleton<DashboardViewModel>();
services.AddLogging(builder => builder.AddDebug());
return services.BuildServiceProvider();
}
}
Notes:
ApplicationLifetime always implements IControlledApplicationLifetime, so you can subscribe to Exit for cleanup even if you do not know the exact subtype.App still runs but you typically return SingleView or host view models manually.Important logging points:
AppBuilder.LogToTrace() uses Avalonia's logging infrastructure (see src/Avalonia.Base/Logging). For production apps, plug in Serilog, Microsoft.Extensions.Logging, or your preferred provider.AppDomain.CurrentDomain.UnhandledException, TaskScheduler.UnobservedTaskException, and Dispatcher.UIThread.UnhandledException to capture failures before they tear down the dispatcher.IControlledApplicationLifetime (ApplicationLifetime) exposes Exit and Shutdown() so you can close gracefully after logging or prompting the user.Example:
[STAThread]
public static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += (_, e) => LogFatal(e.ExceptionObject);
TaskScheduler.UnobservedTaskException += (_, e) => LogFatal(e.Exception);
Dispatcher.UIThread.UnhandledException += (_, e) =>
{
LogFatal(e.Exception);
e.Handled = true; // optionally keep the app alive after logging
};
try
{
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
}
catch (Exception ex)
{
LogFatal(ex);
throw;
}
}
ClassicDesktopStyleApplicationLifetime exposes ShutdownMode, ShutdownRequested, and Shutdown() so you can decide whether to exit on last window close, on main window close, or only when you call Shutdown() explicitly.
You can provide different entry points or compile-time switches:
public static void Main(string[] args)
{
#if HEADLESS
BuildAvaloniaApp().Start(AppMain);
#elif BROWSER
BuildAvaloniaApp().SetupBrowserApp("app");
#else
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
#endif
}
SetupBrowserApp is defined in BrowserAppBuilder.cs and attaches the app to a DOM element.Start (with AppMain) lets you provide your own lifetime, often used in headless/integration tests.Avalonia's headless assemblies let you boot an app without rendering:
using Avalonia;
using Avalonia.Headless;
public static class Program
{
public static void Main(string[] args)
=> BuildAvaloniaApp().StartWithHeadless(new HeadlessApplicationOptions
{
RenderingMode = HeadlessRenderingMode.None,
UseHeadlessDrawingContext = true
});
}
Avalonia.Headless lives under src/Headless and powers automated UI tests (Avalonia.Headless.XUnit, Avalonia.Headless.NUnit).HeadlessUnitTestSession.Run displays an example).Program.cs:
public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseSkia()
.LogToTrace();
[STAThread]
public static void Main(string[] args)
{
if (args.Contains("--single-view"))
{
BuildAvaloniaApp().StartWithSingleViewLifetime(new MainView());
}
else
{
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
}
}
App.axaml.cs sets up both MainWindow and MainView (as shown earlier). At runtime, you can switch lifetimes via command-line or compile condition.
UsePlatformDetect(); on Linux you might need extra packages (mesa, libwebkit) or use UseSkia explicitly.desktop.MainWindow is assigned before calling base.OnFrameworkInitializationCompleted().StartWithSingleViewLifetime) and that your root view is a Control with focusable children.using the provider, keep it alive for the app lifetime and dispose in Exit.ShutdownMode. Default is OnLastWindowClose; switch to OnMainWindowClose or call Shutdown() to exit on demand.App supports both desktop and single-view lifetimes. Use a command-line switch (--mobile) to select StartWithSingleViewLifetime and verify your MainView renders inside a mobile head (Android emulator or dotnet run -- --mobile + SingleView desktop simulation).Microsoft.Extensions.Logging. Log the current lifetime type inside OnFrameworkInitializationCompleted, subscribe to ShutdownRequested, and record when the app exits.MainWindow/MainView through it. Confirm disposal happens when the app exits.BuildAvaloniaApp().Start(AppMain)) and run a unit test that constructs a view, invokes bindings, and pumps the dispatcher.Dispatcher.UIThread.UnhandledException and verify that handled exceptions keep the app alive while unhandled ones terminate.OnFrameworkInitializationCompleted and observe how logging captures the stack. Then add a try/catch to show a fallback dialog or log and exit gracefully.AppBuilder internals: src/Avalonia.Controls/AppBuilder.cssrc/Avalonia.Desktop/AppBuilderDesktopExtensions.cssrc/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cssrc/Avalonia.Controls/ApplicationLifetimes/SingleViewApplicationLifetime.cssrc/Browser/Avalonia.Browser/BrowserSingleViewLifetime.cssrc/HeadlessIControlledApplicationLifetime): src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cssrc/Avalonia.Base/Threading/Dispatcher.csBuildAvaloniaApp() perform before choosing a lifetime?What's next