Click here to Skip to main content
Click here to Skip to main content

A WPF Template solution using MVVM and Castle Windsor

, 18 Nov 2013
Rate this:
Please Sign up or sign in to vote.
A WPF application base solution using Castle Windsor and commonly used utilities.

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 to 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 come 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 it's 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, than maintaining separation of modules through out your development is quite and easy task.

Using the Code

There are mainly four basic rules you have to keep while building your Castle Based MVVM application:

  1. Interface is the base for everything you use.
  2. Smart Registration of Modules.
  3. Injection of Control.
  4. 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 your self 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
    {
        /// <summary>
        /// Gets or sets the model.
        /// </summary>
        /// <value>
        /// The model.
        /// </value>
        IMapModel TheModel { get; set; }
 
        /// <summary>
        /// Handles the help.
        /// </summary>
        /// <param name="source">The source.</param>
        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 through out your classes and modules, you first need to register them. This is also called Installing.

/// <summary>
/// Performs the installation in the <see cref="T:Castle.Windsor.IWindsorContainer">.
/// </see></summary>
/// <param name="container" />The container.
/// <param name="store" />The configuration store.
public void Install(IWindsorContainer container, IConfigurationStore store)
{
    try
    {
        //Adding this code into the installer tells castle that we 
        //are about to specify a new type factory, we now just need 
        //to add a new line to identify the factory as shown below
        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 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.

/// <summary>
/// Initializes a new instance of the <see cref="MainWindow"> class.
/// </see></summary>
/// <param name="context" />The context.
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:

  1. Create and inject the IMapViewModel which is a property within MapView.
  2. 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 it's 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 display the time along with a button which when clicked, will display a CHM help file.

Main Screen

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:

  1. In this solution, I am using several open source libraries, such as Nog, Avalon Dock,  Event Broker and Castle Windsor. 
  2. I have inserted credits in code when necessary, however in case I forgot to do so somewhere it is not intended and it will be corrected.
  3. This is my first article so bare with me when voting Smile | :)  

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Peer Adi
Software Developer (Senior) Taldor Group
Israel Israel
Project Manager and Application Developer, in a wide variety of business applications. Particularly interested in client/server and Graphical User Interface design using Visual C#.
 
Specialties: 8 Y'rs in C#,10 Y'rs experience in C++ Highly experienced in wide technologies, IT projects etc'.
Follow on   LinkedIn

Comments and Discussions

 
GeneralInteresting Pinmemberdor_z18-Nov-13 21:11 
GeneralGreat article Pinmembershai_ni18-Nov-13 11:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 18 Nov 2013
Article Copyright 2013 by Peer Adi
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid