You may, or may not, have heard that the Microsoft Patterns and Practices team recently shipped the newest version of Prism. With the latest 5.0 version of Prism comes A LOT of breaking changes, bug fixes, new features, and brand new functionality. One of these brand new functional additions is a ViewModelLocator. Now, I personally have never been a fan of ViewModelLocator for a number of reasons, but I must say, after playing around with it and having many conversations with my friend Brian Noyes, that I am really starting to like Prism’s version of ViewModelLocator.
One of the cool things the P&P team did was create a separate set of assemblies for everything MVVM for Prism. This means that there is a new component of Prism, that exists completely separate of Prism.Composition.dll (formerly known as the Prism.dll) assembly. basically, you can use all the MVVM goodies, such as BindableBase, DelegateCommand, CompositeCommand, and ViewModelLocator with absolutely zero dependencies on anything else related to Prism. Since this post concentrates on just the ViewModelLocator, let’s just jump right into the code.
First you will want to create a new XAML application. Since this New Prism.Mvvm component is a PCL, it doesn’t really matter what application type you create. I will use a WPF application for my examples. Next, go ahead an add a new Nuget reference, and search for Prism.Mvvm. Once you find it, install it.
Once installed, you will notice three new references:
- Microsoft.Practices.Prism.Mvvm – contains the bulk of the MVVM features such as BindableBase, DelegateCommand, CompositeCommand, ViewModelLocatorProvider
- Microsoft.Practices.Prism.Mvvm.Desktop – contains a WPF specific attached property for using ViewModelLocator in a WPF application
- Microsoft.Practices.Prism.SharedInterfaces – Contains IActiveAware which Prism.Mvvm is dependent upon.
Before we can really get into using the ViewModelLocator, we need to supply a simple View and ViewModel to use as an example. First we need to create two folders in our project; one called Views which will contain our Views, and another called ViewModels which will contain our ViewModels. We also need to create a View, which I am going to call MainDemo, and a ViewModel that I am going to call MainDemoViewModel. I made sure I followed the ViewModelLocator convention and prefixed my ViewModel name with “MainDemo”, and added “ViewModel”, to make the complete name MainDemoViewModel.
IMPORTANT: Prism’s ViewModelLocator is convention based and requires that your Views, and ViewModels, be located in their respective Views and ViewModels folders. Technically this is because when you create classes in these folders the namespace match the folder structure. So this requirement really exists on the namespaces, meaning as long as your namespaces meet the convention, the actual folder structure can be anything you want. Not only that, the ViewModel name must be prefixed with the corresponding Views name, plus “ViewModel”. If you don’t, it won’t work! I personally hate this convention, and I’ll show you how to change it later.
You should have something similar to this:
Here is our simple View:
Here is our simple ViewModel:
Don’t forget to update your App.xaml StartUpUri to use our newly created View.
Now that we have all the hard stuff out of the way. We need to set the DataContext of the View to an instance of our ViewModel. In order to do that, we will be using the ViewModelLocator. Let’s makes some changes to our View.
As you can see, we added a namespace that contains the ViewModelLocator attached property which is located in the Microsoft.Practices.Prism.Mvvm.Desktop assembly. Then we defined the ViewModelLocator.AutoWireViewModel attached property on our View and set the value to true. If you try to run the application as is, it’s not going to work. Why? Well, because we need ot make a small change to our View’s code-behind.
You will notice that we added an interface to our View called IView. This interface is really easy to implement. Actually, it’s already implemented. IView simply contains a property called DataContext which is of type Object. Hey, what do you know, our View already has that property defined. Great, we don’t need to do anything else. Let’s run the app and see what happens.
YAY! Success! The ViewModelLocator magically created an instance of our ViewModel, and set it as the DataContext for our View. Very nice!
Change those Nasty Conventions
If you are anything like me, I really hate putting my ViewModels in a folder called ViewModels! This is actually one of my biggest no-no’s that I tell people not to do when creating large production applications using MVVM. Why? Because you are taking the class that contains all the state and business logic for a View and putting it somewhere in your app that is nowhere close to the actual View. Come on guys! It’s hard enough to find anything in the Visual Studio Solution Explorer as it is. Now you want to place distance between files that are directly related to each other? This seems to work when you first start writing an app, or for smaller apps, but think about your workflow when the applications get large. Folders are expanded, tons of ViewModels are in one location, tons of Views are in another. You have to jump around the Solution Explorer to find the right folders, and then scan those list of files in the folder to find related files is just a big pain in the butt. I always place my ViewModels right next to my View. Now there will be scenarios when this won’t make sense and you want to separate your ViewModels from your Views, but for the most part it doesn’t make sense. Trust me, when your applications source code get to the point where it doesn’t all fit in the Solution Explorer without having to scroll, you will be thanking me.
So how exactly do you change these conventions? It’s easy, but not at all obvious. First let’s change up our structure from the “norm”. Let’s drag our MainDemoViewModel, and place it in the same folder as the View. We also want to remove the “ViewModels” name from the namespace of the MainDemoViewModel class, and add “Views” to place it in the same namespace as the MainDemo view. So now you should have something like this:
Now if you try to run the application it won’t work, because we are not following the convention. No problem, let’s just ass a little code to App.xaml.cs.
We can tell the ViewModelLocator how where to find the ViewModels by implementing the ViewModelLocatorProvider.SetDefaultViewTypeToViewModelTypeResolver that is located in the Microsoft.Practices.Prism.Mvvm assembly. Now this method has a confusing name, and is poorly documented. There is no way you would discover on your own what exactly this method should do or return. This method gets called whenever a View is asking for a ViewModel. Then, the ViewModelLocator takes the Type that is returned form this method, and uses it to instantiate an instance of that type, and then uses it as the DataContext of the View using the IView interface. Simply put, this method is responsible for returning the Type of the ViewModel. In the code snippet above, we are getting the Views name, and getting the Views namespace. Next, we create the correct fully qualified name of the ViewModel. Once we have that, we return a proper Type object using the newly generated ViewModel name.
Would you like DI with that?
The other cool thing I like about Prism’s implementation of ViewModelLocator, is that you can easily use a DI (dependency injection) container to resolve your ViewModels, and all of their dependencies. Now, I’m not going to get into what DI is, or how it works, but I will share a quick code snippet on how to use one. You just need to implement the ViewModelLocator.SetDefaultViewModelFactory method.
By default, this method uses Activator.CreateInstance, but as you can see, you can easily change it to use anything you want.
Well, that does it for this post. I hope I gave you enough information to get you started with the new ViewModelLocator that is available in the Prism.Mvvm Nuget package. Be sure to download the source code, and start playing around with it . As always, feel free contact me on my blog, connect with me on Twitter (@brianlagunas), or leave a comment below for any questions or comments you may have.