Alex Mueller on Software and Technology 
Sunday, August 26, 2007

For the past couple months, I have been using an MVP implementation described by Billy McCafferty, "Model View Presenter with ASP.NET," in my ASP.NET web applications. Please read Billy's article, since this entry assumes a familiarity with it. Recently I began looking for implementations where the view is further separated from the presenter, something similar to the original MVP I first learned with using windows applications. In this effort, I came across an article by Phil Haack, "ASP.NET Supervising Controller (Model View Presenter) from Schematic to Unit Tests to Code." Using these two models, I am now using a solution that implements those attributes I prefer from each.

Billy defines the role of the ASPX page as the "view initializer," where it is responsible for initializing the view, wiring up the presenter, and redirecting pages as needed. Each view interface implements an AttachPresenter method, where the view passes an instance of the presenter to itself. The view knows about the presenter, and the presenter knows about the view.

An example of this relationship is seen below. Typically, a Presenter is instantiated, passing to it a view, and any model objects that are necessary (no model objects are passed in the example below). The presenter then, if necessary, registers any event listeners. The view then attaches the presenter to it so that is can call the presenter when needed in the ASCX code behind. The presenter then initializes the view.

Fictitious Example 1 (this is incomplete code)

// ASPX Code Behind - Wire up the MVP relationship
protected void Page_Load(object sender, EventArgs e)
{
    Presenter presenter = new Presenter(view);
    presenter.SuccessfulEvent += new EventHandler(SuccessfulEventListener);
    view.AttachPresenter(presenter);
    presenter.InitView(Page.IsPostBack);    
}

private void SuccessfulEventListener(object sender, EventArgs e)
{
    Response.Redirect("SomeNewPage.aspx");
}

// ASCX Code Behind
public void AttachPresenter(Presenter presenter)
{
    Check.Require(presenter != null, "presenter cannot be null");
    this.presenter = presenter;
}

protected void DropDownListChange(object sender, EventArgs e)
{
    int itemID = int.Parse(ddlItems.SelectedValue);
    presenter.UpdateItems(itemID);
}

public void SetItems(ICollection items)
{
    // bind the collection to the view
}

// Presenter Code
public void UpdateItems(int itemID)
{
    // do something to refresh the items
    view.SetItems("some collection here");
}

This implementation of MVP has been working great, but I feel that the view should not know about is presenter. This is not to say that Billy's implementation is incorrect by any means. My first introduction to MVP was event-driven, and it was used in a windows application. I prefer this implementation because the view was ignorant of its presenter. It was a cleaner separation. So I searched for other implementations that are used specifically for the web and one article I stumbled upon was Phil Haack's.

Phil's MVP implementation is event-driven and follows the Supervising Controller pattern. The view interface defines events to which the presenter will subscribe. In the default IView interface, event handlers for the page's Init and Load are defined, as well as the ability to retrieve IsPostBack and IsValid status, and a method to databind the page. More specific views will implement this IView interface and define their own events. Basically, the view in this relationship raises events and responds to requests from the presenter.

An example of this relationship requires a little more than a few lines of code, so please see Phil's article for this. The ASP.NET page implements the view's interface, and the page's constructor instantiates the presenter-to-view relationship, passing in any model objects. As events are raised, the view raises these to the presenter, and the presenter responds back to the view.

There are parts of each of these two implementations that I prefer and have chosen to use in my own. I prefer the role of the ASPX page as the view initializer and page redirector, and I prefer the event-driven approach to make the view unaware of its presenter. Due to the stateless nature of the web, we do need to provide a means of retrieving IsPostBack status so we may render our views accordingly, and I prefer to pass this in as a model object. In keeping with the DRY principle of OO, I do not want my presenter to call view.IsPostBack, instead, I will pass this into the presenter.

The web is stateless and therefore we must recreate our MVP relationship with each postback. Unlike a thick client application where we instantiate the presenter once, in the web, we do this every time the page posts back to the server. This makes it difficult to preserve state within our presenter. My approach is to pass in as much domain logic as I can and allow the ASCX view to help persist state using ViewState or Session.

Fictitious Example 2 (this is incomplete code)

// ASPX Code Behind
protected void Page_Load(object sender, EventArgs e)
{
    Presenter presenter = new Presenter(view, model, Page.IsPostBack,);
    presenter.SuccessfulEvent += new EventHandler(SuccessfulEventListener);
    
}

private void SuccessfulEventListener(object sender, EventArgs e)
{
    Response.Redirect("SomeNewPage.aspx");
}

// Presenter Code
public Presenter(IView view, IModel model, bool isPostBack)
{
    Check.Require(view != null, "view cannot be null");
    Check.Require(model != null, "model cannot be null");
    
    this.view = view;
    this.model = model;
    this.isPostBack = isPostBack;
    
    this.view.OnViewLoad += new EventHandler(OnViewLoadListener);
    this.view.RequestProducts += new EventHandler<MyCustomEventArgs<int>>(RequestProductsListener);
}

private void OnViewLoadListener(object sender, EventArgs e)
{
    if(!isPostBack)
    {
        // do whatever is needed the first time the page loads
        view.SetInitialData("pass in some initial data here");
        // call other view setters as needed
    }
    else
    {
        // do whatever
    }
}

private void RequestProductsListener(object sender, MyCustomEventArgs<int> e)
{
    int productID = e.Value;
    IList<Product> products = model.GetById(productID);
    view.SetProductDetails(products);
}

// ASCX View Code
protected void DropDownListChange(object sender, EventArgs e)
{
    int itemID = int.Parse(ddlItems.SelectedValue);
    OnRequestProducts(itemID);
}

public virtual void OnRequestProducts(int productID)
{
    EventHandler<MyCustomEventArgs<int>> eventHandler = RequestProducts;
    if(eventHandler != null)
    {
        eventHandler(this, new <MyCustomEventArgs<int>>(productID));
    }
}

In the example above, I am striving to keep a clean separation between my view and its presenter. I am trying to adhere to the same event-driven MVP relationship I first used in windows applications. There is a good deal of grey-area between the Supervising Controller and Passive View patterns, and while I would prefer to follow the latter, sometimes I need to make my view intelligent enough so it can help in persisting data with each trip to the server.

My two primary reasons for implementing MVP in ASP.NET are separation of concerns and testability. The event-driven approach is easily testable despite using events, and with Rhino Mocks, mocking events, as well as other objects is simple. One added complexity with the event-driven approach is passing data from the view to the presenter in the form of event arguments. Since the presenter cannot persist its state without the help of ASP.NET, I often need to pass objects to the view when raising events, but I try and keep these objects light.

There are a number of MVP implementations to be found on the web. I tend to explore what I find and use what I feel suits a given situation. I do have a sample application that I put together for a demo that I can zip up and email if you would like. In it, I show the MVP implementation of Billy McCafferty that I have been using for months, and my new way, the event-driven approach. If you would like this sample app, email me.

Sunday, August 26, 2007 4:54:14 PM (Mountain Standard Time, UTC-07:00) | Comments [0] | Design | Frameworks/Patterns | Technology#
Comments are closed.
MuellerDesigns.net
Search
On This Page
The Split Personality of the Tester/Developer
Cross Site Scripting (XSS)
Creating files with FSUTIL
PowerShell Management Library for Hyper-V
Installing Windows 7
Installing Linux in Hyper-V
Internet Explorer 8 Release Candidate 1
PowerShell Documentation
Automate Daily Tasks with PowerShell
SketchPath XPath Editor
Software Testing - Revisited
Architecting Buildings and Software
NBCOlympics.com with Silverlight
Marker Interfaces and C# Attributes
Most Popular
JavaScript ReplaceAll Functionality
What is polymorphism?
What is composition?
Sorting with IComparable and IComparer
Applying the Observer Pattern in ASP.NET
MVP in ASP.NET
What is abstraction?
What is encapsulation?
What is a class?
What is inheritance?
Authentication in ASP.NET
Calendar Controls
XPathNavigator.CheckValidity new for 2.0
SQL Server 2005 Connection Issues
Auto-attach to process '[####] aspnet_wp.exe' on m...
What is an object?
FreeTextBox
VMWare and VPC
An Example of Reflection using C#
Changing File Ownership In Vista and Longhorn
Archive
Links
Categories
My Local Blog Map
Blogroll
About
Powered by:

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
MuellerDesigns.net

Sign In

Help Those In Need
The Hunger Site
Ronald McDonald House Charities (RMHC) of Western Washington & Alaska