ViewModel Resolution with Autofac

In Comicster, there's only one way to edit any item in your collection, and that's to select it and click the "Properties" button:

Comicster's Properties Button

When you click this button, Comicster knows which item you have selected, but needs to create an instance of the corresponding "editor" ViewModel class. For example, if you have selected an Issue (as I have in the screenshot above) then Comicster needs a new IssueEditorViewModel to hand off to the view. To make this easy, I use convention over configuration and a little Autofac magic.

First, when I build my container at the application startup, I register all the ViewModel classes that derive from the abstract base EditorViewModel class, with a name that matches the type name:

builder.RegisterAssemblyTypes(System.Reflection.Assembly.GetExecutingAssembly())
    .Where(t => typeof(EditorViewModel).IsAssignableFrom(t))
    .Named(t => t.Name, typeof(EditorViewModel));

So that means that IssueEditorViewModel is registered in the container with that name.

Next, I register a special factory type that resolves an editor given a type that implements IEditableObject:

builder.Register<Func<CollectionViewModel, IEditableObject, EditorViewModel>>(
    container => 
        (c, e) => 
            container.ResolveNamed<Owned<EditorViewModel>>(
                e.GetType().Name + "EditorViewModel",
                new PositionalParameter(0, c),
                new PositionalParameter(1, e)
                ).Value);

I've split that across several lines so you can read it, but here's the breakdown:

Any time the caller asks for a function that takes a CollectionViewModel and an IEditableObject and returns an EditorViewModel, we need to return a function that does this:

First, resolve an instance from the container with the name of the IEditableObject type we've passed in with "EditorViewModel" appended. So now if we pass in an Issue then we'll be asking for IssueEditorViewModel. So far so good. You'll notice, too, that I'm actually asking for an Owned<EditorViewModel> so that Autofac knows that I'll be disposing of it when I'm done (in this case, when the user closes the Properties dialog).

ResolveNamed isn't quite smart enough to match the constructor parameters of the type I'm trying to resolve with the ones I passed in, so I'm also giving it a couple of PositionalParameter instances to help it along. That way the CollectionViewModel instance and the IEditableObject that I'm trying to edit will both get passed into the editor ViewModel's constructor.

So that's it! Here's the code that gets an editor ViewModel for the selected item:

var item = e.Parameter as IEditableObject;
if (item == null) return;

var message = _editorViewModelFactory(this, item);
MessengerInstance.Send(message);

It certainly makes for a simple "ShowProperties" method! The main window of the application responds to any kind of EditorViewModel sent down the message queue by showing a basic dialog with that ViewModel as its DataContext.

This is probably a diabolical misuse of Autofac's power, but it makes it really easy to "map" a Model class to its ViewModel equivalent without a messy "if" cascade or dictionary.

.net wpf autofac mvvm
Posted by: Matt Hamilton
Last revised: 24 Apr, 2017 07:03 PM History

Trackbacks

Comments

Miky
Miky
10 Jan, 2011 08:32 PM

I'd recommend you check for Caliburn Micro, I believe it have the best View/ViewModel injector :)

No new comments are allowed on this post.