We saw last time that setting up dependency injection for our viewmodels involved a small number of moving parts. The same applies when applying dependency injection to our views, but with a few additional complexities.
To select the appropriate view to match a given view model, let’s use a naming convention, again based on changing the suffix of the type. We’ll remove
ViewModel from the name, adding either
|View Model Type||Transformation||View Type|
|VocabularyBrowserViewModel||- "ViewModel" + "View"||VocabularyBrowserView|
|WordTutorApplicationViewModel||- "ViewModel" + "Window"||WordTutorApplicationWindow|
Our existing window is called
MainWindow, which doesn’t follow this naming convention, so we rename it to
Once the appropriate type has been selected, the view factory uses our dependency injection container to create the view. After the container is created, it sets the
DataContext of the new view to hook up our data-binding:
The rest of the implementation of
ViewFactory is entirely similar to the
ViewModelFactory that we wrote last time, so I won’t dig into the rest of the code here.
Our next challenge is to put the views into place as a part of our user experience. Fortunately, the design of WPF comes to our rescue.
When using WPF data binding to a view model, you can specify a converter to transform the value. These converters can be very simple - such as applying number formatting to a price - or relatively complicated.
We’re going to create a converter that accepts a view model and returns the right display component to present that view model.
By convention, these converters are named for the transformation they provide, so we end up with the slightly clumsy class name of
The converter needs access to our factory, so we declare the dependency in our constructor:
To do the actual conversion, we check that we’ve got a ViewModel, then delegate to the factory to do the heavy lifting:
We don’t need to support converting back in the other direction, so we just throw an exception:
In theory, we could provide a conversion back by extracting the
DataContext property from the view. However, given the data flow of the redux architecture (where all state flows out from the core of the application), this doesn’t seem necessary.
We now configure data binding to populate our window. Replace the exiting
ContentControl with this declaration:
(Note that I’ve added extra word wrapping here for clarity.)
We’re binding the content of our content control to the
CurrentScreen property of our view model, using the converter we just created.
For this to work, we need to add the converter into a resource dictionary on the window, with the name
ViewModelToViewValueConverter. Most value converters are directly declared in the XAML for the file, but we can’t do this here because we need our converter to be created by SimpleInjector.
Instead, we make the converter a parameter on our window constructor and add it into the resource dictionary ourselves:
Troubleshooting tip: If you do this yourself and have the
InitializeComponent() call first, you’ll get an exception complaining that the resource isn’t available.
Finally, we need to modify our Program to use the view factory to create our window. The code becomes much simpler because we’re delegating the hard work to our dependency injection framework. The hardest bit is that we need to get to the view factory so that we can create the window.
If you run the program now, you should have everything come up working with the word browser screen. Modifying the
WordTutorApplicationStateFactory class, you can also see that it works with the add-word screen.
But, the application isn’t fully working yet; we can’t transition between the screens. There are two reasons for this. Our view models don’t get updated as the application state changes, and we haven’t hooked up any buttons yet.