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
- System displays all customers in a list and number of customers
- User searches customers by typing the search text
- System matches the search text with customer first name, last name and address
If the search text is empty, system displays all customers - System displays the customer list that matches the search text
- System displays the number of customers based on the search result
- User selects a customer
- System displays the details of the selected customer
- 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.
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.
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.
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.
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>
.
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
The steps to build the ASP.NET Web site are:
|
- 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.
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: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
.
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
.
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,
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,
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:
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:
[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
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:
|
- 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.
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
As expected, the steps again are the same as those have been used in the ASP.NET Web site and the Windows Forms application.
|
- 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.
<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.
<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