Introduction
I was about to start a new long term project, which required building a WPF client application from scratch. As I was browsing the net for the latest design patterns and code solutions, I came across several approaches on which I could base my project on (Prism, MEF, Unity, etc.) but no article had a fully comprehensive base solution which I could just download and start building my code blocks on.
I decided than, to build such a base solution which will include all that is needed for a WPF client application, make it as generic as I can, so it could be a good start point for every WPF application I will build in the future.
In other words, I was looking to create a basic downloadable WPF toolkit which had all of the commonly used utilities, i.e., Threads manager, Messages dispatcher, Log, Unit Testing, Help Manager, Config file manager and a main Docking Panels based screen. Oh, and a good design pattern to manage them all.
So my main goal when seeking for a good framework design for my project - being a WPF client application - was to not only base it on MVVM pattern, but to fully separate all other assemblies as well to allow a complete individuality of each DLL in my solution. This will enable me as the application comes to form, an easy maintenance and version control of each module separately.
The Problem
So basically, the main difficulty when reaching for a modular component based application is how to get access to a class without having to reference its assembly. This of course can be resolved using Inversion Of Control - but to have a truly pure IOC environment you must implement IOC correctly thus, only have a single place where the IOC’s Register
, Resolve
and Release
methods are called.
Castle Windsor
To achieve such modular, component based design in my application - I had decided to use Castle Windsor by Castle project. Castle Windsor is best of breed, mature Inversion of Control container available for .NET and Silverlight. To try and describe it as simply as I can, Castle forces you to design your code in such way that if you build a good infrastructure, then maintaining separation of modules throughout your development is quite an easy task.
Using the Code
There are mainly four basic rules you have to keep while building your Castle Based MVVM application:
- Interface is the base for everything you use.
- Smart Registration of Modules.
- Injection of Control.
- Separate, do not Scatter.
Let's use the Map library as an example to guide you through what it takes to add your libraries into the template solution. The Map library is the only library I added as my Code Block to the basic template solution. When adding your Code Blocks - follow the following steps to maintain MVVM design and to have it managed via Castle framework.
Interfaces Are The Base
Since interfaces rarely change after an application has come to maturity, your goal should be to have a reference in your main project only to the Interfaces library. If you find yourself in need of reference to other libraries in your main project, refactor what you need into an interface in your Interfaces library.
namespace Interfaces
{
public interface IMapViewModel : IViewModel
{
IMapModel TheModel { get; set; }
void HandleHelp(object source);
}
}
namespace Interfaces
{
public interface IViewModel
{
IHelpManager Help { get; set; }
}
}
You can see here my Map View Model interface which inherits a general IViewModel
interface. The Help
property in IViewModel
will be injected (i.e., Created
and Set
) by the Castle framework. This will also be the case with the IMapModel
which is the Model
class for the Map View.
Smart Registration of Libraries
For you to be able to use the Castle advantages, and have your objects injected throughout your classes and modules, you first need to register them. This is also called Installing.
public void Install(IWindsorContainer container, IConfigurationStore store)
{
try
{
container.AddFacility<typedfactoryfacility>();
container.Register(Component.For<iabstructfactory>().AsFactory());
container.Register(Component.For<ishell<mainwindow>>()
.ImplementedBy<shell>().LifestyleTransient());
container.Register(Classes.FromAssemblyInDirectory(
new AssemblyFilter(AssemblyDirectory))
.BasedOn<iview>()
.Configure(c => c.LifestyleTransient().Named(c.Implementation.Name))
.WithService.Base()
.WithService.FromInterface(typeof(IView)));
container.Register(Classes.FromAssemblyInDirectory(
new AssemblyFilter(AssemblyDirectory))
.BasedOn<iviewmodel>()
.Configure(c => c.LifestyleTransient().Named(c.Implementation.Name))
.WithService.Base()
.WithService.FromInterface(typeof(IViewModel)));
container.Register(Types.FromAssemblyInDirectory(
new AssemblyFilter(AssemblyDirectory + "\\CommonData"))
.Where(type => type.Name.StartsWith("Common"))
.WithService.AllInterfaces());
container.Register(Types.FromAssemblyInDirectory(
new AssemblyFilter(AssemblyDirectory + "\\CommonGui"))
.Where(type => type.Name.StartsWith("Common"))
.WithService.AllInterfaces());
container.Register(Types.FromAssemblyInDirectory(
new AssemblyFilter(AssemblyDirectory + "\\CommonUtilities"))
.Where(type => type.Name.StartsWith("Common"))
.WithService.AllInterfaces());
container.Register(Types.FromAssemblyInDirectory(
new AssemblyFilter(AssemblyDirectory + "\\Map"))
.Where(type => type.Name.StartsWith("Map"))
.WithService.AllInterfaces());
container.Register(Component.For<iviewfactory>()
.AsFactory().LifestyleTransient());
container.Register(Component.For<mainwindow>().LifestyleTransient());
}
catch (Exception ex)
{
MessageBox.Show("Error installing some components: " + ex.Message);
}
}
So if we focus on the Map Library Registration part of this Install block, you can see that I am using the <FromAssemblyInDirectory>
option to locate my Map Library. This is how I can avoid referencing the Map Library in my Main project and load it dynamically when the application is up.
Injection of Control
Now that I have told Castle to look-up all of my Map
classes from a directory called Map, residing under my Assembly Directory - I can be sure that Castle has created a kind of links table from all of the classes and interfaces it had found in my Map
Library and will know to create and inject them accordingly.
public MainWindow(IAbstructFactory context)
{
InitializeComponent();
try
{
#region Creating objects
IMapView theMapView = context.Create<imapview>();
#endregion
#region Releasing objects
context.Release(theMapView);
#endregion
}
catch (Exception ex)
{
MessageBox.Show("Failed to create components: " + ex.Message);
}
}
You can see that in my main window, I am only creating IMapView
.
Castle will take care of all the other initialization and will do the following:
- Create and inject the
IMapViewModel
which is a property within MapView
.
- Create and inject the
IViewModel
and IHelpManager
which are properties within IMapViewModel
.
Separate, do not scatter
It is important to remember that Castle and MVVM are here to make your life as a developer easier in maintaining and building your application as it reaches its mature stage holding thousand of code lines. Be smart in how you choose to design your application - meaning over OOD leads to scattered unmanageable hard to maintain application.
My Source Code
My source code shows a main window with Docked Panels. One of the panels holds the MapView
user control and displays the time along with a button which when clicked, will display a CHM help file.
The time is being updated via Thread, and overall this solution is designed for a Multi Threaded application so it has a threads manager and a countdown object which monitors threads initialization and termination.
Points of Interest
Here are a few points I would like to emphasize in regard to this article:
- In this solution, I am using several open source libraries, such as Nog, Avalon Dock, Event Broker and Castle Windsor.
- I have added credits in code whenever necessary, however in case I forgot to do so somewhere it is not intended and it will be corrected.
- This is my first article so bear with me when voting :)