Model-View-Presenter

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies.
This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

Note

This topic is duplicated in the documentation so that you can read all the pertinent information within a single section.

The Web Client Software Factory 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 Composite Web Application Block.

Note

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 them to demonstrate the basic elements of a Model-View-Presenter pattern implementation. For an example of a Model-View-Presenter implementation that uses these 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).

Ff709887.c3b6e223-bc41-47da-af59-5ffe014467b9(en-us,PandP.10).png

Figure 1

Contacts page

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. Download and extract the source code.
  2. Open the solution file MVPQuickstart (VSTS Tests).sln (this solution includes unit tests.)
  3. On the Build menu, click Rebuild Solution.
  4. Press F5 to run the QuickStart.

To build and run the Model-View-Presenter with Composite Web Application Block QuickStart

  1. Download and extract the source code.
  2. Open the solution file MVPWithCWABQuickStart (VSTS Tests).sln (this solution includes unit tests.)
  3. On the Build menu, click Rebuild Solution.
  4. Press F5 to run the QuickStart.

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

  1. Open the solution file MVPQuickstart (VSTS Tests).sln (Model-View-Presenter QuickStart) or MVPWithCWABQuickStart (VSTS Tests).sln (Model-View-Presenter with Composite Web Application Block QuickStart).

  2. On the Build menu, click Rebuild Solution.

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

    Ff709887.0626a07a-801e-4435-8905-f38844d9f8a7(en-us,PandP.10).png

    Figure 2

    Contacts list

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

    Ff709887.68c589aa-1fb4-4dd0-b55a-ddc0aa3241cc(en-us,PandP.10).png

    Figure 3

    The contact details are displayed in a form

  5. Click the Edit button to edit the contact’s details. The form will be displayed in edit mode, as shown in Figure 4.

    Ff709887.ec30f729-741e-40d3-a3b8-acad05146524(en-us,PandP.10).png

    Figure 4

    Contact details form shown in edit mode

  6. Modify the contact’s details, and then click Save. The changes will be reflected in the contacts list.

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 view and presenter. The contacts list view 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 about 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.

Ff709887.06add4c4-4ae0-4e83-9866-be91accf212f(en-us,PandP.10).png

Figure 5

View files in the Model-View-Presenter QuickStart solution

Handling User Events

Views do not contain code to handle user interface events that are trigged by user gestures. Instead, views notify their presenters either through events or with direct method calls. 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 contact’s 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 that interface instead of the view’s concrete implementation. This allows you to replace the actual 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 have 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)

{

//Since we are not using CWAB we need to wire the MVP pattern manually

_presenter = new ContactsListPresenter(this, new ContactsController(new CustomersDataSource()));

this.ContactDetail1.UserControlLoaded += new EventHandler(ContactDetail1_UserControlLoaded);

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 Presenter’s Controller property when it is loaded. The following code extracted from the contacts list page shows the ContactDetail1_UserControlLoaded method, which is invoked when the contact details user control is loaded. This method sets the controller object to the Contact Details Presenter’s Controller property.

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