This is the second in a series of three articles that looks at the built-in support for standard design patterns in ASP.NET, and how you can implement common patterns in your own applications.
In our previous article, we saw ASP.NET automatically implements the Model-View-Presenter pattern when you use the built-in "code-behind" approach. However, there are other patterns usually referred to as "controller patterns" that are suitable for use in ASP.NET, and this is the subject of this article. These patterns extend the capability for displaying a single view by allowing the application to choose which view to display at runtime depending (usually) on user interaction.
The Use Case Controller pattern coordinates and sequences interaction between the system and its users in order to carry out a specific process. An example is the "Wizard" style of interface, where the user steps through a sequence of screens in a defined order. They may be able to go backwards as well as forwards, or jump to a specific step, but the overall approach suggests a defined "forward movement" through the process (see Figure 1).
Figure 1 - The Use Case Controller Pattern
A single Controller interacts with the Model (the data) and displays the appropriate View, depending on the user's interaction or the application's requirements. It is possible to incorporate multiple Controllers in a hierarchy for complex processes, and use configuration files or other persisted data to define the steps to allow easy maintenance and extension to the overall process. However, the underlying principles are as shown in Figure 1.
Two other design patterns related to Use Case Controller are the Page Controller and Front Controller patterns. These provide for implementation and extension of the principles of the Use Case Controller pattern to suit ASP.NET. They allow an application to either select the content to display in a page using different partial Views, or select which View (page) to display (see Figure 2).
Figure 2 - The Page Controller and Front Controller Patterns
In the Page Controller pattern, the controller uses a single Presenter (part of the MVP pattern implemented by the ASP.NET code-behind technology), which interacts with the Model (the data for the page). When it receives a request, the Page Controller can determine which partial View to display within the page, and then interact with that View following the MVP pattern.
In the Front Controller pattern, a separate controller examines each request and determines which page to display. Each page is a complete MVP implementation, with its own View, and each Presenter interacts with the View and the Model (the data).
A series of related design patterns define how applications can use additional components or modules to extend their capabilities or perform specific functions. Typically, they allow the features or behavior of an application to change when it loads separate components that implement extra functionality
The Plug-in pattern usually relates to general-purpose software components such as ActiveX controls orJava applets. An example is the way that Web browsers can use ActiveX controls, Java applets, and the Macromedia plug-in to provide extra functionality or display Flash animations.
The Module pattern usually relates to the capability of an application to load and use custom assemblies (modules) that extend its functionality. An example is the Composite UI Application Block (CAB), which contains a feature that allows an application to load a complete interface component (such as a window with its associated Controller or Presenter and data), and integrate it seamlessly into a composite Windows Forms interface.
The Intercepting Filter pattern is slightly different in implementation in that it usually consists of a component that resides within an application pipeline, such as the HTTP pipeline that ASP.NET uses to process each request. The framework calls a method within the HTTP module at a specific point during pipeline processing, allowing the module to change the behavior of the application.
You can implement both the Page Controller and the Front Controller patterns in ASP.NET. In fact, ASP.NET makes it easy to combine them if required, as shown in Figure 3.
Figure 3 - Combining Front Controller and Page Controller
In this schematic, the Front Controller specifies which of three Web pages will load depending on some feature of the request. Each Web page implements the MVP pattern, though one of them also uses the Page Controller pattern to determine which partial View to display within its page. This is, in fact, the overall structure of the sample application that you can download from here to experiment with on your own server.
All three of the patterns you will see implemented here, the Use Case Controller, Page Controller, and Front Controller, make use of the same set of three partial Views implemented as User Controls. Therefore, before looking at the pattern implementations in detail, the next section examines these three User Controls.
In order to illustrate the various controller patterns, the example application uses three very simple User Controls that implement Views for several of the pages. Figure 4 shows the page from the Use Case Controller pattern example that uses the "CustomerList" User Control (CustomerList.ascx). Note that the page heading, buttons, and actual URL are part of the hosting page, and not part of the User Control.
Figure 4 - The Customer List User Control displayed in the example application
The View itself (the ASPX page) contains just a GridView control and a Label control (where any errors messages will appear). The code to populate the GridView is in the Page_Load event handler. It uses the CustomerModel class to get a DataSet containing the list of customers, and binds this DataSet to the GridView to show the results:
The second User Control, named "CustomerDetails" (CustomerDetails.ascx), displays details of a specific customer (see Figure 5). To simplify the example, this is hard-coded to show the details of the customer with ID value "ALFKI", but you could - of course - modify the page to allow users to enter a customer ID in the same way as in the Default.aspx page you saw earlier.
Figure 5 - The Customer Details User Control displayed in the example application
The code in the Page_Load event handler of this User Control uses the GetCustomerDetails method of the CustomerModel class to get a DataRow containing details of the specified customer, converts this to an Object array, and then iterates through the array displaying the values:
The third User Control, named "CityList" (CityList.ascx), displays a list of the cities where customers reside, together with a count of the number of customers in each city (see Figure 6).
Figure 6 - The City List User Control displayed in the example application
The code in the Page_Load event handler of the "CityList" User Control uses the GetCityList method of the CustomerModel class to get a SortedList instance (a class in the System.Collections namespace) containing the list of cities and the count of customers in each one. It can then iterate through the list displaying the key (the city name) and the value (the number of customers) for each item:
The example implementation of the Use Case Controller pattern uses a single ASPX page named TransferPage1.aspx, with a code-behind file that implements the Presenter. If this page load is a postback (caused by the user clicking one of the buttons in the page), code in the Page_Load event of the Presenter extracts the name of the partial View (User Control) to display from the viewstate that ASP.NET maintains in every page, and saves this in the local variable named viewName. If this page load is not a postback, the code just sets viewName to the default value "CustomerList" and calls the method LoadAndDisplayView within the Presenter:
The remaining code in the Page_Load event displays the actual URL of the current page and any query string, so that you can see the effects of the Front Controller when it redirects requests to different pages. You will see how the Front Controller works in the next article in this series.
To load and display a User Control dynamically, code in the LoadAndDisplayView method creates a new instance of the control and then adds it to the Controls collection of an ASP.NET Placeholder control located in the main View (the ASPX page). After displaying the User Control, the code sets the Enabled properties of the "Back" and "Next" buttons, depending on the current view name, and displays the name of the view in the page header element (a
<div> control with the runat="server" attribute). Finally, it saves the name of the view in the page viewstate ready for the next postback:
As an alternative, you could use the Server.Execute method to execute separate ASPX pages, each an MVP pattern implementation with its own Presenter (code-behind file) and View (ASPX page) that generates the appropriate output. This output will appear in the resulting page as a partial View, though you must remember not to include the , , and elements in the partial View implementation.
However, it is likely that you will have to include the ASP.NET