Share via


Design and Implementation Guidelines for Web Clients

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.

 

patterns & practices Developer Center

Microsoft Corporation

November 2003

Applies to:
   Microsoft .NET Framework
   ASP.NET

Summary: Describes how to separate the responsibilities of components in the presentation layers by using common design patterns. Chapter 2 introduces a template implementation of the key design patterns. The template is inlcuded in the User Interface Process Application Block. This chapter also describes how to use this block as the basis for your own interface code, thereby realizing the benefits described in the previous paragraph.

Contents

In This Chapter

Benefits of Using Design Patterns

Using Design Patterns for the Presentation Layer

Implementing Design Patterns by Using the User Interface Process Application Block

Summary

In This Chapter

This chapter describes how to use presentation layer design patterns to solve common problems in the user interface. It also describes how to use the Microsoft User Interface Process Application Block to implement user interface process components in your application. The chapter includes the following sections:

  • Benefits of Using Design Patterns
  • Using Design Patterns for the Presentation Layer
  • Implementing Design Patterns by Using the User Interface Process Application Block

This chapter describes how to apply design patterns in the presentation layer. The design patterns help you to improve the quality of your implementation code by factoring and organizing components in your presentation layer. Additionally, the design patterns help to increase developer productivity through component reuse and to improve the maintainability of your code. The design patterns also help you identify the components that are affected when making decisions about state management, data access, asynchronous programming, and other areas covered in later chapters in this guide.

Benefits of Using Design Patterns

Most computing problems you will encounter in business applications have already been confronted and solved by someone, somewhere. Design patterns and reusable frameworks based on these solutions help you to overcome the complexity that exists in large applications. The following is a brief summary of the benefits of design patterns and reusable components:

  • Design patterns–Design patterns provide access to proven methodologies for solving general problems and the ability to use the collective knowledge and experience of the IT community to improve the quality of your own applications. You can use patterns to help you organize code in proven ways to solve well-understood problems.

    There are many proven design patterns that help you solve problems relevant to the presentation layers. Choosing the correct patterns leads to more maintainable code that separates unrelated tasks and functionality. Using these patterns leads to better modularity, higher cohesion, and lower coupling in your application; these are essential characteristics of well-designed systems. The design patterns described in this chapter apply to both Windows Forms-based user interfaces and ASP.NET Web pages.

  • Reusable components–Reusable components encapsulate functionality that is common across many applications and increase productivity when building your own components following a certain set of design patterns.

    The Microsoft User Interface Process Application Block is a reusable component that helps you build user interfaces based on the Model-View-Controller (MVC) and Application Controller patterns. This block simplifies navigation between related pages or forms in the user interface, and it also lets you take a snapshot of the current state in the application so the user can resume the application at the same stage later. Additionally, the block enables you to get a clean separation between the code that handles user interactions and renders the user interface and the code that performs ancillary tasks; this approach allows you to use the same programming model for Windows Forms applications, Web Forms applications, and mobile applications.

The following section describes how to use design patterns for the presentation layer. Reusable components are covered later in this chapter.

Using Design Patterns for the Presentation Layer

The presentation layer provides a rich source of well-documented and well-understood design patterns. The purpose of design patterns is to:

  • Document simple mechanisms that work
  • Provide a common vocabulary and taxonomy for developers and architects
  • Enable solutions to be described concisely as combinations of patterns
  • Enable reuse of architecture, design, and implementation decisions

Appropriate use of patterns reduces the design and development effort required to build your application. Additionally, the adoption of widely used patterns improves maintainability and reduces the risk that an early design decision will have consequences later in the development process or product lifecycle.

Poor design decisions in the presentation layer are particularly expensive and time consuming to resolve. You are most likely to notice poor design decisions when:

  • You have to support user interactions of increasing complexity, involving non-trivial relationships between forms and pages in the user interface.
  • Existing business processes change and you have to present new or modified functionality to your users.
  • You have to port your application to other platforms or make the application accessible to additional client types (such as mobile devices).

By basing your design on frequently used patterns, you can avoid many of the problems associated with these scenarios. The following sections describe the patterns that are applicable in the presentation layer and provide recommendations on when to use each design pattern.

Choosing Design Patterns

Use the following guidelines to help you select and use the appropriate design patterns for your presentation layers:

  • Read the following pattern descriptions, and then use Figure 2.1 to understand the reasons for using each design pattern.
  • Identify the patterns that address your particular requirements, and then study the design-level and implementation-level descriptions for these patterns.
  • Examine the sample implementation for the patterns that are relevant to your requirements. A sample implementation is available for each pattern; it shows how to apply the pattern for .NET Framework applications.
  • Evaluate using the User Interface Process Application Block to assist you in implementing the design patterns. For more information, see "Implementing Design Patterns by Using the User Interface Process Application Block" later in this chapter.

The appropriate use of patterns is not always straightforward; patterns provide general-purpose solutions that apply to many different problems. Knowing where to apply a particular pattern can be difficult, especially if the pattern description is particularly abstract or your system requirements documentation is weak.

Ultimately, you will have first-hand experience to help you identify the patterns that are most appropriate for your development team in a particular application scenario and environment. To reduce risk, you are advised to read about as many patterns as you can and experiment with their implementation in test scenarios before you use them on a real project.

Frequently Used Presentation Layer Patterns

The guide Enterprise Solution Patterns: Using Microsoft .NET on MSDN (https://msdn.microsoft.com/en-us/library/ms998469.aspx) describes how to use patterns in the architecture, design, and implementation of .NET Framework applications. Chapter 3 of the guide focuses on presentation layer patterns and describes patterns that are frequently used in presentation layer design.

The patterns that are most relevant to the presentation layer include:

  • Observer
  • Page Controller
  • Front Controller
  • Model-View-Controller (MVC)
  • Application Controller

There are some important differences between Web applications and rich-client applications that affect the relevance and suitability of these design patterns for each kind of application. The following section outlines these differences and describes how they influence your choice of design patterns for Web applications and rich-client applications.

Differences in Patterns for Web Applications and Rich-Client Applications

Some patterns that are related to Web presentation seem to be less relevant in the context of rich-client applications because of differences in the programming models exposed to developers, and how state is managed. For example:

  • Web applications receive HTTP requests that represent commands from the user. Some of the patterns listed earlier, such as the Front Controller pattern, describe how to direct, interpret, and execute these coarse-grained commands.

    ASP.NET uses, and allows you to implement, the basic patterns for mapping HTTP requests to events and functions in your page components.

    The stateless nature of HTTP, and the fact that a Web application is shared across many users, means you have to think about how and where state is managed across requests. The Application Controller pattern describes how to get state management in Web applications.

  • Rich-client applications are generally built on platforms based on in-memory message pumps, such as the message pump built into the Windows operating system. Messages typically represent low-level user interactions such as button clicks and keyboard entry, and are much finer-grained than Web requests.

    .NET Windows Forms encapsulates the underlying message–handling mechanism, and maps Windows messages to 'events' on your form components. It is also possible for business applications to intercept messages and treat them as "business requests," but this approach is uncommon and not recommended.

    Rich-client applications are inherently stateful and typically involve interactions with a single user. These two factors frequently mislead designers and developers into treating state management as an afterthought. This can make it difficult to share information between forms and users. The design patterns listed earlier prescribe effective mechanisms for sharing state in rich-client applications.

Driving Factors for Choosing Patterns

Each of the patterns listed earlier in this chapter has its own particular strengths and considerations for specific scenarios.

The main factor for choosing among these patterns is to identify how to apportion responsibilities across the various classes and components in the presentation layer. The risk of using a pattern too complicated for the application, team, and environment can be higher complexity and lower productivity.

Use Figure 2.1 to walk your way through the following descriptions of patterns and determine applicability and effort required.

Ff647343.f02diforwc01(en-us,PandP.10).gif

Figure 2.1. Choosing patterns for the presentation layer in .NET Framework applications

The following sections describe each of the patterns shown in Figure 2.1. The information for each pattern is organized as follows:

  • Problem–A brief description of the kind of problem that the pattern solves
  • Solution–An explanation of how the pattern resolves the stated problem
  • When to use the pattern–Specific scenarios where the pattern is typically applied
  • Class diagram–A Unified Modeling Language (UML) class diagram that shows the participating classes and interfaces for the pattern
  • How to use this pattern–Step-by-step guidance on how to use the pattern
  • References–Links to further information and samples

This information helps you decide when and how to apply the appropriate design pattern in particular scenarios.

Using the Observer Pattern

The Observer pattern provides a mechanism for one object to notify other objects of state changes without being dependent on those other objects.

Problem: You have a simple scenario where you want to react to user actions such as mouse clicks and keyboard entry. In response to these actions, logic executes in your application.

Solution: Use the ASP.NET code-behind class or Windows Forms events to react to user actions and input, and process data accordingly.

When to use the pattern: Use the Observer pattern when you have simple user interfaces where state is not shared across interactions, and where use cases do not drive the flow of control across multiple pages or forms.

Class diagram: Figure 2.2 shows the classes and interfaces that participate in the Observer pattern.

Ff647343.f02diforwc02(en-us,PandP.10).gif

Figure 2.2. The Observer pattern

To use the Observer pattern to receive events from user interface components

  1. Decide the user actions or UI events that you have to react to in your application.
  2. Evaluate whether you want to use the event handlers provided by ASP.NET pages or Windows Form controls (you typically get these by double-clicking the control in the Microsoft Visual Studio® .NET development system) or add your own handlers.

The following are recommendations for implementing the Observer pattern in the .NET Framework presentation layer code:

  • Use Visual Studio .NET support and default handlers as much as possible. For example, to define event handlers in Microsoft Visual C# ® development tool projects, use the Events list in the Properties window in Visual Studio .NET. To define event handlers in Microsoft Visual Basic® .NET development system projects, use the Class Name and Method Name list boxes at the top of the code editor window.
  • Use standard .NET Framework naming conventions for event handler method names and for any custom delegates and events that you define yourself.
  • Do not invoke event handlers from other event handler methods. This quickly leads to unreadable code where it is unclear what triggers what.
  • Do not link the default event handlers for specific events to events from other controls or actions. Use your own event handler to make this aggregation clear. For example, do not use the same event handler method to handle both the Click and DoubleClick events for a Label control.
  • Use the += syntax to register event handlers in Visual C# code. Use the Handlescontrolname.EventName syntax at the end of event handler method signatures in Visual Basic .NET.

References

Using the Page Controller Pattern

The Page Controller pattern is a variation on the Model-View-Controller (MVC) pattern (described later in this section). The Page Controller pattern is particularly suitable in thin-client applications, where the view and controller are separated; presentation occurs in a client browser, whereas the controller is part of the server-side application.

Problem: You want to separate business layers from the presentation logic in a Web application. You also want to structure the controller components in such a way that you gain reuse and flexibility, while avoiding code duplication between the controller component for each Web page.

Solution: Use a Page Controller component to accept input from the page request, invoke the requested actions on the model, and determine the correct view to use for the resulting page. The Page Controller enables you to separate the dispatching logic from any view-related code.

When you create an ASP.NET Web application using Visual Studio .NET, a Page Controller component is provided automatically for each Web page.

When to use the pattern: Use the Page Controller pattern in Web applications that have simple user interfaces and navigational paths between pages. In these scenarios, the logic in the Page Controller component is simple, and you do not need centralized navigation control or to share state across user interface components.

Class diagram: Figure 2.3 shows the classes that participate in the Page Controller pattern.

Ff647343.f02diforwc03(en-us,PandP.10).gif

Figure 2.3. The Page Controller pattern

To use the Page Controller pattern in your presentation layer

  1. Identify the controller components that you require. This pattern helps mostly with a one-to-one mapping between controller components and Web pages, so typically there will be one controller component per page. Because each Web page is handled by a specific controller, the controllers have to deal with only a limited scope and can remain simple.

  2. Evaluate whether you want to write controller code in the ASP.NET code-behind class for the page or in a separate class. The drawback of a separate class is that you will have to access ASP.NET context information by using the HttpContext.Current property; this returns an HttpContext object that encapsulates all HTTP-specific information about the current HTTP request.

    // Get HTTP-specific information about the current HTTP request
    HttpContext context = HttpContext.Current;
    
    

The following are recommendations for implementing the Page Controller pattern in ASP.NET applications:

  • By default, ASP.NET implements the Page Controller pattern for your application. Every page you create with Visual Studio .NET automatically receives a controller class. This is specified in the @ Page tag in the ASPX file.

    <%@ Page language="c#" 
             Codebehind="SimplePage.aspx.cs" 
             AutoEventWireup="false" 
             Inherits="SimplePage" %>
    
    

When the user navigates to a URL for an ASP.NET Web page, the Web server analyzes the URL associated with the link and executes the associated ASP.NET page. In effect, the ASP.NET page is the controller for the action taken by the user. The ASP.NET page framework also provides code-behind classes to run controller code. Code-behind classes provide better separation between the controller and the view and also allow you to create a controller base class that incorporates common functionality across all controllers.

  • For information about sharing a controller across multiple pages, see Chapter 3, "Building Maintainable Web Interfaces with ASP.NET," in this guide.

References

Using the Front Controller Pattern

The Front Controller pattern is a variation of the Page Controller pattern. The Page Controller pattern associates a separate controller class with each Web page. It is suitable for simple Web applications where the navigation between Web pages is straightforward. In contrast, the Front Controller pattern is applicable where the page controller classes have complicated logic, are part of a deep inheritance hierarchy, or your application determines the navigation between pages dynamically based on configurable rules.

Problem: You have to enforce consistency on how and where a user's actions and input are handled. You might also have to perform other tasks consistently in several Web pages, such as data retrieval and security checks.

You can reach these goals by using the Page Controller pattern and writing the same code in each code-behind page. However, this approach leads to code duplication in the code-behind classes, and therefore leads to maintenance difficulties in your code.

Alternatively, you can use the Page Controller pattern and create a base class for behavior shared among individual pages. However, this approach can result in complex and inflexible inheritance hierarchies when the number of pages in your application grows.

Solution: Have multiple requests channeled through a single controller class. The controller class provides a centralized location for shared logic and also determines how to transfer control to the appropriate view.

When to use this pattern: Use the Front Controller pattern in Web applications where you want to enforce shared behavior across several pages, or where the flow of control between Web pages is non-trivial.

Class diagram: Figure 2.4 shows the classes and interfaces that participate in the Front Controller pattern.

Ff647343.f02diforwc04(en-us,PandP.10).gif

Figure 2.4. The Front Controller pattern

To use the Front Controller pattern in Web applications

  1. Identify the commands that you want to handle in a similar fashion across multiple pages. This similarity may be driven by the type of functional work the request invokes, such as updating data in a database or running business components. Alternatively, the similarity might be driven by the policies or aspects associated with the work that will be completed, such as instrumentation, auditing, or authorization.
  2. Create a handler class that receives the HTTP Post or Get request from the Web server and retrieves relevant parameters from the request. The handler class uses these parameters to run the appropriate command to handle the request. A common way of implementing this is by using an ASP.NET HttpHandler. A code example of how to do this is provided in the "References" section later in this section. The handler should be as efficient as possible and use external resources only when absolutely necessary. You should also consider caching any external resources to increase the handler's performance.

The following are recommendations for implementing the Front Controller pattern in ASP.NET pages:

  • Use the Front Controller pattern to enforce similar behavior between Web pages but do not try to force the whole application into a single front controller class because this can lead to performance issues.
  • If you use the Front Controller pattern to service user interface requests and render the user interface back to the user, you might have to update the URL through a redirect. Otherwise, the whole application might end up having a single URL, and hyperlinks would not work.
  • Carefully test performance of Front Controller implementations because a front controller adds execution overhead to every request it serves.

References

Using the Model-View-Controller (MVC) Pattern

The Model-View-Controller (MVC) pattern adds an intermediary to isolate your user interface logic from your business layers.

Problem: You have to separate business layers from the presentation logic. You also have to separate the logic that controls the state and behavior of the user interface from the logic that renders and acquires data from the user.

Solution: Separate the user interface into controllers and views. Use an intermediary to access appropriate business model elements the views require.

When to use the pattern: Use the Model-View-Controller pattern in applications with complex user interfaces that may display or acquire the same data in different ways in different views. This pattern is also appropriate in applications where strict separation of user interface layers and business layers is required.

Class diagram: Figure 2.5 shows the classes that participate in the MVC pattern.

Ff647343.f02diforwc05(en-us,PandP.10).gif

Figure 2.5. The Model-View-Controller(MVC) pattern

To use the MVC pattern in your presentation layer

  1. Identify the components that represent the model. Typically, this is a combination of business entities, business components, data access logic components, and service agents. For information about how to identify the components that apply to your requirements, see Chapter 4, "Managing Data," in this guide.
  2. Create the views you have to implement as user interface components, such as Windows Forms, ASP.NET pages, and controls. If you are not using the User Interface Process Application Block, you have to create and hold a reference to the controller object from the view object.
  3. Define a controller class and implement methods that the view can call when requesting data or starting an action. These methods will access the model components identified in Step 1.

The following are recommendations for implementing the MVC pattern in Windows Forms and ASP.NET applications:

  • Evaluate using the User Interface Process Application Block for your requirements. This block provides a template implementation of the MVC pattern for .NET Framework applications.
  • Restrict the view to read-only access to data in the model. If any changes are required in the data, these changes should be performed by the controller instead of by the view. This constraint simplifies application logic by reducing direct coupling between the model and the view.
  • Raise model-related events to signify important state changes. This is particularly appropriate in Windows Forms-based applications, where multiple views display data from the same model; the model-related events enable the views to update the data that appears to the user. By raising events in the model, instead of calling methods directly on the views, you preserve the low coupling between the model and the views.
  • Implement controllers in a platform-agnostic manner to increase the portability of the controllers regardless of the kinds of views that interact with them. For example, avoid writing ASP.NET-specific or Windows Forms-specific code in the controller class.
  • Unit-test the controller classes. Controllers can be easily unit tested, because they expose programmatic functionality instead of providing a user interface.

References

Using the Application Controller Pattern

The Application Controller pattern manages multiple user interactions by enforcing control flow, providing state management capabilities, and relating views to specific controller classes.

Problem: You have to enforce the flow of control between different views and gain state management across these views.

You can reach these goals by embedding code in the controller and view classes, but this would make it difficult to change navigation and add additional controllers and views in the future.

Solution: Create a separate Application Controller class to control flow through controller logic and views.

When to use the pattern: Use the Application Controller pattern in applications with deterministic flow between views.

Class diagram: Figure 2.6 shows the classes and interfaces that participate in the Application Controller pattern.

Ff647343.f02diforwc06(en-us,PandP.10).gif

Figure 2.6. The Application Controller pattern

To use the Application Controller pattern in your presentation layer

  1. Identify a distinct use case in your application. The use case defines a set of related user activities to fulfil a particular task. Identify the views that participate in the user case and the controllers that are required for the interaction.
  2. If you are using the User Interface Process Application Block, configure a navigation graph for each use case; a navigation graph defines the flow between views in the use case. For more information about navigation graphs, see the procedure "To configure the User Interface Process Application Block" later in this chapter.

The following are recommendations for implementing the Application Controller pattern in ASP.NET applications:

  • Use the User Interface Process Application Block to implement this pattern in your application. Application Controller functionality is not simple to design or write, especially if you require portability across platforms, if you have to support multiple view types, or if you require state management across the views.
  • If you cannot use the User Interface Process Application Block directly in your application, review the documentation and samples that accompany the block; base your design and implementation on the block, where possible.

References

Implementing Design Patterns by Using the User Interface Process Application Block

After you choose the appropriate design patterns for the presentation layer in your application, the next step is to implement the patterns in code.

The .NET Framework, especially ASP.NET and Windows Forms, provide programming models that implicitly support all the patterns described earlier, except for the Model-View-Controller (MVC) and Application Controller patterns. If you want to use either of these patterns, use the Microsoft User Interface Process Application Block to help you implement these patterns in your application.

The primary goal of most design patterns in the presentation layer is to get a clean separation between the code that renders the user interface and accepts user interactions, and the code that deals with background tasks such as view navigation and state management. Design patterns reinforce the following good practices:

  • Do not implement user interface navigation and workflow as part of the user interface views. Otherwise, code becomes complex and difficult to maintain and extend; each piece of user interface code is tightly linked to the steps before and after it, so a small business process change might result in major re-development and re-testing.
  • Do not manipulate application and session state directly in the view. Otherwise, code becomes unwieldy because each view is responsible for handing off state to the next step in the application workflow.

To avoid the preceding problems, you should separate your presentation layer into user interface components and user interface process components. User interface components and user interface process components perform distinct tasks:

  • User interface components provide the forms and controls that the user interacts with.
  • User interface process components synchronize and orchestrate the user interaction with the application, to provide the navigation and workflow required to support the business processes.

For a more detailed discussion of the context of user interface components and user interface process components, see Application Architecture for .NET: Designing Applications and Services on MSDN (https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/distapp.asp).

The User Interface Process Application Block is a flexible, general-purpose, reusable component that you can use in your applications to implement user processes. The block enables you to write complex user interface navigation and workflow processes that you can reuse and that are easy to extend as your application evolves.

  • This section includes the following topics:
  • Design of the User Interface Process Application Block
  • Benefits of Using the User Interface Process Application Block
  • Building Applications with the User Interface Process Application Block

The User Interface Process Application Block download includes comprehensive documentation on how the block is designed and how to use the block in your applications. The download also includes a sample "shopping cart" ASP.NET Web application that illustrates how to use the block to simplify navigational flow and state management in the presentation layer.

Design of the User Interface Process Application Block

The User Interface Process Application Block helps you build applications based on the Model-View-Controller (MVC) pattern, to get a strict separation between the code that renders the user interface and responds to user interactions, and the code that implements the underlying business logic. The block also implements the Application Controller pattern because it handles control flow and state management between views.

The User Interface Process Application Block has been designed with reusability and extension in mind, to enable you to create controllers that can be used across multiple view types including Windows Forms and ASP.NET applications.

Figure 2.7 shows the major components in the User Interface Process Application Block.

Ff647343.f02diforwc07(en-us,PandP.10).gif

Figure 2.7. Components in the User Interface Process Application Block

The components in the User Interface Process Application Block perform the following tasks:

  • UIPManager–Manages the user interface process. Dispenses controllers to views, generates new views, coordinates tasks, and provides workflow-based navigation.
  • ControllerBase–Abstract base class, equivalent to the controller class in the MVC pattern. Its primary purpose is to control the navigation between views, and to act as a façade between the user interface layer and the business layer.
  • State–Class that represents the state shared across steps in a task. Implements a dictionary-like interface to hold state values. You can inherit from this class to share strong-typed state objects across controllers in a task.
  • SqlServerStatePersistence, SecureSqlServerStatePersistence, MemoryStatePersistence, and SessionStatePersistence–These plug-ins manage storing and loading state from multiple places.
  • WinFormView–Base class for a form in a Windows Forms application. You inherit from this class when developing your Windows forms.
  • WinFormViewManager–Plug-in that the UIPManager uses to transition the user between Windows forms.
  • WebFormView–Base class for a form in an ASP.NET Web application. You inherit from this class when developing your Web forms.
  • WebFormViewManager–Plug-in that the UIPManager uses to transition the user between Web forms.

The User Interface Process Application Block exposes degrees of freedom in its implementation. There are many ways to extend it, but the common ones are:

  • State type–You can create strongly-typed state types, by inheriting from the State class.
  • State location–You can store shared state in different places, by implementing the IStatePersistence interface. You can write methods to store data held in a State object to a durable medium and to retrieve that state when reconstructing a State object.
  • Views–You can assign custom controllers for many component types by implementing the IView interface.
  • View transition–You can handle different styles of visual transition across views by implementing the IViewManager interface.

For more information about how to use and customize the block, search for the topics "Customizing State Management" and "Customizing Views and View Management" in the documentation provided with the block.

Benefits of Using the User Interface Process Application Block

The User Interface Process Application Block helps you to:

  • Abstract the workflow and navigation from the user interface layer
  • Abstract state management from the user interface layer
  • Use the same user interface programming model for different types of applications, including Windows-based applications, Web applications, and device applications
  • Introduce the concept of a task: a snapshot of an interaction in a use case that can be persisted

Each of these points is described more fully in the following sections. For an example of how to use the block, see "Building Applications with the User Interface Process Application Block" later in this chapter.

Separating Workflow from the User Interface

Workflow is a decision process that results in flowing control, either between Web pages, Windows Forms, or mobile phone screens. These decisions generally depend on the result of tasks performed by back-end systems. For example, some possible actions resulting from trying to authorize a customer's credit card are:

  • Ask the customer to use another card because the authorization failed.
  • Show the customer the "Thank You" page and ask them if they want to continue to shop.

If a developer writes all the decision-making code and page-redirection logic in the user interface component (for example, in an ASP.NET code-behind class), the developer creates a brittle system. By allowing user interface components to know about each other, they are inextricably linked. In a 10-page application, this is not a huge liability. However, in a 500-page application, this can become a major problem that makes your application difficult to maintain and extend.

The User Interface Process Application Block helps you separate workflow from your user interface component in the following ways:

  • The block uses navigation graphs—expressed as XML documents—to define reusable workflow definitions. The navigation graphs and other configuration settings dictate how the workflow proceeds.
  • The block delegates all workflow responsibilities to two components: your controller class and the standard UIPManager class. The controller and the UIPManager cooperate as follows, to control the application workflow:
    • The controller decides the next step in the workflow based on business logic such as whether the customer credit card was authorized satisfactorily.
    • The UIPManager uses the results provided by the controller to determine the appropriate view to present the next step in the workflow to the user.

By abstracting workflow in this way, the block enables you to create extremely flexible presentation layers. Flexible presentation layers include views that are independent of each other, business logic that is centralized and reusable, and multiple output modalities that can share most of the same code except the actual user interface elements themselves.

Enhancing Portability

Many applications provide multiple user interfaces to support different client types. For example, an e-commerce application might offer a Web-based user interface to customers and a Windows-based user interfaces to administrative, sales, and support staff. Additionally, as applications evolve, you frequently have to add support for additional client types, such as Personal Digital Assistants (PDAs) and mobile phones.

Although you can make each user interface share common appearance and usage models, their implementation and delivery mechanisms differ significantly. However, despite the user interface differences, the business processes used by the consumers of the application are frequently common across all clients. The solution is to define a user interface process component, as shown in Figure 2.8.

Ff647343.f02diforwc08(en-us,PandP.10).gif

Figure 2.8. Supporting multiple user interfaces

The User Interface Process Application Block helps you build more portable applications in the following ways:

  • The block abstracts state management, allowing both Windows-based and Web applications to use the same state management mechanisms. When you migrate an application, you do not have to rework your state management mechanisms.
  • The block encourages views to never hold state, except state that might be directly involved in generating the user interface elements themselves.
  • The block manages state communication between views by centralizing state in a single entity.
  • The block delegates all business-logic and workflow control to the controller and UIPManager.

The block encapsulates all issues that are not directly related to rendering the user interface and intercepting user actions; these tasks are performed by the user interface components. If you have to migrate your application to a new platform, for example migrating a Windows Forms-based application to a Web application, all you have to re-implement is the user interface components; the code in the user interface process components can be reused without modification.

Abstracting State Management from the User Interface

In classic applications, state is frequently stored directly in the user interface component. This results in complex code to store, access, and manipulate that state. It also means that the state is dependent on the user interface type; this makes it difficult to transfer code between application types.

The following problems typically occur if you store state in the user interface component:

  • In Windows Forms applications, a form might have many member variables that hold state during a user interaction with that form. In most non-trivial scenarios, you have to access the state from other forms in the application. Developers have to write a great deal of extraneous code to capture and transfer that state, using one or all of the following techniques:
    • Externalize the state to a structure or stateful class
    • Use member variables in the business classes underlying the user interface
  • In Web applications, passing state between views is especially chaotic. For example:
    • Query strings produce dependencies between views. Both views must know the name of the passed item, and the underlying type of the information.
    • Hidden form fields introduce the same problem as query strings.
    • Cookies experience the same problem as query strings and hidden form fields; additionally, they are unreliable because some users completely deny cookies.
    • The Session, Application, and Cache types in the .NET Framework class library offer better type-preservation, but they also intimately tie the user interface to ASP.NET.

The User Interface Process Application Block simplifies state management by:

  • Allowing long-running user interaction state to be easily persisted, so that a user interaction can be abandoned and resumed, possibly even using a different user interface. For example, a customer can add some items to a shopping cart using the Web-based user interface, and then call a sales representative later to complete the order.
  • Centralizing user process state, making it easier to achieve consistency in user interfaces with multiple related elements or windows. Centralized user process state also makes it easier to allow users to perform multiple activities at the same time.
  • Abstracting state location and lifetime, to enhance the robustness of the user interface component; views know where to find state, but they do not have to know where state is stored or how it is passed to other views.
  • Providing the IStatePersistence interface for persisting State objects. The flexibility offered by separating persistence behavior from the State data means that developers are free to optimize persistence without affecting the consumers of State objects.

The preceding benefits illustrate how the User Interface Process Application Block can simplify the development of presentation layers that require user interface process functionality.

Building Applications with the User Interface Process Application Block

There are two steps to building applications with the User Interface Process Application Block:

  • Gather requirements for your application. The first step in this task is to separate the application into distinct use cases. For each use case, you must define the workflow and state management requirements.
  • Implement views and controller classes as required and write a configuration file to configure the User Interface Process Application Block for use in your application.

Gathering Requirements

The following steps help you identify the requirements for your application, and they help you decide how to use the User Interface Process Application Block to assist you in implementation:

  1. Separate the application into use cases.
  2. Design the navigation graph for each use case.
  3. Verify the existing functionality in the User Interface Process Application Block satisfies your requirements.
  4. Determine state persistence for each task.
  5. Design the controller classes.
  6. Design the views.

The following sections describe each of these steps.

Separate the Application into Use Cases

Applications are typically composed of many use cases. For example, an online retailing application might have use cases to purchase items, add stock to the inventory, and remove low-selling stock from the inventory.

Each use case involves a particular sequence of interactions with the user. Separating use cases correctly gives you better reusability, because your use cases will be encapsulated and you will almost be able to call them as a function.

Design the Navigation Graph for Each Use Case

Decide the control flow that each use case requires and draw a navigational graph that depicts the use case as a visual flow between views.

For example, Figure 2.9 shows a navigation graph for the UIProcessQuickstarts_Store sample application in the User Interface Process Application Block download.

Ff647343.f02diforwc09(en-us,PandP.10).gif

Figure 2.9. Sample navigation graph

The navigation graph in Figure 2.9 identifies logical views in the application, such as "browsecatalog" and "cart." The navigation graph also defines the navigational steps that cause a transition from one view to another. For example, the "addItem" navigational step causes a transition from the "browsecatalog" view to the "cart" view, and the "resume" navigational step causes a transition back again.

Verify that the Existing Functionality in the User Interface Process Application Block Satisfies your Requirements

The User Interface Process Application Block includes common implementations for state persistence, state type, views, and view managers. Review your requirements to evaluate whether the block provides sufficient functionality in the following areas:

  • State location (the block provides built-in support for storing state data in SQL Server, secure SQL Server, session, and memory)
  • State type (by default, the block uses loose-typed dictionary-like state storage)
  • View management (the block provides built-in support for Windows Forms navigation and Web forms navigation)

For more information about how to customize these aspects of behavior in the block, see the User Interface Process Application Block documentation.

Determine State Persistence for Each Task

In the terminology used in the User Interface Process Application Block documentation, a task is a running instance of a process. For example, a user can start a task for an "Add Payment Method" process. A task encapsulates all conversation state between the application and the user.

Each use case might have a different lifetime for its tasks. For example, you may want the user to be able to resume a task he or she started earlier in a different Web session or after closing a Windows application. You also have to determine whether the task can be associated with only a single user (for example, a checkout in a retail site) or multiple users (for example, a support call being transferred across members of a helpdesk team).

Table 2.1 describes the attributes of the four state persistence providers available in the User Interface Process Application Block. Use this table to help you decide the state persistence provider that best suits your requirements.

Table 2.1: Attributes of State Persistence Providers

State Provider Provider Flexibility in assigning a task to different users Ability for task to span session or application lifetime Supported for Windows-based applications Supported for Web applications
SQL Server x x x x
Secure SQL Server x x x x
Session       x
Memory     x  

Design the Controller Classes

The User Interface Process Application Block defines a reusable class named ControllerBase; this acts as the base class for your application-specific controller classes.

In your controller classes, you must write properties and methods that interact with the business layers to retrieve data, update data, and perform business-related processing. Declare some of these properties and methods public; this enables views to render data or pass updated data to the controller when necessary. Do not allow views to modify state directly, because this violates the MVC principle of separation between the model and the view.

For example, the UIProcessQuickstarts_Store sample application defines a StoreController class that has methods such as IsUserValid, AddToCart, and CheckoutOrder.

Design the Views

The final step is to design the views for your application. Typically, you implement each view as a Windows form or an ASP.NET Web form. Each view is responsible for displaying a particular user interface, retrieving data from a controller and rendering it on the form, and invoking other methods on the controller to instigate business-related processing.

Building Components

After you gather the requirements, implement the views and controllers that make up the use case and configure the User Interface Process Application Block to tie them together.

The first step is to add a reference to the block in your application.

To reference the User Interface Process Application Block

  1. Open Visual Studio .NET, and create a Windows-based application or ASP.NET Web application, as appropriate.

  2. In Solution Explorer, right-click your project, and then add a reference to the Microsoft.ApplicationBlocks.UIProcess.dll assembly. The location of this assembly depends on how you installed it.

    • If you installed the assembly in the global assembly cache, you can reference the assembly from this location.
    • Otherwise, you must reference the assembly from its installation folder, such as C:\Program Files\Microsoft Application Blocks for .NET\User Interface Process\Code\CS\Microsoft.ApplicationBlocks.UIProcess\bin\Debug.
  3. In each source file that uses classes from the block, add a using statement to reference the Microsoft.ApplicationBlocks.UIProcess namespace. All User Interface Process Application Block types are located in this namespace.

    using Microsoft.ApplicationBlocks.UIProcess;
    
    

To create a view

  1. If you are building a Windows-based application, view the source code for the Windows form class. In the form class definition, remove the base class System.Windows.Forms.Form and replace it with the WinFormView base class.

    public class MyWindowsForm : WinFormView
    
    

    If you are building an ASP.NET Web application, view the source code for the Web form class. In the form class definition, remove the base class System.Web.UI.Page and replace it with WebFormView.

    public class MyWebForm : WebFormView
    
    
  2. Create a private property "getter" to return a reference to the controller object for your application.

    Use the static Controller property, defined in the WinFormView or WebFormView base class, to access the controller object. Cast the controller object into the actual type of the controller class for your application.

    private StoreController StoreController
    {
      get{ return (StoreController)this.Controller; }
    }
    
    
  3. Add code to your view class to perform tasks such as retrieving data from the database, saving data to the database, and moving on to the next form in the process. These tasks are implemented by methods in the controller class; to instigate these tasks from the view, invoke the appropriate methods on this controller object.

    The following example invokes a method named AddToCart on the controller to add a quantity of a particular product to the user's shopping cart.

    StoreController.AddToCart(productID, quantity);
    
    
  4. Add code to your view class to retrieve state information from the controller object when required. The controller object has a public State property that enables you to get and set named items of state, and it facilitates state management for both Windows-based and Web applications.

    The following example gets the value of a state item named CustomerNameState.

    customerNameLbl.Text = StoreController.State["CustomerNameState"].ToString();
    
    

    The controller object encapsulates all implementation details about state management. The code in your view class is unconcerned about where the state comes from or where it is stored.

To create a controller class

  1. Create a new class using Visual Studio .NET.

  2. Make this class inherit from ControllerBase.

    public class StoreController : ControllerBase
    {
      // ...
    }
    
    
  3. Add a constructor that takes a State parameter and calls the corresponding constructor in the base class.

    public class StoreController : ControllerBase
    {
        public StoreController( State controllerState ) 
            : base( controllerState ){}
        //...
    }
    
    
  4. Write helper properties in your controller class to help state management.

    In the following example, the Cart property gets and sets a state item named CartState; it is a hash table containing the user's shopping cart.

    private Hashtable Cart
    {
      get
      {
        Hashtable cart = (Hashtable)State["CartState"];
        if (cart == null)
        {
          cart = new Hashtable();
          State["CartState"] = cart;
        }
        return cart;
      }
      set
      {
        State["CartState"] = value;
      }
    }
    
    
  5. Write public methods in your controller class to provide controlled access to state items.

    In the following example, the AddToCart method adds a quantity of a particular product to the user's shopping cart.

    public void AddToCart(int productID, int quantity)
    {
      if (Cart.Contains(productID))
      {
        // Product ID is already in cart, so just increment quantity
        int existingQuantity = (int)Cart[productID];
        Cart[productID] = existingQuantity + quantity;
      }
      else
      {
        // Product ID is not in cart, so insert it
        Cart[productID] = quantity;
      }
    }
    
    
  6. Write public methods in your controller class to change the current step in the user interface process. To change the current step, set the NavigateState property on the State object and call the Navigate method. The UIPManager checks its configuration to determine the view that is expected to show on that outcome, and then it uses the ViewManager of the current NavigationGraph to transition to that view.

    The following methods show how to stop and resume the current shopping task.

    public void StopShopping() 
    {    
      // Proceed to next view
      State.NavigateValue = "stop";
      Navigate();
    }
    
    public void ResumeShopping() 
    {
      // Proceed to next view
      State.NavigateValue = "resume";
      Navigate();
    }
    
    

    The following method shows how to complete checkout. The method validates the user's credit card details, and then it proceeds to the "failCheckout" or "successCheckout" navigation step accordingly.

    public void CompleteCheckout( string name, string addr, string ccnum )
    {
      if ( … some credit card validation code … )
      {
        State.NavigateValue = "failCheckout";
      }
      else
      {
        State.NavigateValue = "successCheckout";
      }
      Navigate();
    }
    
    

    In each of these examples, notice that the controller code does not contain any hard-coded links to specific views. Instead, the association between navigation steps and views is specified declaratively in the navigation graph. Therefore, the controller class has no direct dependencies on the view classes or on the sequence of views in each use case.

To configure the User Interface Process Application Block

This section describes how to configure the User Interface Process Application Block to define the flow of control between views, and to specify state management information in your application.

For complete information about how to configure the block, search for "Creating the Configuration File" in the documentation provided with the block.

  1. Open the configuration file for your application:

    • For ASP.NET Web applications, the configuration file is Web.config in your ASP.NET project.
    • For Windows Forms applications, create a configuration file named ApplicationName.exe.config in the same folder as your application executable file The configuration file must have a root element named <configuration>.
  2. In the configuration file, define a new configuration section in the <configSections> element as follows.

    <configuration>
      …
      <configSections>
        <section name="uipConfiguration"
                 type="Microsoft.ApplicationBlocks.UIProcess.UIPConfigHandler,
                       Microsoft.ApplicationBlocks.UIProcess" />
      </configSections>
      …
    </configuration>
    
    
  3. Create a new configuration section named <uipConfiguration>. This is used to define the classes to be used by the application for view management, state management, and controllers; the views available to the user; and the navigation graphs that define the workflow path through those views.

    The structure of the <uipConfiguration> section is defined in the UIPConfigSchema.xsd XML Schema document, which is included in the block. This schema specifies which elements and attributes are required and which are optional, how many times each may occur, and in what order they must appear. If your <uipConfiguration> section does not adhere to the schema, an exception will occur at run time.

    <configuration>
      …
      <uipConfiguration enableStateCache="true">
        <objectTypes>
          … identify the relevant classes in the application …
        </objectTypes>
        <views>
          … identify the views available to the user …
        </views>
        <navigationGraph>
          … define the navigation graphs …
        </navigationGraph>
      </uipConfiguration>
      …
    </configuration>
    
    
  4. Specify the details in the <objectTypes> element to identify the relevant classes in your application. You must provide the following information for <objectTypes>.

    <objectTypes>
    
      <iViewManager 
        name="name of view manager class"
        type="type of view manager class"/>
    
      <state 
        name="name of state management class"
        type="type of state management class"/>
    
      <controller
        name="name of controller class"
        type="type of controller class"/>
    
      <statePersistenceProvider
        name="name of state persistence provider class"
        type="type of state persistence provider class"
        connectionString="connection string to data source for state management"/>
    
    </objectTypes>
    
    

    The following example shows the <objectTypes> element in the configuration file for the UIProcessQuickstarts_Store sample application in the User Interface Process Application Block download.

    <objectTypes>
    
      <iViewManager 
        name="WebFormViewManager"
        type="Microsoft.ApplicationBlocks.UIProcess.WebFormViewManager, 
              Microsoft.ApplicationBlocks.UIProcess, Version=1.0.1.0,
              Culture=neutral, PublicKeyToken=null"/>
    
      <state 
        name="State" 
        type="Microsoft.ApplicationBlocks.UIProcess.State, 
              Microsoft.ApplicationBlocks.UIProcess, Version=1.0.1.0,
              Culture=neutral, PublicKeyToken=null"/>
    
      <controller 
        name="StoreController" 
        type="UIProcessQuickstarts_Store.StoreController, 
              UIProcessQuickstarts_Store.Common, Version=1.0.1.0,
              Culture=neutral, PublicKeyToken=null" />
    
      <controller 
        name="SurveyController" 
        type="UIProcessQuickstarts_Store.SurveyController,
              UIProcessQuickstarts_Store.Common, Version=1.0.1.0,
              Culture=neutral, PublicKeyToken=null" />
    
      <statePersistenceProvider 
        name="SqlServerPersistState"  
        type="Microsoft.ApplicationBlocks.UIProcess.SqlServerPersistState, 
              Microsoft.ApplicationBlocks.UIProcess, Version=1.0.1.0,
              Culture=neutral, PublicKeyToken=null"
        connectionString="Data Source=localhost;Initial Catalog=UIPState;
                          user id=UIP;password=U1Pr0c3ss"/>
    
    </objectTypes>
    
    
  5. Specify the details in the <views> element to identify the views available in your application. You must provide the following information for <views>.

    <views>
    
      <view 
        name="logical name of view"
        type="ASP.NET file for view"
        controller="name of controller class for this view"/>
    
      … plus additional <view> elements, for the other views in the application …
    
    </views>
    
    

    The following example shows the <views> element in the configuration file for the UIProcessQuickstarts_Store sample application in the User Interface Process Application Block download.

    <views>
    
      <view 
        name="cart" 
        type="cart.aspx" 
        controller="StoreController" />
    
      <view 
        name="browsecatalog" 
        type="browsecatalog.aspx" 
        controller="StoreController" />
    
      <view 
        name="error" 
        type="uipError.aspx" 
        controller="StoreController" />
    
      <view 
        name="congratulations" 
        type="congratulations.aspx" 
        controller="StoreController" />
    
      <view 
        name="checkout" 
        type="checkout.aspx" 
        controller="StoreController" />
    
      <view 
        name="survey" 
        type="survey.aspx" 
        controller="SurveyController" />
    
    </views>
    
    
  6. Specify the details in the <navigationGraph> element, as follows:

    • <navigationGraph> has attributes that specify the name of the process, the starting view, the ViewManager type, the shared state type, and the state persistence provider for this process.
    • <navigationGraph> also has <node> elements that identify the nodes in the navigation graph.

    The structure of the <navigationGraph> is as follows.

    <navigationGraph
        name="name of this navigation graph"
        startView="logical name of first view in this navigation graph"
        iViewManager="name of the view manager"
        state="name of the state type"
        statePersist="name of the state persistence mechanism">
    
      <node view="name of view'>
        <navigateTo navigateValue="navigation step" 
                    view="view to navigate to, for this value of navigateValue"/>
        … plus additional <navigateTo> elements, for other navigational steps …
      </node>
    
      … plus additional <node> elements, for other views …
    
    </navigationGraph>
    
    

    The following example shows the navigation graph for the UIProcessQuickstarts_Store sample application provided in the User Interface Process Application Block download.

    <navigationGraph 
        iViewManager="WinFormViewManager"
        name="Shopping" 
        state="State" 
        statePersist="SqlServerPersistState"
        startView="cart">
    
      <node view="cart">
        <navigateTo navigateValue="resume"   view="browsecatalog" />
        <navigateTo navigateValue="checkout" view="checkout" />
        <navigateTo navigateValue="fail"     view="error" />
        <navigateTo navigateValue="stop"     view="cart" />
      </node>
      <node view="browsecatalog">
        <navigateTo navigateValue="addItem" view="cart" />
        <navigateTo navigateValue="fail"    view="error" />
      </node>
      <node view="error">
        <navigateTo navigateValue="resume"  view="cart" />
      </node>
      <node view="checkout">
        <navigateTo navigateValue="successCheckout"  view="congratulations" />
        <navigateTo navigateValue="failCheckout"     view="checkout" />
      </node>
      <node view="congratulations">
        <navigateTo navigateValue="resume" view="cart" />
      </node>
    
    </navigationGraph>
    
    

    The navigation graph configuration maps closely to the flow of control identified in the requirements and it clearly exposes the major options for user interface process design.

Summary

This chapter described how to use design patterns to solve common problems in the presentation layer. The .NET Framework provides built-in support for most of these patterns in Windows Forms applications and ASP.NET Web applications. Additionally, the Microsoft User Interface Process Application Block provides support for the Model-View-Controller and Application Controller patterns to enable you to decouple user interface code from user interface process code. You can use this block as a starting point in your own applications to help you implement best practice presentation layer solutions.

Start | Previous | Next

patterns & practices Developer Center

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.

© Microsoft Corporation. All rights reserved.