Total votes: 6
Print: Print Article
Please login to rate or to leave a comment.
Published: 29 Jun 2007
Download Sample Code
This article demonstrates some best practices for developing WPF applications. A complete “enterprise” ready architecture is recommended covering everything from data access to presentation. Of particular interest is an MVP UI framework inspired by “Rails” which is included with the downloadable solution.
The internet, with its vast array of information, along with the constant stream of media being produced
publishers and similar organizations, offers the contemporary software developer an overwhelming set of
resources to choose
from. When a new technology arrives, the market is quickly flooded with media surrounding the essential
"How To's" of that
new thing. However, it often takes very long for best practices to emerge if they do at all. As a result
an interested and
motivated developer has to constantly search, often unable to find any real world examples. Though the
sample discussed in this
article is very simple, it is designed to show a set of best practices that can be used in a full
enterprise scenario. These best
practices are not specific to
WPF and did in fact not originate in this area. I have
attempted to apply what I have learned in
other types of application development to a WPF scenario and have included all the necessary framework
pieces to help a developer be
Figure 1: Solution
Functionality of the Sample
The sample application is about as simple in nature as one can get and still include all of the pieces
needed to demonstrate
an enterprise example. Its purpose is to allow a user to track books that they have in their home
library. The user
interface allows the addition of new items to the library and a basic search feature.
When viewing items in your personal library, Amazon.com is queried for additional information about the
book (only a
picture at present). The user can also track whether or not they have loaned the book out to an
HomeLibrary solution consists of three assemblies:
Each of these assemblies represents a layer or an important aspect of a layer of the application.
HomeLibrary.Core is what
would typically be thought of as the middle layer of the application. It contains the
Model, the fundamental representation
of our "problem" in code; in this case:
LoanInfo. The model
is built entirely of
POCOs (Plain Old CLR Objects),
having no knowledge of data access code. The POCO nature of these classes is important because it allows
us to have a clean
Separation Of Concerns. Having the domain model loosely coupled to data access allows us to
focus on individual aspects of
our application without muddying the waters and facilitates the creation of clean, maintainable code. The
best example of this
loosely coupled nature can be seen in the
ILibraryItemRepository interface. This is an
abstraction of the
store and is used by the application to work against the database rather than any concrete
The UI/Application layer is represented by the
HomeLibrary.Client assembly. This layer is
organized into two main sets of objects:
Views. The presenters hold the application logic and interact
with the domain model and the abstract
have a series of methods and properties which are bound at runtime to a set of views. The views are
represented primarily by
Controls and are organized in namespaces that relate to the presenters they work for.
At runtime the repository interfaces are instantiated with concrete classes from the
HomeLibrary.Data assembly. This assembly forms the bridge
between the domain model/repositories and the data access layer. Keeping the concrete repositories in
their own assembly allows the model and application
layers to be "unconcerned" about these details by always interacting through an interface. This "loose
coupling" allows for greater testability and
an overall increase in maintainability.
Looking At The Infrastructure
There are several frameworks and tools included with the sample that help to bring all of the pieces
together in a manageable way. They are:
NHibernate, NHibernate Query Generator, Rhino.Commons, Castle.Windsor, log4net and MVPforWPF.
NHibernate is an
O/RM (Object Relational Mapper) that uses metadata to understand how the
classes in the domain model are represented within the
relational data store. Using this metadata (which can be found in the Mappings folder of
HomeLibrary.Data), NHibernate can save, retrieve and
delete domain objects from the underlying data store without the need
to write custom SQL. It does this intelligently and flexibly, allowing the developer access to low level
functionality if they need to optimize code.
NHibernate provides several different ways to query against objects. This sample application uses a tool
called NHibernate Query Generator to generate
strongly typed query objects for use with NHibernate. (The output of this tool can be found in
These query classes allow the code to have a very readable
LINQ type syntax. The utility
assembly Rhino.Commons sits on top
of NHibernate and manages many of the low level details as well as adding some nice additional
functionality. Mainly, it provides a concrete
repository implementation (
NHRepository<T>) that can be used for all data access. You
can see a good example of all of these pieces coming together
by examining the RepositoryImplementations folder in the HomeLibrary.Data assembly and by looking at the
Update method of the LibraryItemPresenter in
HomeLibrary.Client assembly. NHibernate's configuration is in its own file found at
Castle.Windsor is a Dependency Injection framework (also known as Inversion Of Control).
Let me explain. If we look at the constructor of the
class in the
HomeLibrary.Client we see that this class depends on an implementation of
AmazonSearchService. An instance of this
class cannot be constructed without these two pieces. Windsor is essentially a fancy object factory that
knows how to instantiate classes that
depend on others. It also knows the relationships between abstract classes/interfaces and their concrete
implementations. This framework enables the developer
to simply register types with it and later call on them to be instantiated; Windsor will determine all of
the dependencies and hand you a fully
configured object ready for work. You can view the configuration of Windsor by looking at the
Windsor.config file in the
bin/Debug/config folder of
The information in the file is used by Rhino.Commons to provide strongly typed repositories and by
MVPforWPF to instantiate presenters like the one mentioned above.
Many applications require some form of logging. log4net provides a very robust solution. It is used
internally by several of the frameworks listed above and has been
configured for use in the sample application. Take a look at the
app.config for a basic
logging configuration. The
app.xaml.cs demonstrates a simple usage of the API.
I have included full source for a
WPF Model View Presenter framework. The framework is
the beginnings of something that I have been experimenting with in my
spare time. It is highly influenced by the MonoRail web framework and attempts to intuitively add some
similar functionality to
WPF. If you spend any amount of
MVP patterns with
WPF, you will not find
very many resources. It is fairly widely agreed upon that these patterns or their variants should be
considered when developing a presentation tier.
There are several pieces of the framework that you should know about if you would like to use or extend
it. The two most important pieces are
ViewSites are locations in the UI where you wish a "view" to appear. Presenters contain the application
logic and should inherit from
PresenterBase. When you declare a
ViewSite, you specify a
Presenter, either by name (if it is configured for
Windsor) or by type if you are not using Windsor.
Listing 1: ViewSite Declaration
When the UI is instantiated the
obtain the presenter from the
. Any view displayed in the
will be bound to the
, which means
that which presenter is used can be databound.
Listing 2: ViewSite With Databinding
also has a View property which you can set to the name of a view or you can use the
attribute on your presenter
to declare a default view.
Listing 3: Presenter Attribute
Binding to a
Presenter allows you to use a special
AttachedProperty to bind Presenter
methods to UI elements.
Listing 4: Presenter Actions
When the above button is clicked, the "Search" method on the bound presenter will be called and if that method
has a parameter (which it does), whatever the
will be passed in. In the case that the control is an
ItemsControl, then the selected item will be
passed in. Additionally, you can place an
Action attribute or an
attribute on any presenter method. Using this you can specify custom code that automatically disables parts of
the UI, run code asynchronously and specify callbacks. Here is an example of
what that would look like:
Listing 5: Action Attribute
There is some nice functionality provided by the
attributes that is worth looking into. Additionally,
method on it
that allows for changing the view and
will use a built in transition mechanism to switch
from view to view. (You can also add your own transitions.)
Listing 5: Render View
Note: Views should be arranged hierarchically by Presenter within the solution. A
default namespace should be configured with the framework.
Spend some time studying the code and I am sure you will find some useful tools to simplify
development. Hopefully this will give you some ideas to chew on and
motivate the creation of a more maintainable presentation layer for your apps.
There are several possible changes that could be made to the sample that would be a great study in
application development. One of the first things I might do
would be to convert this to either a distributed or a service oriented architecture. This is a common
scenario. To make these changes, one would remove dependencies
on repositories (
HomeLibrary.Data) and Rhino.Commons. If you are going the distributed
route, you can simply move the repositories and their associated code into the service and then
inject these services into the presenters using Castle.Windsor. If you are taking the
route, you may need to move more code from your presenters into your services
and additionally, remove the reference to
HomeLibrary.Core. In a
SOA you don't
want to talk in terms of domain objects, but rather in terms of messages. The domain objects
should be hidden below the service later.
One big set of improvements surround the MVP framework. The code provided with the sample is only the
beginning of what would be needed on a typical project. There
are many improvements to the existing functionality that could be made as well as plenty of other useful
features that could be added. A few examples are:
Remove ViewSite's hard coded uses of the
CrossFade transition and add a transition
property to allow different transitions to be selected.
Improve databinding to action parameters. Automatically bind the values of controls to the input
parameters of actions based on name.
Allow the return value of actions to be bound back to the UI.
Add a system for loosely coupled events.
Abstract a set of interfaces that cover common UI related tasks, such as
MessageBox functionality. Concrete implementations should be automatically injected at
I will be continually
improving this framework over the next several months and may even begin an official open source project.
If you are interested, please watch my blog for details
in the future.
The final piece that is missing from the sample are
Unit Tests. In any real world project I
would employ a test first methodology. The architecutre that I have
recommended here should make it easier to test various aspects of the application. I have included the
excellent NSpecify framework with the download. This is my
favorite tool for doing
BDD at present.
I hope that you will take time to look closely at the sample code. Though its purpose is simple in
nature, it shows all the necessary pieces needed to
build an enterprise
WPF application. It demonstrates such useful strategies as application
layering, loose coupling, dependency injection, domain modeling and MVP.
If anything, I hope that you can use the sample as a startup project for future
Note: Before running the solution you must create the database, set the connection
string (NHibernate.config) and declare your amazon.com key (LibraryItemPresenter.cs).
Sorry, no bio is available
This author has published 2 articles on DotNetSlackers. View other articles or the complete profile here.
You might also be interested in the following related blog posts
$style - Get Any CSS Property Value of an Object
Introducing Fluent Control Container
You gotta love SeeWindowsVista
Please login to rate or to leave a comment.