Model-View-Presenter pattern description | How to: Implement the Model-View-Presenter Pattern | How to: Unit Test the Presenter

Model-View-Presenter QuickStarts

This bundle includes the following QuickStarts:
  • Model-View-Presenter QuickStart. This QuickStart illustrates the key components in the implementation of the Model-View-Presenter pattern.
  • Model-View-Presenter with Composite Web Application Block QuickStart. This QuickStart illustrates the key components in the implementation of the Model-View-Presenter pattern in a Composite Web Client solution, taking advantage of the dependency injection mechanism included in the application block.
Note:
The Composite Web Application Block and ObjectBuilder include classes that help you implement the Model-View-Presenter pattern. To keep things straightforward, the Model-View-Presenter QuickStart does not use either application block to demonstrate the basic elements of a Model-View-Presenter pattern implementation. For an example of a Model-View-Presenter implementation using the aforementioned application blocks, see the Model-View-Presenter with Composite Web Application Block QuickStart.

Business Scenario

Both QuickStarts correspond to the same business scenario. The QuickStarts contain a single contacts management Web page that allows users to see a list of contacts and edit the contacts’ information using a form embedded in the same page as the list. Figure 1 illustrates the Contacts page (both QuickStarts share the same user interface).
ContactsPage.png
Figure 1
Contacts page.

Walkthrough

Perform the following steps in the Model-View-Presenter or Model-View-Presenter with Composite Web Application Block QuickStarts to explore the business scenario.

To explore the business scenario
  • Open the solution file MVPQuickstart.sln (Model-View-Presenter QuickStart) or MVPWithCWABQuickStart.sln (Model-View-Presenter with Composite Web Application Block QuickStart).
  • On the Build menu, click Rebuild Solution.
  • Press F5 to run the QuickStart. The Contacts page will be shown. The Contacts page displays a list of contacts, as shown in Figure 2.
ContactsList.png
Figure 2
Contacts list.
  • On the Contacts page, select a user by clicking Select. The user details will be displayed in the form below the grid, as shown in Figure 3.
ContactDetailsDisplayedInAForm.png
Figure 3
The contact details are displayed in a form.
  • Click the Edit button to edit the contact’s details. The form will be displayed in edit mode, as shown in Figure 4.
ContactDetailsFormShownInEditMode.png
Figure 4
Contact details form shown in edit mode.
  • Modify the contact’s details, and then click Save. The changes will be reflected in the contacts list.

Building and Running the QuickStart

The QuickStarts ship as source code, which means you must compile them before running them.

To build and run the Model-View-Presenter QuickStart
  1. Open the solution file MVPQuickstart.sln or MVPQuickstart (VSTS Tests).sln (this solution includes unit tests.)
  2. On the Build menu, click Rebuild Solution.
  3. Press F5 to run the QuickStart.
To build and run the Model-View-Presenter with Composite Web Application Block QuickStart
  1. Open the solution file MVPWithCWABQuickstart.sln or MVPWithCWABQuickStart (VSTS Tests).sln (this solution includes unit tests.)
  2. On the Build menu, click Rebuild Solution.
  3. Press F5 to run the QuickStart.

Acceptance Tests

This bundle includes separate solutions with the acceptance tests for the Model-View-Presenter and the Model-View-Presenter with Composite Web Application Block QuickStarts. The acceptance tests describe how QuickStarts should perform when you follow a series of steps; you can use the acceptance tests to explore the functional behavior of the QuickStarts in a variety of scenarios.
The acceptance tests were developed using the testing framework WatiN. To run the tests, you need to have WatiN installed.

To run the Model-View-Presenter QuickStart acceptance tests
  1. Open the solution file MVPQuickstart_FunctionalTests.sln.
  2. Fix the references to Interop.SHDocVw.dll, Rhino.Mocks.dll, and WatiN.Core.dll assemblies in the MVPQuickStart_FunctionalTests project.
  3. Run the tests using the Test Manager.
To run the Model-View-Presenter with Composite Web Application Block acceptance tests
  1. Open the solution file MVPWithCWABQuickStart_FunctionalTests.sln.
  2. Fix the references to Interop.SHDocVw.dll, Rhino.Mocks.dll, and WatiN.Core.dll assemblies in the MVPWithCWABQuickStart_FunctionalTests project.
  3. Run the tests using the Test Manager.

Implementation Notes

The QuickStarts highlights the key components of a Model-View-Presenter pattern implementation. The following are the key artifacts in the QuickStarts:
  • Contacts list Web page and presenter. The contacts list Web page and the presenter interact to display a list of contacts to the user.
  • Contact details user control and presenter. The contact details user control and presenter interact to display detailed information of a contact to the user.
In both QuickStarts, the Web page and user control files reside in the Web application project, while the view interfaces and presenter classes reside in a class library project. Figure 5 illustrates the solution structure of the Model-View-Presenter QuickStart.
Views-Presenter-MVPQuickStart.png
Figure 5
Views and presenter files in the Model-View-Presenter QuickStart solution.

User Gestures Handling

Views do not contain code to handle user interface events; instead, views notify their presenters through events or direct method calls to the presenter. The contact details view uses the former approach and the contacts list view the latter.
The following code extracted from the contact details user control’s code behind file illustrates how an event is raised when the user clicks the Edit button.

protected void OnEditClicked(EventArgs e)
{
    if (EditClicked != null)
    {
        EditClicked(this, e);
    }
}

protected void EditButton_Click(object sender, EventArgs e)
{
    OnEditClicked(e);
} 

When the contact details view is loaded, the presenter adds event handlers for the view’s events, as shown in the following code.

public virtual void OnViewLoaded()
{
    _view.LoadStates(_dataSource.States);
    Controller.CurrentCustomerChanged += new EventHandler(Controller_CurrentCustomerChanged);
    _view.EditClicked += new EventHandler(View_EditClicked);
    _view.DiscardChangesClicked += new EventHandler(View_DiscardChangesClicked);
    _view.SaveClicked += new EventHandler<DataEventArgs<Customer>>(View_SaveClicked);
} 

The following code extracted from the contacts list Web page’s code-behind file shows the event handler for the SelectedIndexChanged event of the contacts GridView. In this case, the view invokes the OnSelectedIndexChanged method on the presenter to notify the user gesture.

protected void CustomersGridView_SelectedIndexChanged(object sender, EventArgs e)
{
     _presenter.OnSelectedIndexChanged();
} 

Referencing the View Interface instead of the Concrete Implementation

In the Model-View-Presenter pattern, the view implements an interface and the presenter references the view interface instead of the view real implementation, to facilitate the replacement of the view with a mock view when running unit tests. The following code shows the interface definition for the contact details view. Note that the interface exposes events to communicate with the presenter and methods that the presenter can call to manipulate the state of the view.

public interface IContactDetailView
{
    void LoadStates(ICollection<State> states);
    void SetViewReadOnlyMode(bool readOnly);
    void SetViewControlsVisible(bool visible);
    void ShowCustomer(Customer customer);

    ContactDetailPresenter Presenter { get;}
    event EventHandler EditClicked;
    event EventHandler<DataEventArgs<Customer>> SaveClicked;
    event EventHandler DiscardChangesClicked;
    event EventHandler UserControlLoaded;
} 

Wiring Up Presenters and Views

ASP.NET constructs the views (Web pages, user controls, and master pages), but views require a presenter, and presenters might require other dependencies. When you use the Composite Web Application Block, you use dependency injection to create the presenter and its dependencies, as illustrated in the following code taken from the Model-View-Presenter with Composite Web Application Block QuickStart.

[CreateNew]
public ContactsListPresenter Presenter
{
    set
    {
        this._presenter = value;
        if (value != null)
        {
            this._presenter.View = this;
        }
    }
    get
    {
        return this._presenter;
    }
} 

In contrast, in the Model-View-Presenter QuickStart, the view constructs both the presenter and the dependencies for the presenter, as shown in the following code.

protected void Page_Load(object sender, EventArgs e)
{
    _presenter = new ContactsListPresenter(this, new ContactsController(new CustomersDataSource()));
    if (!this.IsPostBack)
    {
        this._presenter.OnViewInitialized();
    }
    this._presenter.OnViewLoaded();
} 

Updating the View to Reflect Changes in the Model

To perform view updates, you can have the view directly interact with the model to perform simple data binding operations or have the presenter exclusively interact with the model and manage view updates.
The contacts list Web page uses an ObjectDataSource control to directly interact with the model to retrieve the list of contacts to display; the presenter does not intervene in view updates. In contrast, in the contact details view, the presenter updates the view when the model changes. The following code extracted from the ContactDetailPresenter class code shows how the presenter tells the view to display a customer when the selected customer in the contacts list changes.

void Controller_CurrentCustomerChanged(object sender, EventArgs e)
{
    LoadCurrentCustomerOnView();
    _view.SetViewControlsVisible(true);
    _view.SetViewReadOnlyMode(true);
}
private void LoadCurrentCustomerOnView()
{
    _view.ShowCustomer(Controller.CurrentCustomer);
} 

To facilitate the implementation of the Model-View-Presenter pattern, the contact details user control uses an ObjectContainerDataSource control. The method ShowCustomer implemented in the contact details view sets the data source’s DataSource property to the customer passed by the presenter, as shown in the following code.

public void ShowCustomer(Customer customer)
{
    CustomerDataSource.DataSource = customer;
} 

Communication Between Views

The contacts list view contains a contact details view. When the user selects a contact in the contacts list, the contact details view displays the selected contact’s information details. To enable this behavior, both views need to interact with each other.
In the QuickStarts, both views share the same instance of a controller object (of type ContactsController) that coordinates the interaction of the views. The following code extracted from the ContactsController shows the SetSelectedContactIndex method implementation. This method is invoked by the contacts list view’s presenter when the user selects a contact. When this method is called, the contact details view is notified of the selection change because the CurrentCustomerChanged event, to which the contact details view is subscribed, is raised.

public void SetSelectedContactIndex(int selectedContactIndex)
{
    _selectedContactIndex = selectedContactIndex;
    OnCurrentCustomerChanged(new EventArgs());
} 

The contacts list page is responsible for setting the ContactsController instance to the contact details view’s presenter when it is loaded. The following code extracted from the contacts list page shows the ContactDetail1_method which is invoked when the contact details user control is loaded. This method sets the controller object to the contact details view’s presenter.

void ContactDetail1_UserControlLoaded(object sender, EventArgs e)
{
    this.ContactDetail1.Presenter.Controller = _presenter.Controller;
} 

Model-View-Presenter pattern description | How to: Implement the Model-View-Presenter Pattern | How to: Unit Test the Presenter

Last edited Dec 5, 2007 at 12:56 PM by ejadib, version 2

Comments

devkhinchi Sep 4, 2010 at 9:22 AM 
i have faced this error when i open MVPQuickstart.sln file


Microsoft Visual Studio
---------------------------
The selected file is a solution file, but was created by a newer version of this application and cannot be opened.

devkhinchi Sep 4, 2010 at 9:21 AM 
problem to Open the solution file MVPQuickstart.sln (Model-View-Presenter QuickStart)