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:
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 using the aforementioned application blocks, see the Model-View-Presenter with Composite Web Application Block QuickStart.
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).
Figure 1
Contacts page
The QuickStarts ship as source code, which means you must compile them before running them.
To build and run the Model-View-Presenter QuickStart
Extract the source code for the QuickStart. To do this, run the file WebClientFactorySourceInstall.msi.
Open the solution file MVPQuickstart (VS2005).sln or MVPQuickstart (VS2005 with VSTS Tests).sln (this solution includes unit tests.)
On the Build menu, click Rebuild Solution.
Press F5 to run the QuickStart.
To build and run the Model-View-Presenter with Composite Web Application Block QuickStart
Extract the source code for the QuickStart. To do this, run the file WebClientFactorySourceInstall.msi.
Open the solution file MVPWithCWABQuickstart (VS2005).sln or MVPWithCWABQuickStart (VS2005 with VSTS Tests).sln (this solution includes unit tests.)
On the Build menu, click Rebuild Solution.
Press F5 to run the QuickStart.
This guidance includes separate solutions that include 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. For more information about WatiN, including download information, see http://watin.sourceforge.net.
To run the Model-View-Presenter QuickStart acceptance tests
Open the solution file MVPQuickstart(VS2005)_FunctionalTests.sln.
Fix the references to Interop.SHDocVw.dll, Rhino.Mocks.dll, and WatiN.Core.dll assemblies in the MVPQuickStart(VS2005)_FunctionalTests project. To do this, remove the references from the project and add them again.
Run the tests using the Test Manager.
To run the Model-View-Presenter with Composite Web Application Block acceptance tests
Open the solution file MVPWithCWABQuickStart (VS2005)_FunctionalTests.sln.
Fix the references to Interop.SHDocVw.dll, Rhino.Mocks.dll, and WatiN.Core.dll assemblies in the MVPWithCWABQuickStart (VS2005)_FunctionalTests project. To do this, remove the references from the project and add them again.
Run the tests using the Test Manager.
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 (VS2005).sln (Model-View-Presenter QuickStart) or MVPWithCWABQuickStart (VS2005).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.
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.
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.
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.
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.
Figure 5
Views and presenter files in the Model-View-Presenter QuickStart solution
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(); }
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; }
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(); }
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; }
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; }
Enjoy..!! :)