Adding Autofac to WPF MVVM application
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.)
Ben
Updated on July 11, 2022Comments
-
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.