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
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.
- ViewModel Resolution with Autofac « Mas-Tool's Favorites | http://mas-tool.com/?p=2771