Published: 29 Jun 2007
By: Rob Eisenberg
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.

Introduction

The internet, with its vast array of information, along with the constant stream of media being produced by 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 successful with WPF.

Figure 1: Solution


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 acquaintance.

Architectural Overview

The HomeLibrary solution consists of three assemblies: HomeLibrary.Client, HomeLibrary.Core and HomeLibrary.Data. Each of these assemblies represents a layer or an important aspect of a layer of the application.

The HomeLibrary.Core is what would typically be thought of as the middle layer of the application. It contains the Domain Model, the fundamental representation of our "problem" in code; in this case: LibraryItems and 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 LibraryItem data store and is used by the application to work against the database rather than any concrete implementation.

The UI/Application layer is represented by the HomeLibrary.Client assembly. This layer is organized into two main sets of objects: Presenters and Views. The presenters hold the application logic and interact with the domain model and the abstract repositories. They have a series of methods and properties which are bound at runtime to a set of views. The views are represented primarily by WPF User 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 HomeLibrary.Data.Querying.) 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 the HomeLibrary.Client assembly. NHibernate's configuration is in its own file found at bin/ebug/config.

Castle.Windsor is a Dependency Injection framework (also known as Inversion Of Control). Let me explain. If we look at the constructor of the ShellPresenter class in the HomeLibrary.Client we see that this class depends on an implementation of ILibraryItemRepository and 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 HomeLibrary.Client. 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.

MVPforWPF

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 time researching MVC or 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 ViewSite and PresenterBase. 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 ViewSite will obtain the presenter from the IPresenterFactory. Any view displayed in the ViewSite will be bound to the Presenter. The Presenter is a WPF DependencyProperty, which means that which presenter is used can be databound.

Listing 2: ViewSite With Databinding

ViewSite also has a View property which you can set to the name of a view or you can use the Presenter 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 DataContext is 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 AsynAction 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 Action, AsyncAction and Presenter attributes that is worth looking into. Additionally, PresenterBase has a method on it that allows for changing the view and ViewSite 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 WPF development. Hopefully this will give you some ideas to chew on and motivate the creation of a more maintainable presentation layer for your apps.

Possible Improvements

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 SOA 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 Dialog and MessageBox functionality. Concrete implementations should be automatically injected at runtime.
  • 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 TDD/BDD at present.

    Summary

    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 WPF work.

    Note: Before running the solution you must create the database, set the connection string (NHibernate.config) and declare your amazon.com key (LibraryItemPresenter.cs).

    References

    <<  Previous Article Continue reading and see our next or previous articles Next Article >>

    About Rob Eisenberg

    Sorry, no bio is available

    This author has published 2 articles on DotNetSlackers. View other articles or the complete profile here.

    Other articles in this category


    Integrate Lua into WPF Games
    Lua is a world-famous scripting language. It is a lightweight multi-paradigm programming language de...
    Creating Video Clips in WPF
    In this article we will see how we can clip videos with few kilobytes of memory being used by your h...
    Styles in WPF
    In this article, you will learn what are styles, how to create and use styles in WPF applications.
    Cascading ListBox Using MVVM in WPF
    In this article we will see how we could implement a List Box and using the powerful Model View View...
    Storyboard Playing with Playlist Manager in WPF
    In last article we achieved clipping videos out of the parent video. In this article we will see how...
    Top
     
     
     

    Discussion


    Subject Author Date
    placeholder Nice Thoughts! Scott Criswell 7/2/2007 2:03 PM
    Vote Scott Criswell 7/2/2007 5:23 PM
    placeholder Voting Sonu Kapoor 7/2/2007 5:25 PM
    Wow Rob! Michelle Rutherford 7/2/2007 8:04 PM
    placeholder Article James Dewberry 7/5/2007 3:59 PM
    Tanx Lasse Lindström 7/13/2007 5:18 AM
    placeholder Excellent Sam B 9/18/2007 4:53 PM
    WPF Framework Rob Eisenberg 9/18/2007 6:53 PM
    placeholder How do I set up the database Elisabeth Boonin 11/1/2007 7:42 PM
    How do I set up the database Elisabeth Boonin 11/1/2007 7:42 PM
    placeholder Getting the DB Working Rob Eisenberg 11/1/2007 10:28 PM
    RE: Getting the DB Working Elisabeth Boonin 11/2/2007 3:17 PM
    placeholder Open Source Release Kikok Kikok 12/5/2007 4:21 AM
    Open Source Rob Eisenberg 12/5/2007 10:38 AM
    placeholder RE: Open Source John Vanspronssen 12/7/2007 12:39 AM
    New Version and Code Available Rob Eisenberg 1/7/2008 6:27 PM
    placeholder RE: New Version and Code Available Sonu Kapoor 1/7/2008 9:24 PM

    Please login to rate or to leave a comment.