wpfdependency-injection

How can I use dependency injection with UserControl instance inside a Window in WPF?


I want to do a navigation bar and the most common way to navigate in WPF application is using UserControls and data templates but if I using dependency injection i can´t use xaml like this:

<Window x:Class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyApp"
        Title="MainWindow" Height="200" Width="300">
    <Grid>
        <local:MyUserControl />
    </Grid>
</Window>

WPF creates the instance of user control and I can´t manage that with my dependency injection container, for example I do this with my MainWindow but I want to do some similar with user control:

public App()
{
    AppHost = Host.CreateDefaultBuilder()
        .ConfigureServices((hostContext, services) =>
        {
            services.AddSingleton<MainWindow>();
        })
        .Build();
}

protected override async void OnStartup(StartupEventArgs e)
{
    await AppHost!.StartAsync();

    MainWindow = AppHost.Services.GetRequiredService<MainWindow>();
    MainWindow.Show();

    base.OnStartup(e);
}

Manage User Controls of WPF with dependency injection


Solution

  • You can create a markup extension to resolve UserControl from a DI container.

    Ioc.cs

    public class Ioc : MarkupExtension {
        public static Func<Type, object> Resolver { get; set; }
        public Type Type { get; set; }
        public override object ProvideValue(IServiceProvider serviceProvider) => Resolver?.Invoke(Type)!;
    }
    

    App.cs

    public partial class App : Application {
        //...
    
        protected override void OnStartup(StartupEventArgs e) {
            //...
            //Configure the resolver so that Ioc knows how to create your UserControls
            Ioc.Resolver = (type) => type != null ? host.Services.GetRequiredService(type) : null!;
        }
    }
    

    MainWindow.xaml

    <Window xmlns:in="clr-namespace:MySuperApp.Infrastructure"
            xmlns:views="clr-namespace:MySuperApp.Views">
        <Grid>
            <ContentControl Content="{in:Ioc Type={x:Type views:MyUserControl}}"/>
        </Grid>
    <Window>
    

    With this technique, you can easily resolve any UserControl (or its ViewModel) by its type.