The object-oriented nature of the .NET Framework—and thus, ASP.NET—opens up a new world to Web developers. Although this world requires some different skills and a new way of thinking about Web development, the investment pays off by enabling greater reuse and maintainability of your sites.
One example of applying these object-oriented principles to Web development involves creating a page framework, or a base class for pages within your site. In this article, I’ll explain the why and how of developing a page framework in ASP.NET.
Enter the Page class
When a Web page (often referred to as a form) is created in Visual Studio .NET, the page actually consists of two files, the .aspx file that contains the HTML and the .vb or .cs file that contains the code that executes as a part of the page, often called the codebehind. The Page directive on the .aspx file points to the codebehind file and the class in the file through the Codebehind and Inherits attributes, respectively.
It’s possible, of course, to include the Visual Basic or C# code directly in the .aspx file using server-side script blocks. For example, the Web Matrix tool available on http://www.asp.net/ and many of the examples on http://www.gotdotnet.com/ use this approach. However, the separation provided by the codebehind model of ASP.NET, coupled with VS.NET, allows increased maintainability by enforcing a model-view-controller type architecture. You can also do codebehind outside of VS.NET using the src attribute of the Page directive.
The developer then builds the project in VS.NET, producing a codebehind assembly (DLL) that is coupled with the .aspx page by the ASP.NET runtime to create the runtime Page class that is actually executed when a request for the page is processed.
What some developers may not realize is that the codebehind file contains a class that inherits from the Page class in the System.Web.UI namespace. This class (which ultimately inherits from a class called Control) provides all of the events, methods, and properties that the ASP.NET runtime uses to render the page on the browser, as shown in Figure A. Note that the Codebehind class (Form1 in this case) inherits from the Page class. Listing A shows a simple example of both the .aspx page and codebehind file in VB.
|The inheritance hierarchy involved when creating a new form|
The Page directive of Form1.aspx points to the Form1.aspx.vb file and its Form1 class. The Form1 class inherits from the Page class and then includes the declarations for controls placed on the page as well as event-handling code (in this case, the Load event of the Page class and the Click event of the btnHello button).
The interesting aspect of Listing A, however, is the Page_Load event handler. You’ll notice that this method handles the Load event of the MyBase object. In fact, the Load event is only one of several events that are fired while the page is processed. Others (in order of execution, along with Load) include Init, Load, PreRender, and Unload. As these events are fired, code in the Codebehind class is able to process the request and, for example, make a database call in the Load event to display the results on the page and log the request to a database in the Unload event.
Expanding the hierarchy
Although the .aspx page can contain all the HTML necessary to render the page, each page on a site often contains common elements that include headers, footers, menus, and navigation. These elements are typically placed in server-side includes or repeated inline; in an ASP.NET application, they are placed in Web User Controls and referenced on each page.
Unfortunately, these approaches all require that the process of including the elements be repeated on each page. An elegant alternative is to take advantage of the inheritance hierarchy shown in Figure A, coupled with the event processing model of the Page class, by creating a page framework class.
A page framework class is simply one that derives from the Page class and is then used as the base class for new forms. The result is that the hierarchy shown in Figure A is expanded, as shown in Figure B.
|The page hierarchy with the inclusion of a class that derives from the Page class|
To create a page framework class, you just create a new class in an application and derive it from Page. Then, to add HTML and other elements common to every page (such as an HTML table to produce the layout and Web User Controls), you add handlers for the various events, such as Init and PreRender, that programmatically create and add the appropriate controls to the Controls collection of the page. When a form is modified to inherit from your page framework class, it will include both the HTML rendered by the page framework class and that present in the form.
The Controls collection of the Page class holds references to all the controls that are to be rendered on the page. When the page is processed, the control tree stored in this collection is traversed and its controls rendered.
Unfortunately, the forms designer in VS.NET does not support visually inheriting the page, so the page framework class must be developed in code only. As a result, forms that end up inheriting from your page framework class will not appear different until requested by the browser. Also, before attempting to inherit from the page framework class, the project containing it must be compiled once so that the forms designer can display the form.
Creating the page framework
The simple class in Listing B shows an example of creating a page framework. Notice that the PageFramework class inherits from the Page class. Essentially, this class handles the Init and PreRender events via add handlers in the OnInit method at the bottom of the class, using the Control_Init and Control_PreRender methods.
The Control_Init method uses the private FindFormTag method to locate the form tag on the derived form, and then creates both literal HTML (using the LiteralControl) and Web User Controls (specified in the public fields) and adds them to the Controls collection of the form. This is required since certain ASP.NET Server Controls must be child controls of a form in order to execute.
The interesting aspect of Control_Init is that as it creates the HTML table used for the layout and adds the appropriate user controls for the header, menu, and footer, it adds them to the Controls collection using the Add and AddAt methods. You’ll notice that everything up to the menu is loaded using AddAt so that it can be positioned before the content in the derived form. However, the footer control and the end of the table tag are added using the Add method so that they are placed directly before the closing form tag on the form. This allows the content produced by the PageFramework class to wrap itself around any content in the form.
The StringBuilder class is used to build the HTML since it is more efficient than simple string concatenation.
Interestingly, since the PageFramework class exposes public fields, a form that derives from PageFramework can set these fields in its Init event, as shown in Listing C.
The Page_Init method sets the Title, InitialFocus, and MenuControl fields exposed by the PageFramework class. When this page is requested by the browser, the end result is a page that inherits the look and feel of the PageFramework class complete with user controls for the header, menu, and footer without having to add any specific HTML. This provides an elegant solution to the problem of enforcing a particular style for a site.