Click here to Skip to main content
15,867,594 members
Articles / Web Development / ASP.NET
Article

Presentation Model in Action

Rate me:
Please Sign up or sign in to vote.
4.93/5 (31 votes)
3 Feb 2008CPOL14 min read 124.1K   1.6K   119   17
Use the Presentation Model pattern in ASP.NET Web site, Windows Forms and WPF

Introduction

The Model-View-Controller (MVC) is the famous design pattern that separates the presentation and the domain/data access. Further more MVC also splits presentation logic into view and controller.

Presentation Model and Model View Presenter (MVP) are variations on model-view-controller (MVC) design pattern where the controller becomes Presentation Model and Presenter and has a different way to organize state, logic and references.

Presentation Model Key Features

  • State is in the presentation model
  • Logic is in the presentation model
  • View observes the model and updates accordingly
  • The view “knows” about the presentation model
  • The presentation model does not “know” about the view

Model View Presenter has two flavors, named Supervising Controller and Passive View. They share the same key features.

Model View Presenter Key Features

  • State is in the view
  • Logic is in the Presenter
  • Presenter observes the view
  • Presenter updates the view
  • Presenter ‘knows’ about the view
  • View does not ‘know’ about the Presenter

Presentation Model is a pattern that pulls presentation behavior from a view. It has a centralized place to store state/data and centralized event source which is completely independent to the views used for display.

In real life projects, I prefer using the Presentation Model. This article is going to show the design journey of designing a use case of managing the Customer business entity and shows how the Presentation Model fits into ASP.NET Web site, Windows Form application and WPF application.

Let’s start with analyzing the requirements of a fictional business entity, Customer management application.

Requirement Analysis

Many business management processes can be abstracted as the following stories:

  • User searches business entities
  • User views the list of entities
  • User selects an entity from the list
  • User views the details of the selected entity
  • User manipulates the selected entity, e.g. modify, delete etc.

Our story is about to manage Customer, the sample business entity. The use case can be described as below.

Use Case Name

Search and view customer information

Goal

Users search customers and view details of selected customer.

Pre-Conditions

User launches the application

Event Flows

  1. System displays all customers in a list and number of customers
  2. User searches customers by typing the search text
  3. System matches the search text with customer first name, last name and address
    If the search text is empty, system displays all customers
  4. System displays the customer list that matches the search text
  5. System displays the number of customers based on the search result
  6. User selects a customer
  7. System displays the details of the selected customer
  8. User can repeat the search

Design Considerations

The application can be designed as an ASP.NET Web site, a Windows Form application or a WPF application. This article will show all three application forms using the Presentation Model. To begin the design, first have a look at some user interface options on the Web site.

On a Web site, usually there are three options to implement this customer management.

Option 1: List Page and Popup Detail Page

This option is to display the customer list with a hyperlink on each row. When the hyperlink is clicked , a new popup window shows up with customer details in it.

Option1.JPG

Option 2: List Page and Switch to Detail Page

While the first option is a commonly used pattern in many earlier Web applications, more recent Web applications show the details in the same window to avoid popup.

Option2.JPG

Option 3: List Page and Embedded Detail Page

Similar to Option 2, there won't be popup, further more this option does not hide the list while showing the details either. It shows the list and details together.

Option3.JPG

The user experience designer usually decides which option is to be used in final products. The decision is based on users’ feedback and UI layout and look and feel designs.

The decision could be changed in the middle of the project. E.g. initially option 1 was chosen. Then users reported they blocked popup, so it has to be option 2. Finally, since the layout and font make the screen have quite a big space, designer may come to ask if we can display the list and details at the same time. It then becomes option 3.

Architectural design should foresee the potential requirement changes and have minimum impacts on code in case of the changes. Another word is to say the code should be reusable as much as possible.

Good news is that we can use user controls. User control is a great technology to achieve reusability. It is known as application building blocks. It created a customer list user control and a customer details user control. All three options can be built upon them.

In option 1, a page hosts customer list user control. A popup page hosts customer details user control.

In option 2, a page hosts customer list user control and customer details user control. When a customer is selected, hide the customer list user control and show the customer details user control.

In option 3, a page hosts customer list user control and customer details user control. When a customer is selected, show the customer details user control. Sometimes it may need to scroll the screen to the top of the customer details area.

Good news again is that no matter how screen layout or window arrangement changes, the data model behind the scene is actually the same. It is a customer list and a selected customer.

We need a class to hold the customer list and a selected customer. If the customer list changed in case the user typed in search text, it sends out an event saying customer list changed. If user selected a customer, it sends out an event saying selected customer changed.

User controls connect to this class and pull the customer list or selected customer to data bind to UI elements, such as grid view or form view.

User controls also listen to the events this class sends out. If the customer list changed, re-bind the grid view. Or if the selected customer changed, re-bind the form view.

Very naturally, the Presentation Model pattern comes into the picture. The class described above is exactly the Presentation Model, we name it CustomerPresentationModel.

The CustomerPresentationModel class has properties to hold the customer list and selected customer data.

C#
public interface ICustomerPresentation : INotifyPropertyChanged
{
    IEnumerable<Customer><customer /> Items { get; set; }
    Customer SelectedItem { get; set; }
    int ItemCount { get; set; }
}

In order to send out an event, we can define a custom event handler. But since Windows Forms data bind and WPF data bind rely on INotifyPropertyChanged interface, we use this interface.

The CustomerPresentationModel class is a subclass of the generic Presentation Model class, PresentationModel<T>.

C#
public class CustomerPresentationModel :
    PresentationModel<Customer>, ICustomerPresentation
{
    //...
}

public abstract class PresentationModel<T> : INotifyPropertyChanged
{
    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    #endregion

    protected int itemCount;
    public int ItemCount
    {
        get { return itemCount; }
        set { itemCount = value; NotifyPropertyChanged("ItemCount"); }
    }

    protected IEnumerable<t /> items;
    public virtual IEnumerable<t /> Items
    {
        get { return GetItems(); }
        set { items = value; NotifyPropertyChanged("Items"); }
    }

    protected abstract IEnumerable<t /> GetItems();

    private T selectedItem;
    public virtual T SelectedItem
    {
        get { return selectedItem; }
        set { selectedItem = value; NotifyPropertyChanged("SelectedItem"); }
    }

    protected void Reset()
    {
        Items = null;
        SelectedItem = default(T);
    }
}

PresentationModel<T> class is a base class using generics. With this class, we can apply the Presentation Model design pattern to all kinds of business entities, such as Order, Products, etc. in the future.

For now, we will focus only on the CustomerPresentationModel and on using it to build three types of applications.

Build the ASP.NET Web Site

Option3.JPG

The steps to build the ASP.NET Web site are:

p1.JPG

  • Create user controls CustomerList.ascx and CustomerEdit.ascx
  • Create subclass of the CustomPresentationModel
  • Databind to the CustomPresentationModel
  • Create Web forms to host the user controls:
    • Customer1.aspx hosts CustomerList.ascx
    • Customer2.aspx hosts CustomerList.ascx and CustomerEdit.ascx
    • Customer3.aspx hosts CustomerList.ascx and CustomerEdit.ascx
    • CustomerDetails.aspx hosts CustomerEdit.ascx

These are straight forward steps, but there are some points of interests.

ASP.NET Web site can be stateless meaning that objects are typically created and destroyed to serve each request. In this case, due to the fact that Presentation Model is accessed by multiple user controls, it is expected to be created at the beginning of the request and live until the end of the request. But while ASP.NET Framework provides application scope storage and session storage to store custom objects, unlike JSP, it does not have a request scope storage. So we will store the CustomerPresentationModel in the session.

C#
public static CustomerPresentationModel Instance
{
    get
    {
        CustomerPresentationModel instance = HttpContext.Current.Session["_c_"]
        as CustomerPresentationModel;
        if (instance == null)
        {
            instance = new CustomerPresentationModel();
            HttpContext.Current.Session["_c_"] = instance;
        }
        return instance;
    }
}

Data Bind to the Presentation Model Instance

ASP.NET has a great declarative databinding model against plain .NET objects. It is done through the ASP.NET ObjectDataSource control which connects the data-bound controls such as the GridView, FormView, or DetailsView controls to objects.

ASP.NET
<asp:GridView .... DataSourceID="CustomerListDataSource" DataKeyNames="Id">
    <Columns>
    ...
    </Columns>
</asp:GridView>

<asp:ObjectDataSource ... DataObjectTypeName="Demo.DataModel.Customer"
TypeName="CustomerPresentationModel" SelectMethod="GetCustomerList"
OnObjectCreating="CustomerListDataSource_ObjectCreating" />

ObjectDataSource exposes a TypeName property that specifies an object type (class name) for performing data operations. In our case it is the CustomerPresentationModel.

By default, ObjectDataSource will instantiate CustomerPresentationModel instance through a default constructor (no arguments), but we already have CustomerPresentationModel object in the session. Fortunately, we can handle the ObjectCreating event to assign CustomerPresentationModel object from session to the ObjectInstance property of ObjectDataSource.

C#
protected void CustomerDataSource_ObjectCreating
        (object sender, ObjectDataSourceEventArgs e)
{
    e.ObjectInstance = CustomerPresentationModel.Instance;
}

When data-bound controls need data, ObjectDataSource calls into the CustomerPresentationModel instance’s method GetCustomerList (declared in the ObjectDataSource’s SelectMethod property). That method should return any Object or IEnumerable list, collection, or array.

CustomerPresentationModel exposes the customer list and selected customer as properties of, not method, so here we need a little wrapping in order to work with the ObjectDataSource.

C#
public IEnumerable<Customer> GetCustomerList()
{
    return Items;
}

public Customer GetCustomer()
{
    return SelectedItem;
}

Listen to the Events

The user controls listen to Presentation Model’s events. The customer list user control is monitoring the “items changed” event to re-bind the grid view. The customer details user control is similarly monitoring the “selected item changed” event. This is not only to refresh the form view, but also to hide itself if the selected item in the CustomerPresentationModel is null.

In CustomerEdit.ascx.cs,

C#
void pm_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "SelectedItem")
    {
        if (pm.GetCustomer() != null)
        {
            CustomerFormView.DataBind();
            Visible = true;
        }
        else
        {
            Visible = false;
        }
    }
}

Event driven makes option 2 and option 3 very easy to implement. In option 2, the logic is if there is no customer selected, show the list and hide the details. In option 3, no extra logic is needed.

When user searches the customer list, CustomerPresentationModel sends out event “items changed” event to notify customer list user control to refresh accordingly.

In Customer2.aspx.cs,

C#
void pm_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "SelectedItem")
    {
        this.CustomerList1.Visible = pm.GetCustomer() == null;
    }
}

The benefit of this event driven mechanism is that there potentially could be more than one user controls to be notified and refreshed. And it also provides a solution to synchronize state of different user controls.

Need for Dependency Injection Framework

Looking into the code of CustomerList.ascx.cs and CustomerEdit.ascx.cs, you can find repeated code, like:

C#
CustomerPresentationModel pm;

protected void Page_Load(object sender, EventArgs e)
{
    pm = CustomerPresentationModel.Instance;
    System.Diagnostics.Debug.Assert(pm != null);
    if (pm != null)
    {
        pm.PropertyChanged += new PropertyChangedEventHandler(pm_PropertyChanged);
    }
}

protected void Page_Unload(object sender, EventArgs e)
{
    System.Diagnostics.Debug.Assert(pm != null);
    if (pm != null)
    {
        pm.PropertyChanged -= new PropertyChangedEventHandler(pm_PropertyChanged);
    }
}

void pm_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
}

If there is a Dependency Injection Framework, then the above code can be simplified to be something look like this:

C#
[PresentationMode(Scope= Session)]
CustomerPresentationModel pm;

[PropertyChangedEventSubscription]
void pm_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
}

I am looking for a light weight Dependency Injection Framework that can be used in a Web application.

Windows Forms Application

WindowsFormsApp.JPG

We have created the ASP.NET Web site. It’s time to create a Windows Forms application.

The steps used in the ASP.NET Web site apply here which includes:

p2.JPG

  • Create user controls CustomerList.cs and CustomerEdit.cs
  • Create subclass of the CustomPresentationModel and make a singleton
  • Databind to the CustomPresentationModel

In Windows Forms application, CustomPresentationModel can live as a singleton. The data binding is also nice and easy by creating BindingSource in Visual Studio.

But it is important to remember that the Windows Forms data binding is driven off of change notification. That means the Windows Forms will only update a user interface element when the data source notifies that the data has changed (by providing a notification event).

In our case, the data source is the CustomPresentationModel instance. When customer list is reloaded, it sends out two PropertyChanged events. One has changed property name of Items, the other one has changed property name of ItemCount.

The label bound to the ItemCount property updated automatically, but the grid does not seems to pick up Items changed event. Why?

In the case of simple property to property binding, the data source needs to provide property change notification by either providing a "PropertyName changed event for the property or by implementing the INotifyPropertyChanged interface.

When the data source is a list, the data source needs to provide the list change notification that is used to notify user interface elements when an item has been added, removed or deleted from the list via the IBindingList interface.

That is to say to be fully integrated with Windows Forms data binding, the rule is to implement the INotifyPropertyChanged interface on your business entity class and use the IBindingList interface for your business entity collections.

I like data binding, but am hesitating to do it to pollute my entities and entity collections. I would rather refresh the grid view by the code using an anonymous method.

C#
private void CustomerList_Load(object sender, EventArgs e)
{
    customerPresentationModelBindingSource.DataSource =
        CustomerPresentationModel.Instance;
    customerBindingSource.DataSource =
        CustomerPresentationModel.Instance.Items;

    CustomerPresentationModel.Instance.PropertyChanged +=
        delegate(object s, PropertyChangedEventArgs ev)
    {
       if (ev.PropertyName == "Items")
              customerBindingSource.DataSource =
              CustomerPresentationModel.Instance.Items;
        };
    }
}

Within the Composite UI Application Block, there are Work Items that act like a dependency injection container and Event Broker that provides a many-to-many, loosely coupled event system mechanism. This application block is a perfect platform to build applications using the Presentation Model pattern.

WPF Application

WPFApp2.JPG

As expected, the steps again are the same as those have been used in the ASP.NET Web site and the Windows Forms application.

p3.JPG
  • Create user controls CustomerList.xaml and CustomerEdit.xaml
  • Create subclass of the CustomPresentationModel and make a singleton
  • Databind to the CustomPresentationModel

Here, CustomPresentationModel lives as a singleton the same way as in the Windows Forms application.

The list view bound to the customer list updates accordingly to the Items changed event. Besides this, there are some other great features in WPF data binding.

In WPF, we can directly connect to the objects without helper such as the ObjectDataSource in ASP.NET and BindingSource in Windows Forms. Usually I define the a static resource referencing the CustomPresentationModel instance.

XML
<UserControl x:Class="Demo.WpfApp.CustomerEdit"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="550"
    xmlns:local="clr-namespace:Demo.WpfApp">

<UserControl.Resources>
    <ObjectDataProvider x:Key="controller"
        ObjectType="{x:Type local:CustomerPresentationModel}"
    MethodName="get_Instance" />
</UserControl.Resources>

It is also possible to bind data to UI element’s DataContext property and allow the child element to inherit the data source information and simplify the binding syntax. This is well demonstrated in the CustomerEdit.asmx user control where the top level grid’s DataContext was bound to the selected customer object. Elements inside the grid then bind to the customer object’s properties just using path.

XML
<UserControl x:Class="Demo.WpfApp.CustomerEdit"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="550"
    xmlns:local="clr-namespace:Demo.WpfApp">

<UserControl.Resources>
    <ObjectDataProvider x:Key="controller"
        ObjectType="{x:Type local:CustomerPresentationModel}"
    MethodName="get_Instance" />
    <SolidColorBrush x:Key="LabelForegroundBrush" Color="#FFFFFFFF"/>
</UserControl.Resources>

<Grid DataContext="{Binding Source={StaticResource controller},
        Path=SelectedItem}" Background="#FF595959">
<Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto" />
    <ColumnDefinition Width="Auto" />
    <ColumnDefinition Width="Auto" />
    <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
    <RowDefinition Height="*" />
</Grid.RowDefinitions>

<Label Grid.Row="0" Foreground="{DynamicResource LabelForegroundBrush}">
        First Name</Label>
<TextBox Grid.Row="0" Grid.Column="1" Width="150"
         HorizontalAlignment="Left" VerticalAlignment="Center"
         Text="{Binding Path=FirstName}">
</TextBox>

<Label Grid.Row="0" Grid.Column="2"
        Foreground="{DynamicResource LabelForegroundBrush}">Last Name</Label>
<TextBox Grid.Row="0" Grid.Column="3" Width="150"
         HorizontalAlignment="Left" VerticalAlignment="Center"
         Text="{Binding Path=LastName}"/>

<Label Grid.Row="1" Foreground="{DynamicResource LabelForegroundBrush}">Address</Label>
<TextBox Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3"
         HorizontalAlignment="Stretch" VerticalAlignment="Center"
         Text="{Binding Path=Address}"/>

<Label Grid.Row="2" Foreground="{DynamicResource LabelForegroundBrush}">Comments</Label>
<TextBox Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3"
         HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
         MinHeight="50"
         Text="{Binding Path=Comments}" TextWrapping="Wrap" />
</Grid>

</UserControl>

Some thoughts on WPF. WPF is supposed to bring application "differentiated user experience" or "differentiated UI". In our case, it is possible upon the “selected item” changed event, active storyboard or timeline that mimic 3D effects or page flip effect in the bottom area of our application.

What does differentiated UI mean to LOB applications? Probably not just flipping the pages. I am waiting to see how the WPF composite client application block will define that.

Review

So far we have created using the Presentation Model in ASP.NET Web site, a Windows Forms application and a WPF application. Although the Customer class used here is fictional, the design practice of the Web site UI options actually was a true story. We did not realize the Presentation Model is in action until we found the following references later on.

Looks like since the Presentation Model originated from Smalltalk, Java world, FLEX world as well as .NET world are experimenting and using this pattern. This encouraged us to keep exploring more on this track.

Microsoft Web Client Software Factory and Microsoft Smart Client Software Factory are using MVP pattern. There are several things that are not as good as the Presentation Model. E.g. every view, usually a user control requires a presenter class addition to existing code behind class / code beside class. I feel it increases code maintenance difficulties. Not as clear as the Presentation Model demonstrated above.

Microsoft Smart Client Software Factory is based on the Composite UI Application Block (CAB) which provides many great features, such as Dependency Injection (IoC) Framework, event broker. To build application using CAB and the Presentation Model is easy and fun.

In the WPF would, the WPF team has been prompting the Model-View-ViewModel pattern. Essentially Model-View-ViewModel is a Presentation Model. They are two labels for the same thing.

In the coming WPF composite client application block, it is very interesting to see how the patterns & practices team from Microsoft will use the Presentation Model pattern or MVP. How will the Dependency Injection work and how to extend the WPF command/event handling to AOP style?

Conclusion

As demonstrated in this article, the Presentation Model is proven to be an effective pattern in practice. Although ASP.NET, Windows Forms and WPF are total different technologies, they can share the same Presentation Model class. The pattern brings the value of decoupling data model and presentation. It indeed can also integrate with and take advantages of many .NET technologies, such as user control and data binding.

Hope you start to like the Presentation Model after reading this article, if not yet. To make it more convincing, I have another article showing how to use the Presentation Model in SharePoint.

History

License

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


Written By
Architect
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralWeird phenomena Pin
jefferycxl6-Aug-09 13:24
jefferycxl6-Aug-09 13:24 

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.