Contents
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).
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.
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.
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:
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:
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:
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
{
}
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:
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:
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.
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:
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:
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:
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:
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:
[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:
[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:
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.
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:
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:
<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:
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:
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)
);
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 LifeStyle
s. As you can see here, I only used two kinds of LifeStyle
s: 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:
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:
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:
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();
}
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:
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.
- The Presentation Model is created and hooked with the View.
- The View registers handlers to the Presentation Model's notifications.
- The user does something in the View.
- The View will propagate theses changes to the Presentation Model.
- The Presentation Model does something that changes its state.
- 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:
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:
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 ObservableCollection
s, 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:
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:
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)
);
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.
- 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.