Today, we’ll look at enhancing IHostedService integration with built-in DI in WinUI apps.
I’ve previously explained how I use standard DI in WinUI applications. However, that approach has a significant drawback: the host starts after the app launches. While this generally works, it becomes problematic when you need services running before the app starts. For example, if an IHostedService
handles settings storage, those settings won’t load until after launch. This is particularly annoying when we need to check the app’s selected theme before startup—to avoid window flickering:
public App(IServiceProvider serviceProvider)
{
//...
// Configuring the app's selected theme to prevent flickering during startup
var theme = serviceProvider.GetRequiredService<ISettingsService>().WindowTheme.Value;
switch (theme)
{
case WindowTheme.Dark:
RequestedTheme = ApplicationTheme.Dark;
break;
case WindowTheme.Light:
RequestedTheme = ApplicationTheme.Light;
break;
}
}
I considered the complex approach - implementing a custom IHost
(+builder) from scratch. But this seemed too complicated for my needs.
The solution I found was using IHostApplicationLifetime
. This requires changing the host-application relationship. Previously, the host was created within the application and launched when the app started. Now, the host will be created and started first, and the application will only launch after the host (and all its IHostedService
s) are fully initialized:
[STAThread]
public static void Main(string[] args)
{
XamlCheckProcessRequirements();
WinRT.ComWrappersSupport.InitializeComWrappers();
// Build the dependency injection host (configured elsewhere)
using var host = CreateHost();
// Get the application lifetime management service
var lifetime = host.Services.GetRequiredService<IHostApplicationLifetime>();
// Register cleanup handler when host is stopping
lifetime.ApplicationStopping.Register(() => instance?.Exit());
// Defer app creation until host is fully initialized
lifetime.ApplicationStarted.Register(() => Start(_ =>
{
// Establish UI thread synchronization context for WinUI
var context = new DispatcherQueueSynchronizationContext(DispatcherQueue.GetForCurrentThread());
SynchronizationContext.SetSynchronizationContext(context);
// Create WinUI Application instance after host is ready
instance = new App(host.Services, args);
}));
// Start the host (this blocks until shutdown)
host.Run();
}
private static App? instance; // Main application instance
This approach has a clear issue - the Start
method is blocking, and other Register
callbacks will likely malfunction. Yet surprisingly, this specific code (seems to) work. Additional problems may surface during actual use. Therefore, this should be considered purely experimental - at least for now.