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:
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.
Trackbacks
- rimonabantexcellence site title | http://www.rimonabantexcellence.com/t.php?ahr0cdovl21hdhroyw1pbhrvbi5vcmcvdmlld21vzgvslxjlc29sdxrpb24td2l0ac1hdxrvzmfj
- ViewModel Resolution with Autofac « Mas-Tool's Favorites | http://mas-tool.com/?p=2771
No new comments are allowed on this post.
Comments
Miky
I'd recommend you check for Caliburn Micro, I believe it have the best View/ViewModel injector :)