Click here to Skip to main content
15,867,686 members
Articles / Desktop Programming / WPF

Presentation Model (MVVM) Good Practices

Rate me:
Please Sign up or sign in to vote.
4.81/5 (13 votes)
11 May 2010CPOL16 min read 67.5K   944   76   18
Showing some good practices that can be applied to the Presentation Model/MVVM pattern.

Contents

Introduction

About two months ago, I found Josh Smith's article which introduces a WPF Design Pattern called MVVM. As stated on the article, the MVVM is not a distinct Design Pattern, it's just a WPF "adapted version" of the Presentation Model pattern that was introduced by Martin Fowler. The main difference between MVVM and Presentation Model is that ViewModels have dependencies on WPF libraries to make better use of the WPF native synchronization mechanism and remove all code-behind. Those Presentation Models are perfect for WPF/Silverlight projects, but some people (like me) are still uncomfortable with creating dependencies between an application logic and a specific GUI framework.

In this article's demo, I'm going to show how to design WPF applications using the Presentation Model Design Pattern with the same efficiency as when using MVVM, but without creating dependencies to WPF or any GUI framework. Besides that, some other topics will be covered. Here is a summary:

  • Creating WPF applications using the Presentation Model UI Design Pattern.
  • Using multiple threads in a Presentation Model application without changing the main application logic. This technique is called Aspect Oriented programming.
  • Using dialog boxes in the PresentationModels without creating dependency to a specific GUI framework.
  • Invert the control of your application using the Service Locator Design Pattern.
  • Adding "virtual" behaviors to a class hierarchy using TypeDescriptors.
  • Show some basic usage of Castle Windsor (Inversion of Control container).

Prerequisites

To take most out of this article, you should be familiar with the Presentation Model Design Pattern. A good reading is the original paper, or Josh Smith's article. I am not going to show any XAML, but it is recommended to have some knowledge of WPF/Silverlight to understand what's going on in the WPF project, although this is not necessary (I had my first real contact with WPF by reading Josh Smith's article). At minimum, you should have worked with some kind of technology that provides high level data binding, like ASP.NET or Windows Forms (WPF data binding is by far the most powerful). To break dependencies between the components, I am going to use the Service Locator pattern, so it would help if you have already worked with some pattern that abstracts the creation of services/components. Here is a great reading about the subject.

Demo Overview

This demo will be a tabbed WPF application, much like the one in Josh Smith's tutorial (I even borrowed some XAML :) ). The sample solution will be composed of five projects:

  • The Presentation Model project (PresentationModelBase). For more compatibility, it is going to be a .NET 2.0 Class Library project.
  • The Core Application project (App.Core). It will contain the basic application logic/workflow (PresentationModels) and service contracts. This will also be a .NET 2.0 library.
  • The Data Persistence project (App.Data). It will be a dummy implementation of the only data access interface defined in the core project.
  • The Configuration project (App.Configuration). For the sake of simplicity, this project will configure and contain all the code that will be 'injected' into the application.
  • The WPF front end project (WPFUI). Besides the XAML, this will contain the code to call the configuration class in App.Configuration and start the application.

While this demo will be far from complex, the design used here could be applied in a more real world project.

Presentation Model Project

This project will contain some base classes for the Presentation Model pattern. Almost every PresentationModel class will have a corresponding interface that exposes its public API. This is a good practice because if your components talk using interfaces, their dependencies can be easily replaced, or even mocked for unit testing (which will not be covered here, there are plenty of examples around). Let's start with the base PresentationModel interface:

C#
public interface IPresentationModel : INotifyPropertyChanged, IDisposable
{
    string DisplayName { get; set; }

    void InvokeMethod(string methodName, object[] parameters, params Type[] typeParameters);
}

The DisplayName property is self-explanatory. The InvokeMethod method can be used to call a method in a PresentationModel class. For now, suffice to say that this is going to be a 'gateway' between the user interface and the application logic. Here is the implementation:

C#
public abstract class PresentationModel : IPresentationModel
{
    static PresentationModel()
    {
        _serviceLocator = AppDomain.CurrentDomain.GetData("servicelocator") 
                          as IServiceProvider;
        if (_serviceLocator == null)
            _serviceLocator = CreateProvider();
    }

    private static IServiceProvider CreateProvider()
    {
        ServiceCreator creator = new ServiceCreator();
        return new ServiceContainer(creator);
    }

    #region Fields

    static IServiceProvider _serviceLocator;

    #endregion

    #region Methods

    protected T Get<T>>()
    {
        return (T)_serviceLocator.GetService(typeof(T));
    }

    public virtual void InvokeMethod(string methodName, 
           object[] parameters, params Type[] typeParameters)
    {
        MethodInfo currentMethod = this.GetType().GetMethod(methodName, 
          BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        if (currentMethod == null)
            throw new ArgumentException("Cannot find this method");

        Type returnType = currentMethod.ReturnType;
        if (returnType != typeof(void))
            throw new ArgumentException("Methods called " + 
                      "by this command must return void");

        if (currentMethod.IsGenericMethodDefinition)
            currentMethod = currentMethod.MakeGenericMethod(typeParameters);

        currentMethod.Invoke(this, parameters);
    }

    #endregion

    #region Properties

    private string _displayName;
    public virtual string DisplayName
    {
        get { return _displayName; }
        set { _displayName = value; }
    }

    #endregion

    #region INotifyPropertyChanged Members

    private PropertyChangedEventHandler _handler;
    public event PropertyChangedEventHandler PropertyChanged
    {
        add { _handler += value; }
        remove { _handler -= value; }
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (_handler != null)
        {
            var e = new PropertyChangedEventArgs(propertyName);
            _handler(this, e);
        }
    }

    #endregion

    #region IDisposable Members

    public void Dispose()
    {
        OnDispose();
    }

    protected virtual void OnDispose() { }

    #endregion
}

As you can see, the Service Locator pattern will be used from the core of the Presentation Model framework, with the helper method 'Get<T>()'. Basically, using this pattern means that whenever you need an object for some task, you must ask another object (the Service Locator) for it, as opposed to directly instantiating them. This may sound useless if you have never used Inversion of Control, but as I will show in this article, using the Service Locator pattern allows the programmer to do really interesting things, like injecting some behavior on your code without directly modifying it. For this application, I'm going to use Castle Windsor, an IoC container that implements IServiceProvider, the basic interface of a service locator in the .NET framework. As shown in the static constructor, when the PresentationModel type is initialized, it will look for a service locator stored in the AppDomain's servicelocator property, but if doesn't find anything, it will use a default service locator, so it won't depend on additional code to work. The service locator it will use is an implementation of IServiceContainer (which is also a IServiceProvider) that comes with the .NET Framework. This implementation can be hooked with another IServiceProvider that will be used in case it doesn't contain a requested service. So I created a special implementation of IServiceProvider that has the following properties:

  • It contains some pre-registered components.
  • If it is asked for a component and it is not pre-registered, it will try to instantiate it using Reflection (if it is a concrete class).

The ServiceCreator was hooked with the ServiceContainer so this framework can work without any pre-configuration. In this sample, I am not going to use this custom service locator. I just wanted to show how you can build a framework that uses the Service Locator pattern and still make it work without configuration. The first type of concrete PresentationModel I'm going to introduce is the ICustomActionPresentationModel; the equivalent for this in Josh Smith's MVVM sample is CommandViewModel. This represents some UI element that can execute a custom action when interacted with. Here is the code:

C#
public interface ICustomActionPresentationModel : IPresentationModel
{
    IPresentationModel Owner { get; set; }

    string ActionName { get; set; }

    object[] Parameters { get; set; }

    Type[] TypeParameters { get; set; }

    void ExecuteAction();
}

public class CustomActionPresentationModel : 
       PresentationModel, ICustomActionPresentationModel
{
   //implementation
}

The Owner property represents the View that owns the UI element represented by this PresentationModel. The ActionName property is the method that will be invoked in the Owner. The rest is self explanatory. I won't show the implementation here because there is nothing special about it.

As I said in the introduction, I will use dialog boxes in the PresentationModels, but for that, the dialog concept must be brought to the framework:

C#
public interface IDialogSystem
{
    QuestionResult AskQuestion(string question);

    QuestionResult AskQuestion(string question, string title);

    FileInfo ChooseFile(string title, string filter);

    FileInfo ChooseImage();

    FileInfo ChooseImage(string title);
}

public enum QuestionResult
{
    Yes,
    No,
    Ok,
    Cancel
}

There is no implementation for it now because the dialog system depends on which UI engine is being used. Pay attention to the ChooseFile method. As you will see later, I use it inside the Presentation Model with the same filter format that the WPF's OpenFileDialog uses. I have done that to make it simple, but to make it more robust, a translation mechanism should be added, just like the QuestionResult enumeration that will translate message box results to the PresentationModels.

We also need a PresentationModel that can be closed, so here it goes:

C#
public interface IClosableViewPresentationModel : IPresentationModel
{
    void Close();

    event EventHandler RequestClose;
}

public abstract class ClosableViewPresentationModel : 
       PresentationModel, IClosableViewPresentationModel
{
    public ClosableViewPresentationModel()
    {
        _dialogServicesProvider = Get<IDialogSystem>();
    }

    #region Fields

    IDialogSystem _dialogServicesProvider;

    #endregion

    #region Methods

    public void Close()
    {
        OnRequestClose();
    }

    #endregion

    #region Events

    private EventHandler _handler;
    public event EventHandler RequestClose
    {
        add { _handler += value; }
        remove { _handler -= value; }
    }

    protected virtual void OnRequestClose()
    {
        if (_handler != null)
            _handler(this, EventArgs.Empty);
        Dispose();
    }

    #endregion

    protected QuestionResult AskQuestion(string question)
    {
        return _dialogServicesProvider.AskQuestion(question);
    }

    protected QuestionResult AskQuestion(string question, string title)
    {
        return _dialogServicesProvider.AskQuestion(question, title);
    }

    protected FileInfo ChooseFile(string title)
    {
        return _dialogServicesProvider.ChooseFile(title, null);
    }

    protected FileInfo ChooseImage()
    {
        return _dialogServicesProvider.ChooseImage();
    }

    protected FileInfo ChooseImage(string title)
    {
        return _dialogServicesProvider.ChooseImage(title);
    }
}

I have added protected methods for dialog services and made the dialog system private, so this will be the base class for the PresentationModels in the application core. That's all with the PresentationModel framework project.

The Core Application Project

This project will contain the main application workflow, the model, and the definition for the services it will use. Before showing code, the requirements must be defined. It will be a simple application that manages information about products in a store. The user must be able store the product's name, price, and photo. The user must also be able to edit, remove, and view the stored products. Pretty original, right? So, let's start with the only class in the model:

C#
public class Product
{       
    private string _name;
    public virtual string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    private decimal _price;
    public virtual decimal Price
    {
        get { return _price; }
        set { _price = value; }
    }

    private byte[] _photography;
    public virtual byte[] Photography
    {
        get { return _photography; }
        set { _photography = value; }
    }
}

Since data storage is a requirement, we need a data access interface. I could add a specific interface that would handle CRUD operations for products, but maybe the requirements can grow later, so I will use a generic definition. This will be the only data service defined by the application:

C#
public interface IDao<T>
{
    void SaveOrUpdate(T item);

    void Remove(T item);

    T GetByKey(int key);

    IList<T> SelectAll();

    event ListChangedEventHandler Changed;
}

OK, now let's see what kind of views the application needs:

  • A main view that will be showed when the application starts. Its basic function will be to navigate to other views that will perform specific tasks.
  • A view that will show the details about a product. This view can be used to insert a new product or to edit an existing one.
  • A view that will show a list of all the products stored by the application. This view can be used to select a product for editing or removal.

If the application's model ever grows, it will end up needing more views whose functions are the same as the last two views' behavior, so this is a good time to create a more generic definition for the last two views:

  • A view that will show the details about a model's entity instance. This view can be used to insert a new instance of an entity or to edit an existing one.
  • A view that will show a list of all the instances of a certain entity stored by the application. This view can be used to select an entity instance for editing or removal.

Since the views get their behavior from the PresentationModel, I should start their definition with the following:

C#
public interface ISelectableViewPresentationModel : IClosableViewPresentationModel
{
    bool IsSelected { get; set; }
}

public interface IMainViewPresentationModel : IClosableViewPresentationModel
{
    ICollection<ICustomActionPresentationModel> NavigationItems { get; }

    ICollection<ISelectableViewPresentationModel> Workspaces { get; }

    void CreateNew<T>() where T : new();

    void EditExisting<T>(T item) where T : new();

    void ViewAll<T>() where T : new();
}

public interface IEntityViewPresentationModel<T> : ISelectableViewPresentationModel
    where T : new()
{
    T Entity { get; set; }

    void SaveOrUpdate();

    void OpenEditView();
}

public interface IEntityCollectionViewPresentationModel<T> : ISelectableViewPresentationModel 
        where T : new()
{
    ICollection<IEntityViewPresentationModel<T>> EntityCollection { get; }

    IEntityViewPresentationModel<T> Selected { get; set; }

    void CreateNew();

    void RemoveSelected();

    bool CanRemove { get; }
}

The IEntityViewPresentationModel<T> will show information about an entity, which can be the details in a dedicated view, or just a summary in a datagrid contained in a IEntityCollectionViewPresentationModel<T>. If you think about it, the only difference between the IEntityViewPresentationModel<T> of different entities are the properties that will represent the fields on the screen. That kind of code can get very repetitive, but fortunately, the .NET Framework provides us with a way to generalize this by the Type Descriptor metadata inspection mechanism. Unlike Reflection which reads the compiled type information, the Type Description class uses Reflection to get its initial data and then allows this data to be modified at runtime. Of course, the type is not really being modified, but Type Description allows its initial data to be modified so others that query type information to it will see the changes. Fortunately, the .NET UI Framework (not sure about gtk# in Mono; from what I've heard, it only has bindings to gtk+) uses this mechanism to inspect types, and so we can use it to have some fun :)

The base class for modifying a type using Type Description is to subclass CustomTypeDescriptor and override its query methods. To make a type using a certain TypeDescriptor, you must add a TypeDescriptorProvider to a class using the following code:

C#
TypeDescriptor.AddProvider(someTypeDescriptor, typeof(someOtherType));

I wont discuss all that you can do with the TypeDescriptor, just what I am going to do with it. The PresentationModel classes that inherit from EntityViewPresentationModel<T> will have different properties (whose only function is to encapsulate the entity's properties) based on the entities properties and what information you want to show on that view. For example, we will have a ProductEditViewPresentationModel for editing products. This view will have fields for name and price, along with a picture box that shows the product photo (and the ability to change it). Instead of implementing these properties explicitly, we are going to use this:

C#
[AttributeUsage(AttributeTargets.Class, AllowMultiple=true)]
class EncapsulatesPropertyAttribute : Attribute
{
    public EncapsulatesPropertyAttribute(string propertyName) 
    {
        _propertyName = propertyName;
    }

    private string _propertyName;
    public string PropertyName
    {
        get { return _propertyName; }
        set { _propertyName = value; }
    }
}

This attribute specifies a single property that should be encapsulated in a EntityViewPresentationModel. So here is the definition for the PresentationModels:

C#
[EncapsulatesProperty("Name")]
[EncapsulatesProperty("Price")]
[EncapsulatesProperty("Photography")]
public class ProductEditViewPresentationModel : 
       EntityViewPresentationModel<Product>
{
}
public class ProductsViewPresentationModel 
        : EntityCollectionViewPresentationModel<Product>
{
}

All that the custom type descriptor has to do is read the EncapsulatesPropertyAttribute and create properties that will encapsulate the properties specified in the attribute. These properties must also raise the PropertyChanged event on the PresentationModel when required. I won't show the implementation details here for the article's size sake. With this techinque, I can easily create other views of the same entity or for other new entities introduced in the model.

Before moving to the next project, let me show the MainViewPresentationModel, so you can better understand what has been happening so far:

C#
public class MainViewPresentationModel : ClosableViewPresentationModel, 
                                         IMainViewPresentationModel
{
    public MainViewPresentationModel()
    {
        _navigationItems = Get<ICollection<ICustomActionPresentationModel>>();
        _workspaces = Get<ICollection<ISelectableViewPresentationModel>>();

        CreateNavigationItems();
    }

    private void CreateNavigationItems()
    {
        ICustomActionPresentationModel openNewProductView = 
                   Get<ICustomActionPresentationModel>();
        openNewProductView.Owner = this;
        openNewProductView.ActionName = "CreateNew";
        openNewProductView.TypeParameters = new Type[] { typeof(Product) };
        openNewProductView.DisplayName = "Create new product";

        ICustomActionPresentationModel openProductsView = 
                   Get<ICustomActionPresentationModel>();
        openProductsView.Owner = this;
        openProductsView.ActionName = "ViewAll";
        openProductsView.TypeParameters = new Type[] { typeof(Product) };
        openProductsView.DisplayName = "View all products";

        _navigationItems.Add(openNewProductView);
        _navigationItems.Add(openProductsView);
    }

    private ICollection<ICustomActionPresentationModel> _navigationItems;
    public ICollection<ICustomActionPresentationModel> NavigationItems
    {
        get { return _navigationItems; }
    }

    private ICollection<ISelectableViewPresentationModel> _workspaces;
    public ICollection<ISelectableViewPresentationModel> Workspaces
    {
        get { return _workspaces; }
    }

    public void CreateNew<T>()
        where T : new()
    {
        IEntityViewPresentationModel<T> workspace = 
                   Get<IEntityViewPresentationModel<T>>();
        workspace.Entity = new T();
        AddWorkspace(workspace);
    }

    public void EditExisting<T>(T item)
        where T : new()
    {
        IEntityViewPresentationModel<T> workspace = 
                   Get<IEntityViewPresentationModel<T>>();
        workspace.Entity = item;
        AddWorkspace(workspace);
    }

    public void ViewAll<T>()
        where T : new()
    {
        IEntityCollectionViewPresentationModel<T> workspace = 
                   Get<IEntityCollectionViewPresentationModel<T>>();
        AddWorkspace(workspace);
    }

    void AddWorkspace(ISelectableViewPresentationModel workspace)
    {
        if (!_workspaces.Contains(workspace))
        {
            workspace.RequestClose +=
                   (sender, e) => OnWorkspaceRequestClose(sender);
            Workspaces.Add(workspace);
        }
        SetActiveWorkspace(workspace);
    }

    void OnWorkspaceRequestClose(object sender)
    {
        ISelectableViewPresentationModel workspace = 
               sender as SelectableViewPresentationModel;
        Workspaces.Remove(workspace);
    }

    protected virtual void SetActiveWorkspace(
              ISelectableViewPresentationModel workspace)
    {
        workspace.IsSelected = true;
    }
}

As you can see, the only thing I instantiate directly are the entities. All dependencies are obtained from the service locator. Even the collection used is not specified directly.

I wont explain the Data project because it only has one class, DummyDao<T>, a dummy implementation of IDao<T> that stores all data in memory.

Wiring it all up - The Configuration Project

This project will define code that is not related to the application's main logic, and will connect all components. The first thing I'm going to show is how the WPF views will talk to the presentation models. Not unlike MVVM, it will be through commands, more specifically, only one command:

C#
class MethodCallCommand : ICommand
{
    public MethodCallCommand(PresentationModel target)
    {
        _target = target;
    }

    PresentationModel _target;

    #region ICommand Members

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        _target.InvokeMethod(parameter.ToString(), null);
    }

    protected virtual void Execute(MethodInfo mi)
    {
        mi.Invoke(_target, null);
    }

    #endregion
}

As you can probably guess by now, we are going to use the TypeDescriptor API to 'inject' a property of this type on the PresentationModel base class. This will allow us to call methods on any PresentationModel like this:

XML
<Button     
    Content="Save"
    Command="{Binding Call}"
    CommandParameter="SaveOrUpdate"
    />

Pretty simple, right? And no code-behinds. Another way to use commands in the Presentation Models is to declare an interface with a similar signature to that of the ICommand interface. Then, the PresentationModel base class could get a reference to a MethodCallCommand without creating dependencies to WPF (or, you could use either of the two techniques to add many commands to the concrete PresentationModels, one for each method, like how it is normally done in MVVM).

The next thing we need is to implement the IDialogSystem interface:

C#
class WpfDialogSystem : IDialogSystem
{
    #region IDialogServicesProvider Members

    public QuestionResult AskQuestion(string question)
    {
        return AskQuestion(question, "Question");
    }

    public QuestionResult AskQuestion(string question, string title)
    {
        var result = System.Windows.MessageBox.Show(question, title, 
            System.Windows.MessageBoxButton.YesNo, 
            System.Windows.MessageBoxImage.Question);
        switch (result)
        {
            case System.Windows.MessageBoxResult.Yes:
                return QuestionResult.Yes;
            default:
                return QuestionResult.No;
        }
    }

    public System.IO.FileInfo ChooseFile(string title, string filter)
    {
        OpenFileDialog dialog = new OpenFileDialog();
        dialog.Title = title;
        dialog.Filter = filter;
        dialog.ShowDialog();
        if (dialog.FileName != null && dialog.FileName.Trim() != string.Empty)
            return new System.IO.FileInfo(dialog.FileName);
        return null;
    }

    public System.IO.FileInfo ChooseImage()
    {
        return ChooseImage("");
    }

    public System.IO.FileInfo ChooseImage(string title)
    {
        return ChooseFile(title, "Image Files (*.bmp, *.jpg, *.jpeg, " + 
                          "*.png)|*.bmp;*.jpg;*.jpeg;*.png");
    }

    #endregion
}

Now, let's see how this all fits together:

C#
public class ConfigurationManager
{
    internal static WindsorContainer _windsor = new WindsorContainer();

    public static void Configure()
    {
        WpfConfiguration();
    }

    static void WpfConfiguration()
    {
        GenericConfiguration();

        _windsor.Register
            (
            Component.For<IDialogSystem>().ImplementedBy<
              WpfDialogSystem>().LifeStyle.Is(LifestyleType.Singleton),
            Component.For(typeof(ObservableCollection<>), typeof(
              ICollection<>)).LifeStyle.Is(LifestyleType.Transient)
            );
        
        //Inserting the command properties on the presentation models.
        System.ComponentModel.TypeDescriptor.AddProvider(
            new PresentationModelTypeDescriptionProvider(
                System.ComponentModel.TypeDescriptor.GetProvider(
                typeof(PresentationModel))),
                typeof(PresentationModel));
    }

    static void GenericConfiguration()
    {
        AppDomain.CurrentDomain.SetData("servicelocator", _windsor);
        var propertyDIContributor = 
          _windsor.Kernel.ComponentModelBuilder.Contributors.OfType<
          PropertiesDependenciesModelInspector>().Single() ;
        _windsor.Kernel.
            ComponentModelBuilder.
            RemoveContributor(propertyDIContributor);
        _windsor.Register
            (
            Component.For<MainViewPresentationModel, 
              IMainViewPresentationModel>().LifeStyle.Is(LifestyleType.Singleton),
            Component.For<ProductsViewPresentationModel, 
              IEntityCollectionViewPresentationModel<
                Product>>().LifeStyle.Is(LifestyleType.Singleton),
            Component.For(typeof(DummyDao<>), 
                typeof(IDao<>)).LifeStyle.Is(LifestyleType.Singleton),
            Component.For(typeof(List<>), 
                typeof(IList<>)).LifeStyle.Is(LifestyleType.Transient),
            Component.For<CustomActionPresentationModel, 
                ICustomActionPresentationModel>().LifeStyle.Is(LifestyleType.Transient),
            Component.For<ProductEditViewPresentationModel, 
              IEntityViewPresentationModel<Product>>().LifeStyle.Is(LifestyleType.Transient)
            );
    }
}

Castle Windsor allows you register service/component interfaces and implementations with many possible LifeStyles. As you can see here, I only used two kinds of LifeStyles: Singleton and Transient. If you register a Singleton, the container will create only one instance of it, so every time you request that service, the same instance will be returned. On the contrary, Transient means a new instance will be created every time the service is requested. Although I'm using Windsor for the Service Locator pattern, it will also automatically perform constructor and setter dependency injection, and that's why I used this code:

C#
var propertyDIContributor = 
  _windsor.Kernel.ComponentModelBuilder.Contributors.OfType<
    PropertiesDependenciesModelInspector>().Single() ;
_windsor.Kernel.ComponentModelBuilder.RemoveContributor(propertyDIContributor);

This contributor I removed is responsible for injecting dependencies on properties. Since I'm only using parameterless constructors, I don't have to worry about automatic constructor injection. The last thing to note is:

C#
AppDomain.CurrentDomain.SetData("servicelocator", _windsor);

Since the configuration will be the first thing done, the presentation models will get everything they need with the Get<T> method. It is important to note that the only place I reference Castle libraries is in the configuration project because the PresentationModel class is talking to a IServiceProvider, so if I replace it with another implementation that provides the correct services, the application won't notice.

I won't show the XAML in the WPF project, so here is the only code present in it:

C#
protected override void OnStartup(StartupEventArgs e)
{
    ConfigurationManager.Configure();
    IServiceProvider prov = (IServiceProvider)
      AppDomain.CurrentDomain.GetData("servicelocator");
    base.OnStartup(e);
    MainWindow mainWindow = new MainWindow();
    IMainViewPresentationModel mainViewPresentationModel = 
      (IMainViewPresentationModel)prov.GetService(typeof(IMainViewPresentationModel));
    mainViewPresentationModel.RequestClose += (a, b) => mainWindow.Close();

    mainWindow.DataContext = mainViewPresentationModel;
    mainWindow.Show();
}

Working with Multiple Threads

Although the application is running fine right now, one thing is missing: UI responsiveness when running long operations. This demo doesn't have any long running samples, so I added this when loading the list of products:

C#
Thread.Sleep(10000);

These ten seconds can make the user think the application stopped responding, and delays like this are common in real world applications where the volume of data is great. To handle this, I am going to make the application code run in separate threads from the UI thread without changing the main logic. This technique is called Aspect Oriented Programming, and can be extremely useful to handle any non-functional requirement like logging and database transaction management.

We are adding this multi-threading aspect to the code by intercepting method calls with dynamic proxies for the presentation models (subclasses generated at runtime). While this may sound complicated, Castle Windsor will do almost everything. That's one great advantage of not directly instantiating classes: when we ask for a component, instead of returning our implementation, it will give us a dynamic subclass with custom behavior! Before explaining how to implement this, let's analyze a basic conversation between a Presentation Model and a View.

  1. The Presentation Model is created and hooked with the View.
  2. The View registers handlers to the Presentation Model's notifications.
  3. The user does something in the View.
  4. The View will propagate theses changes to the Presentation Model.
  5. The Presentation Model does something that changes its state.
  6. The registered notifications handlers are called.

We must intercept this workflow twice:

  • We must redirect step 5 to a separate background thread so the UI will not look unresponsive if it takes some time to complete.
  • When the background thread is about to call the notification handlers, we must redirect it to the UI thread. This is a requirement because normally UI controls can only be modified by the thread that created them.

We can accomplish the first step with this interceptor:

C#
class BackgroundWorkInterceptor : IInterceptor
{
    #region IInterceptor Members

    public void Intercept(IInvocation invocation)
    {
        if (!CheckIfShouldIntercept(invocation))
        { invocation.Proceed(); return; }

        PresentationModel pm = (PresentationModel)invocation.InvocationTarget;

        ThreadPool.QueueUserWorkItem(
            o =>
            {
                invocation.Proceed();
            });
    }

    #endregion
    
    bool CheckIfShouldIntercept(IInvocation invocation)
    {
        if (invocation.Method.Name == "InvokeMethod")
            return true;
        return false;
    }
}

As I told when explaining the IPresentationModel interface, we are going to use the InvokeMethod method as a gateway between the UI and the Presentation Model. This background thread redirection could have been done in the command class since that's how WPF calls methods in the Presentation Model, but then I could not use this technique with another GUI Framework. The InvokeMethod provides us with a more reusable gateway so this interceptor could be used with non-WPF UIs. The next interceptor will take a method and send it to the dispatcher, so it is very WPF specific:

C#
class DispatchInterceptor : IInterceptor
{
    Dispatcher _dispatcher = Dispatcher.CurrentDispatcher;
    #region IInterceptor Members

    public void Intercept(IInvocation invocation)
    {
        if (!CheckIfShouldIntercept(invocation))
        { invocation.Proceed(); return; }

        if (Thread.CurrentThread == _dispatcher.Thread)
            invocation.Proceed();
        else
        {
            ThreadStart ts = () => invocation.Proceed();
            _dispatcher.Invoke(ts, null);
        }
    }

    #endregion

    bool CheckIfShouldIntercept(IInvocation invocation) 
    {
        if (invocation.Method.Name == "OnPropertyChanged")
            return true;
        if (invocation.Method.Name == "OnCollectionChanged")
            return true;
        return false;
    }      
}

This time I check if the invocation is not already coming from the UI thread before dispatching. With the background interceptor, I didn't need to do it because only the UI thread would call InvokeMethood. Notice that the interceptor will intercept OnCollectionChanged. This is because we will add this interceptor to ObservableCollections, and it's not hard to figure why: when an ItemsControl (or similar) is bound to an ObservableCollection, the UI will connect handlers to the CollectionChanged event, so these handlers must also be called in the UI thread.

Another requirement is to add a boolean property (with the type descriptor) to the Presentation Model base class. This property will be called 'IsWorking' and will return true every time a Presentation Model is doing background work. Here is the modified background interceptor:

C#
class BackgroundWorkInterceptor : IInterceptor
{
    #region IInterceptor Members

    public void Intercept(IInvocation invocation)
    {
        if (!CheckIfShouldIntercept(invocation))
        { invocation.Proceed(); return; }

        PresentationModel pm = (PresentationModel)invocation.InvocationTarget;

        ThreadPool.QueueUserWorkItem(
            o =>
            {
                SetIsWorkingProperty(pm, true);
                invocation.Proceed();
                SetIsWorkingProperty(pm, false);
            });
    }

    #endregion

    void SetIsWorkingProperty(PresentationModel pm, bool value)
    {
        var property =
            TypeDescriptor.GetProperties(typeof(PresentationModel)).Find("IsWorking", false);
        property.SetValue(pm, value);
    }

    bool CheckIfShouldIntercept(IInvocation invocation)
    {
        if (invocation.Method.Name == "InvokeMethod")
            return true;
        return false;
    }
}

The only thing left is to configure these components and add some special effect to the user controls when their respective Presentation Models are working. Here is the updated configuration class:

C#
public class ConfigurationManager
{
    internal static WindsorContainer _windsor = new WindsorContainer();

    public static void Configure()
    {
        WpfConfiguration();
    }

    static void WpfConfiguration()
    {
        _windsor.Kernel.ComponentModelCreated += ComponentModelCreated;
        GenericConfiguration();

        _windsor.Register
            (
            Component.For<DispatchInterceptor>(),
            Component.For<IDialogSystem>().ImplementedBy<
                      WpfDialogSystem>().LifeStyle.Is(LifestyleType.Singleton),
            Component.For(typeof(ObservableCollection<>), 
                      typeof(ICollection<>)).LifeStyle.Is(LifestyleType.Transient)
            );
        
        //Inserting the command properties on the presentation models.
        System.ComponentModel.TypeDescriptor.AddProvider(
            new PresentationModelTypeDescriptionProvider(
                System.ComponentModel.TypeDescriptor.GetProvider(
                typeof(PresentationModel))),
                typeof(PresentationModel));
    }

    static void GenericConfiguration()
    {
        AppDomain.CurrentDomain.SetData("servicelocator", _windsor);
        var propertyDIContributor = 
          _windsor.Kernel.ComponentModelBuilder.Contributors.OfType<
          PropertiesDependenciesModelInspector>().Single() ;
        _windsor.Kernel.
            ComponentModelBuilder.
            RemoveContributor(propertyDIContributor);
        _windsor.Kernel.ComponentModelCreated += ComponentModelCreated;
        _windsor.Register
            (
            Component.For<BackgroundWorkInterceptor>(),
            Component.For<MainViewPresentationModel, 
              IMainViewPresentationModel>().LifeStyle.Is(LifestyleType.Singleton),
            Component.For<ProductsViewPresentationModel, 
              IEntityCollectionViewPresentationModel<
                Product>>().LifeStyle.Is(LifestyleType.Singleton),
            Component.For(typeof(DummyDao<>), 
                typeof(IDao<>)).LifeStyle.Is(LifestyleType.Singleton),
            Component.For(typeof(List<>), 
                typeof(IList<>)).LifeStyle.Is(LifestyleType.Transient),
            Component.For<CustomActionPresentationModel, 
              ICustomActionPresentationModel>().LifeStyle.Is(LifestyleType.Transient),
            Component.For<ProductEditViewPresentationModel, 
              IEntityViewPresentationModel<
                Product>>().LifeStyle.Is(LifestyleType.Transient)
            );
    }

    static void ComponentModelCreated(ComponentModel model)
    {
        if (typeof(PresentationModel).IsAssignableFrom(model.Implementation))
        {
            model.Interceptors.Add(
               InterceptorReference.ForType<BackgroundWorkInterceptor>());
        }
        if (typeof(PresentationModel).IsAssignableFrom(model.Implementation) ||
            typeof(ObservableCollection<>) == model.Implementation)
        {
            model.Interceptors.Add(InterceptorReference.ForType<DispatchInterceptor>());
        }
    }
}

That's it! We just made the application work with multiple threads without interfering with its main logic. I hope you enjoyed this article; feel free to leave comments and suggestions.

Points of Interest

  • Pay attention that each Presentation Model has its own background thread, so you can still mess around with the other Views when one is doing processing.
  • Keep in mind that this sample is not very well tested. I have not handled input validations or method parameters, so it can break without much effort. The main point of this article is to show a nice way to use the Presentation Model UI Design Pattern for business applications.
  • I have used the Attached Command Behavior to handle the ListView's double click event.
  • Castle Windsor/Microkernel is a fantastic IoC container, and I have not shown 10% of what you can do with it. It is extensible, and integrates very well with other technologies like NHibernate.
  • The theme I used on the sample is part of the WPF Themes package.

License

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


Written By
Brazil Brazil
Software developer specialized in the .NET framework

Comments and Discussions

 
GeneralRe: Not a bad effort, I use similar idea in my MVVM Framework Cinch Pin
Thiago de Arruda5-May-10 0:03
Thiago de Arruda5-May-10 0:03 
GeneralRe: Not a bad effort, I use similar idea in my MVVM Framework Cinch Pin
Sacha Barber5-May-10 1:18
Sacha Barber5-May-10 1:18 
GeneralRe: Not a bad effort, I use similar idea in my MVVM Framework Cinch Pin
Pete O'Hanlon5-May-10 3:27
subeditorPete O'Hanlon5-May-10 3:27 
GeneralRe: Not a bad effort, I use similar idea in my MVVM Framework Cinch Pin
Yury Goltsman10-May-10 7:34
Yury Goltsman10-May-10 7:34 
GeneralRe: Not a bad effort, I use similar idea in my MVVM Framework Cinch Pin
Sacha Barber10-May-10 7:44
Sacha Barber10-May-10 7:44 
GeneralRe: Not a bad effort, I use similar idea in my MVVM Framework Cinch Pin
Yury Goltsman10-May-10 8:26
Yury Goltsman10-May-10 8:26 
GeneralRe: Not a bad effort, I use similar idea in my MVVM Framework Cinch Pin
Sacha Barber10-May-10 9:10
Sacha Barber10-May-10 9:10 
GeneralRe: Not a bad effort, I use similar idea in my MVVM Framework Cinch Pin
Yury Goltsman10-May-10 9:51
Yury Goltsman10-May-10 9:51 

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

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