Adding Autofac to WPF MVVM application

17,783

Solution 1

Expanding on my comment above:

I use Autofac with all my WPF MVVM applications, I believe it to be one of the better DI frameworks - this is my opinion, but I think it is valid.

Also for me PRISM should be avoided 99% of the time, it's a 'solution looking for a problem' and since most people don't build dynamically composable runtime solutions in WPF it is not needed, i'm sure people would\will disagree.

Like any architectural patterns there is a setup\configuration phase to the application life-cycle, put simply in your case before the first View (window) is shown there will be a whole of setup done for Dependency Injection, Logging, Exception Handling, Dispatcher thread management, Themes etc.

I have several examples of using Autofac with WPF\MVVM, a couple are listed below, I would say look at the Simple.Wpf.Exceptions example:

https://github.com/oriches/Simple.Wpf.Exceptions

https://github.com/oriches/Simple.Wpf.DataGrid

https://github.com/oriches/Simple.MahApps.Template

Solution 2

You can use a similar technique as your console application:

class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<Cleaner>().As<ICleaner>();
        builder.RegisterType<Repository>().AsImplementedInterfaces().InstancePerLifetimeScope();

        // Add the MainWindowclass and later resolve
        build.RegisterType<MainWindow>().AsSelf();

        var container = builder.Build();

        using (var scope = container.BeginLifetimeScope())
        {
            var main = scope.Resolve<MainWindow>();
            main.ShowDialog();
        }
    }
}

Be sure to mark Main with [STAThread]. Then in the project's properties, under the Application tab, set the Startup object to the Program class.

However, I am not certain of the implications of not running App.Run() and of running MainWindow.ShowDialog() instead.

To do the same using App.Run(), do the following:

1) delete StartupUri="MainWindow.xaml" from App.xaml

2) Add the following to App.xaml.cs

protected override void OnStartup(StartupEventArgs e)
{
    var builder = new ContainerBuilder();
    builder.RegisterType<Cleaner>().As<ICleaner>();
    builder.RegisterType<Repository>().AsImplementedInterfaces().InstancePerLifetimeScope();

    // Add the MainWindowclass and later resolve
    build.RegisterType<MainWindow>().AsSelf();

    var container = builder.Build();

    using (var scope = container.BeginLifetimeScope())
    {
        var window = scope.Resolve<MainWindow>();
        window.Show();
    }
}

Solution 3

WPF doesn't have a natural composition root or easy DI integration. Prism is a pretty common set of libraries specifically intended to bridge that for you.

(That's not Autofac specific - it's general guidance for adding DI to WPF apps.)

Share:
17,783
Ben
Author by

Ben

Updated on July 11, 2022

Comments

  • Ben
    Ben almost 2 years

    I can't seem to find an solution to this problem. I've seen several questions about this, but none really give me a solution. I am totally new to Autofac and haven't really done much WPF + MVVM, but know the basics.

    I have a WPF application (using ModernUI for WPF) which I'm trying to add Autofac to, and I am having a hard time figuring out how to resolve my services within all the views, since they have no access to my container. I have a main view, which is my entry point, where I set up my container:

    public partial class MainWindow : ModernWindow
    {
        IContainer AppContainer;
    
        public MainWindow()
        {
    
            SetUpContainer();
    
            this.DataContext = new MainWindowViewModel();
            InitializeComponent();
    
            Application.Current.MainWindow = this; 
        }
    
        private void SetUpContainer()
        {
            var builder = new ContainerBuilder();
    
            BuildupContainer(builder);
    
            var container = builder.Build();
    
            AppContainer = container;
        }
    
        private void BuildupContainer(ContainerBuilder builder)
        {
            builder.RegisterType<Logger>().As<ILogger>();
            ...
        }
    }
    

    The problem I'm having is figuring out how I can resolve my logger and other services within my other views, where I inject all my dependencies through the ViewModel constructor, like so:

    public partial class ItemsView : UserControl
    {
        private ItemsViewModel _vm;
    
        public ItemsView()
        {
            InitializeComponent();
    
            IFileHashHelper fileHashHelper = new MD5FileHashHelper();
            ILibraryLoader libraryLoader = new LibraryLoader(fileHashHelper);
            ILogger logger = new Logger();
    
            _vm = new ItemsViewModel(libraryLoader, logger);
            this.DataContext = _vm;
        }
    }
    

    Some views have a ridiculous amount of injected parameters, and this is where I want Autofac to come in and help me clean things up.

    I was thinking of passing the container to the ViewModel and storing it as a property on my ViewModelBase class, but I've read that this would be an anti-pattern, and even then I don't know if that would automatically resolve my objects within the other ViewModels.

    I managed to put together a simple Console Application using Autofac

    class Program
    {
        static void Main(string[] args)
        {
    
            var builder = new ContainerBuilder();
            builder.RegisterType<Cleaner>().As<ICleaner>();
            builder.RegisterType<Repository>().AsImplementedInterfaces().InstancePerLifetimeScope();
    
            var container = builder.Build();
    
            using (var scope = container.BeginLifetimeScope())
            {
    
                ICleaner cleaner = container.Resolve<ICleaner>();
                cleaner.Update(stream);
            }
        }
    }
    

    but that was simple since it has a single entry point.

    I'd like some ideas on how to add Autofac to my WPF app. I'm sure that I'm doing something wrong. Your help is appreciated.