After a long summer pause I’m back with this new post. This time I want to investigate an aspect of the Model-View-ViewModel pattern programming with Silverlight. During the summer I've started working on an application where I used Prism to implement MVVM and I found useful to write some code to easily create instance of views during application inizialization and during its lifetime. I really do not know if this is the better solution but I found it easy and useful so I've decided to share it with you. Let me say in this post I assume you are already aware of the reasons and benefits coming from the usage of the popular Model-View-ViewModel pattern and how the pattern works. I also assume you have a minimal knowledge of the Prism toolkit and Unity.
The relationship between the View and the ViewModel
In this section I would like to depict how the View and ViewModel collaborate and what we have to do to create an instance of a View and of its own ViewModel at the sole purpose of preparing the field for explaining my solution.
In the MVVM pattern the View represent the user interface and the ViewModel represent the business logic required to drive the informations displayed on the View. This way, every time we have to create a View we also have to create a specific ViewModel exposing the expected informations to be displayed. The ViewModel is not aware of the View, so It does not know how its properties are consumed, but it simply publish some data through some properties and the View will attach these properties using DataBinding.
What I've said reveal almost two things. The first is that the View does not know how the ViewModel works internally, but it have to know the structure of its surface. In other words the ViewModel have to implement a given well-known "interface" to let the View working fine. This interface contains only properties the View is expected to attach. This implementation is not truly required by the pattern but we can think it is near to reality.
The other thing we have to know is that to let the view binding to the ViewModel, we have to put the ViewModel in the DataContext of the View. So, the creation of the View involve the creation of a specific ViewModel -possibly implementing a given interface - and the assignment of this instance to the DataContext property of the View. Translating this concept in code we have to write something like this:
1: MyView view = new MyView()
2: {
3: DataContext = new MyViewModel()
4: };
In my projects I do not like to write this code because it does not let me impose to the ViewModel an interface implementation. I can obviously implement the interface but there is nothing checking that this implementation has been done. To avoid this problem I've decided to create a service I use every time I need to create a View. The service is responsible to check that the ViewModel implements a specific interface. So the creation of a View become this:
1: // assuming "vbs" is an instance of the service
2: MyView view = vbs.CreateView<MyView>();
What is a service?
Before entering the service implementation details, I would like to explain what is a Service. As I've already said in the previous paragraphs I'm using Prism, a.k.a Composite Application Guidance. Prism is a collection of useful tools to let the developer apply easily the most common architectural patterns. For the purpose of this article we will use Unity, a dependency injection container recently ported to Silverlight from the Microsoft Pattern and Practice team. It is available inside the Prism collection.
Unity is an "Inversion of Control" container. It let you create and register instances of classes - giving them a lifetime manager responsible of how and when the class is created and destroyed - and then locate these classes when you need to use them. Creating a class and registering it in the IOC container let you having something similar to a service, always available and discoverable, you may dedicate to specific responsibility.
The purpose of Unity is to promote layer decoupling to enable unit testing of the applications, but this is out of the scope of this article. If you would like to read something about this argument please refer to the Unity home page on codeplex: http://www.codeplex.com/unity.
Anatomy of the ViewBuilderService
Now that I have briefly explained how a service works it is time to explain the basis of my ViewBuilderService. The trick is that the View have to declare somewhere the interface it expects the ViewModel to implement. We may have an interface "IMyViewModel" and a view "MyView" that can consume a ViewModel implementing this interface. Using a simple custom attribute we can decorate the View codebehind with the interface:
1: [ViewModel(typeof(IMyViewModel))]
2: public partial class MyView : UserControl
3: {
4: public MyView()
5: {
6: }
7: }
Declaring a custom Attribute is simple and it enable the ViewBuilderService to discover the interface to search for in the IOC container. In the next box there is the declaration of the ViewModelAttribute where I use the AttributeUsage attribute to inform the compiler where it have to expect the attribute to be applied.
1: [AttributeUsage(AttributeTargets.Class)]
2: public class ViewModelAttribute : Attribute
3: {
4: public Type ViewModelType { get; set; }
5:
6: public ViewModelAttribute(Type viewModelType)
7: {
8: this.ViewModelType = viewModelType;
9: }
10: }
To connect a ViewModel to its own interface we need to create the ViewModel and register it into the IOC container. This way the ViewBuilderService can read the ViewModelAttribute on the View and ask the container to resolve the interface and give us the ViewModel instance to put in the DataContext. The best place to register the ViewModels is a Module we have declared in the Unity Bootstrapper. In the sample attached to this article I've omitted to use a Module for the purpose of not complicate the sample. So the ViewModels are registered in the Bootstrapper itself.
1: /// <summary>
2: /// Registers the types.
3: /// </summary>
4: private void RegisterTypes()
5: {
6: this.Container.RegisterType<IMyViewModel, MyViewModel>();
7: }
Now all is ready and we can create the service. We have to remember that the service has to be registered in the IOC contained to be discoverable using the ServiceLocator. So we start creating a class that implements an interface IViewBuilderService:
1: public class ViewBuilderService : IViewBuilderService
2: {
3: private static IUnityContainer TheContainer { get; set; }
4:
5: public ViewBuilderService (IUnityContainer theContainer)
6: {
7: this.TheContainer = theContainer;
8: }
9: }
The IViewBuilderService interface have to expose only a CreateView<T>() method we will implement in the service. This method is responsible of finding the ViewModel in the container, creating the View we specified in the T parameter and finally connect view and viewmodel together.
1: public T CreateView<T>()
2: where T : UserControl, new()
3: {
4: ViewModel vm = this.GetViewModel(typeof(T));
5: return new T() { DataContext = vm };
6: }
7:
8: private ViewModel GetViewModel(Type viewType)
9: {
10: ViewModelAttribute attribute =
11: viewType
12: .GetCustomAttributes(typeof(ViewModelAttribute), true)
13: .OfType<ViewModelAttribute>()
14: .SingleOrDefault();
15:
16: if (attribute == null)
17: throw new InvalidOperationException("Missing ViewModelAttribute");
18:
19: ViewModel vm = this.TheContainer.Resolve(attribute.ViewModelType) as ViewModel;
20:
21: if (vm == null)
22: throw new InvalidOperationException("Cannot Resolve ViewModel");
23:
24: return vm;
25: }
Using the ViewBuilderService
It is time to use the service inside our applications. First of all we have to register the service into the IOC container. We can register the service like we already have registered the ViewModel using the RegisterType method. While we can use the same instance of the service in all the application we can use a ContainerControlledLifetimeManager. This type of lifetime manager will transform the service in a singleton instance.
1: /// <summary>
2: /// Registers the types.
3: /// </summary>
4: private void RegisterTypes()
5: {
6: this.Container.RegisterType<IViewBuilderService, ViewBuilder>(
7: new ContainerControlledLifetimeManager());
8:
9: this.Container.RegisterType<IMyViewModel, MyViewModel>();
10: }
Then now using the ServiceLocator we can discover the service when we need to create a View. The ServiceLocator is a Singleton class, that is capable to search registered types by its interface and return an instance. You can use the ServiceLocator everywhere you need to find the IViewBuilderService. Here is the code to create a View and assign to the Shell:
1: Grid grid = this.Shell.FindName("LayoutRoot") as Grid;
2:
3: if (grid != null)
4: {
5: IViewBuilderService vbs =
6: ServiceLocator.Current.GetInstance<IViewBuilderService>();
7:
8: MyView view = vbs.CreateView<MyView>();
9:
10: grid.Children.Add(view);
11: }
Obviously I could use the RegionManager to put the new View into the user interface but I wanted to keep the example simple.
All the code I've tryied to explain in this post is attached at the end of the article. I hope you will find it useful in your applications and I will expect comments from you to improve my design and know your opinion about it.
Download: SilverlightPlayground.ViewBuilderService.zip (~430 kb)