Since there is also already a WPF/MVVM project in-flight, I have taken this as an opportunity to dig into MVVM frameworks. That is one way of learning a new technology :). The challenge is that Microsoft does not have direct IDE support for MVVM. PRISM is out there, but it tends to be a little heavy weight. After evaluating some of the other frameworks available, I ended up with Jounce (http://jounce.codeplex.com/). Jounce became my experiment of choice, since it is actually very close to some of the initial thoughts I had around MVVM. It utilizes MEF for managing the different parts of the model. With MEF it's a straight forward exercise to dynamically load additional parts and have them automatically snap-in to your application. MEF is a natural match for the loosely coupled architecture that MVVM promotes.
The first thing I did was rip out the existing event aggregator in Jounce and replace it with a Reactive Framework based event aggregator. Since Reactive Framework has hit the market, there is no longer an excuse to roll out your own pub/sub implementation. It also removes some of the ugly code needed for having the event aggregator manage the subscriptions. Within the Reactive Framework the publisher is not responsible for managing subscriptions.
It also turned out that MEF keeps a reference to each object it instantiates. With non-shared view models this results in a memory leak. A shared view model is a singleton: there is only one instance of the view model during the life of your application. A non-shared view model can have multiple instances during the life of the application. Each instance should be free for garbage collection once it is no longer utilized. Since MEF keeps a reference to each object it instantiates, non-shared view models will not be garbage collected. In general, the Jounce support for non-shared view models seems like an afterthought. The Jounce interfaces have many assumptions in them that only apply to 'shared objects'. Strengthening the support for non-shared view models resulted in the following changes:
- Heavy modification of the interfaces in Jounce to be agnostic to the part creation policy. You can now simply use the MEF part creation policy for indicating the shared status of a view model. I choose for never having shared views, since views are driven by the view model and should only be bound to a single view model instance. To keep MEF from creating a reference to the instantiated non-shared view model, some additional plumbing is needed for creating a temporary child container. The use of a temporary child container is described in detail in the MEF documentation. The main thought is that the MEF reference to the instantiated object is released when the child container is disposed.
- Several classes were referencing view and view model data that was imported through MEF. I consolidated that into a single class that exposes the information through an interface.
A side effect of having a non-shared view model is that MEF recomposition will not occur. MEF does not keep a reference to the view model, so it can't perform recomposition for the non-shared view model.
A common requirement is support for different cultures. The original Jounce implementation drives menu names and tooltips through an attribute. This is a rather convenient approach, but it puts up severe challenges for internationalization. So I provided menu/tooltip routes (very similar to the other routes that can be configured in Jounce) that allow you to dynamically get menu/tooltip text. With additional support for a 'Language' property on the base view model and resource file management, this provides a rich set of options for internationalization. As a bonus, I added a public event (through the event aggregator) that notifies listeners when the culture changes and support for a 'CultureDependentAttribute' on properties, so that the we can fire 'OnPropertyChanged' automatically for all culture dependent properties when the culture changes.
The other observation I made was that many of the quick start solutions provided with Jounce are not always clean examples of code that I would like to see in actual production environment. One lesson I have learned is to provide clean examples. Examples are there to guide people that are learning the framework. Chances are they will not recognize that you were just quickly providing a rough outline instead of an actual example of how to correctly utilize the framework. So I spent some time cleaning up the quick start solutions while I was testing my changes to Jounce.
Independent of the question if I will end up utilizing Jounce or not, analyzing and improving a framework is always a good exercise. You end up getting deep insight into underlying issues and solutions while at the same the time sharpening your awareness of the consequences of design choices.
During a discussion with an architect last week I brought the following point up: "Have you ever NOT ended up regretting [for any number of reasons] cutting significant corners on your architecture?" His answer matched my experience: "I have always ended up regretting it."