Click here to Skip to main content
15,860,861 members
Articles / Web Development / ASP.NET
Article

ASP.NET Page Templates - Using Inheritance

Rate me:
Please Sign up or sign in to vote.
4.76/5 (135 votes)
19 Sep 20028 min read 1.3M   11.8K   304   233
A tutorial showing how to create page templates that use object-oriented inheritance.

Page Inheritance Class Diagram

Introduction

Anyone who has developed commercial websites has run into the problem of creating a template for the site. For most sites a large percentage of the HTML is the same or similar for all pages. The header, navigation, and footer elements of a typical site layout appear on almost every page.

Some developers will put that markup in every page's source file. Anyone who's had to maintain a site put together this way knows how difficult it is to make site-wide changes. You end up depending on massive search and replace operations, which can be difficult to do without creating markup errors that require hand-tuning to repair.

In traditional ASP programming, like many other server-based programming environments, this was typically solved using an include file. The page is divided into logical sections representing the header, left navigation, body, and footer elements. A separate include is created for each of the common elements and the body section is placed in the actual ASP page. The page then includes the appropriate files to build up the look and feel of the page. This is a significant improvement over the previous approach, but still creates a few maintenance problems.

First of all, the individual ASP files must contain the code necessary to include the correct support files. This makes each page's content strongly coupled to the site's template. It also means that when you add a new page to the site, you must remember to setup all the correct includes in the right order.

An additional problem happens when you decide to make a significant look-and-feel change to the site. If you're lucky, you may be able to make all of your changes in the include files. Most of the time, however, the tight coupling between the include files and the ASP pages means that you end up having to edit each and every ASP page as well.

With the introduction of ASP.NET, developers have been giving a powerful new set of tools to help resolve these problems. ASP.NET uses an object-oriented development paradigm. In practical terms this means that every page is a class that derives from System.Web.UI.Page. This class provides a number of services to the web developer including caching, rendering, response and request access, etc.

So the question is: How can we best take advantage of the object-oriented nature of ASP.NET when creating websites? Is there a better way to create templates than using include-files?

Web User Controls

One of the first things a developer notices when getting started with ASP.NET is that a new style of control has been introduced: Web User Controls. User Controls allow a developer to encapsulate a common chunk of HTML or server-side code into a component that can be reused on many different pages.

User controls are not used in a page using the #include directive. Instead that are either placed in the ASPX file as a custom tag (with the Register directive) or from server-side code with the LoadControl statement.

This article is not going to go into the details of creating and using User Controls; there are many other sources that cover that topic. They are, however, and improvement over the old include-file approach and deserved mention here.

They don't solve all of the problems discussed above though. If, for example, you encapsulate your page header in a user control, you still have to remember to use it on each and every page in the site. If you decide you need to add another user control to another part of your site, then once again you end up editing every page to get that user control embedded.

Also, depending on the layout of the pages in your site, you may end up having some formatting or positioning markup in each page to make sure that the User Control is exactly where you want it on the page. This markup is common to every page and we should be able to find a way to have that code exist in only one place.

Simple Page Inheritance

If you're familiar with object-oriented programming then you are probably already seeing something familiar. When you have a chunk of code that occurs in more than one class, it is common practice to refactor that code by creating a base class and moving that code "up" one level in the inheritance chain. Why not do that here?

Since all ASPX pages derive from System.Web.UI.Page, we should be able to create a base class that sits between our page class and the Page class. This class will be responsible for producing the template used in our site. Then our page classes will derive from the base class and will only contain the markup and server controls needed for their specific task. To illustrate this, let's create an example base class called PageBase and then derive a page class from it. (All of the examples in this article are written in C#, but you should easily be able to convert them to VB.NET or any other .NET language.)

C#
using System;
using System.Web.UI;
public class PageBase : System.Web.UI.Page
{
    private string _pageTitle;
    public string PageTitle
    {
        get { return _pageTitle; }
        set { _pageTitle = value; }
    }

    protected override void Render(HtmlTextWriter writer)
    {
        // First we will build up the html document, 
        // the head section and the body section.
        writer.Write( @"
            <html>
                <head>
                    <title>" + PageTitle + @"</title>
                </head>
                <body>" );

        // Then we allow the base class to render the 
        // controls contained in the ASPX file.
        base.Render( writer );

        // Finally we render the final tags to close
        // the document.
        Writer.Write( @"
                </body>
            </html>" );
    }
}

Let's take a look at a few interesting points illustrated in this base class. First of all, we expose a property called PageTitle that will contain the title for the page. This property's contents are written out in the <title> section of the page. Also notice the '@' symbol used before the string literals. While not explicitly required for this example, the '@' symbol is a C# token that says the following string literal should not have escape sequences expanded. If we didn't do this, we would have to escape our slash characters. If you are working in VB.NET you don't have to worry about this.

Now we will create an ASPX page that derives from PageBase. I'll be using code-behind pages, so we have two files. The first file has a file extension .ASPX and contains the markup for the page.

ASP.NET
<%@ Page language="c#" Codebehind="SimplePageInheritance.aspx.cs" AutoEventWireup="false" 
       Inherits="SimplePageInheritance" %>
<form id="SimplePageInheritance" method="post" runat="server">
    <h1>This is Page 1</h1>
    <p>
        This page demonstrates Simple Page Inheritance where the content is rendered 
        using the base class' Render() method. You cannot use Server Controls that 
        postback in the base class, they can only be used in the .ASPX page itself.
    </p>
</form>

Notice that there is no markup for "standard" content like <html>, <head> and <body> because they will be rendered in the base class. The code-behind class defines the SimplePageInheritance class referenced in the Page declaration.

C#
public class SimplePageInheritance : PageBase
{
    public SimplePageInheritance()
    {
        PageTitle = "Simple Page Inheritance";
    }
}

Pretty cool, eh? This solution solves all of the problems with include-based templates mentioned earlier. For some situations, however, it doesn't solve everything. For those we need Advanced Page Inheritance.

Advanced Page Inheritance

The biggest problem with the Simple Page Inheritance system is that you cannot put any server controls that cause a post-back in the template base class. The reason for this is related to the position of the <form> tag. In Simple Page Inheritance, the <form> tag is included in the derived ASPX page. Therefore any markup rendered by the base class will be either before or after the ASPX file markup. This might occur if, for example, we wanted a search system included in the template.

There are many different ways we could solve this problem. For this example, we will move the <form> tag from the ASPX file to the base class. We can't just render the <form> tag as a string literal either, because in ASPX files, the form is actually processed on the server before the markup is sent to the browser. So we have to create a Form object and insert it into the Controls collection of the page.

C#
using System;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;    
public class AdvancedPageInheritanceBase : System.Web.UI.Page
{
    protected override void OnInit(System.EventArgs e)
    {
        BuildPage( GenerateHtmlForm() );
        base.OnInit(e);
    }

    protected void BuildPage( HtmlForm form )
    {
        ////////////////////////////////////////////////////////
        // Build the page and include the generated form

        this.Controls.AddAt( 0, new LiteralControl( @"
            <html>
                <head>
                    <title>" + PageTitle + @"</title>
                </head>
                <body>
            ") );

        this.Controls.Add( form );

        this.Controls.Add( new LiteralControl( @"
                </body>
            </html>
        "));
    }

    private HtmlForm GenerateHtmlForm()
    {
        HtmlForm form = new HtmlForm();
        AddSearch(form);
        AddControlsFromDerivedPage(form);
        return form;
    }

    private void AddSearch( HtmlForm form )
    {
        searchBox = new TextBox();
        Button searchButton = new Button();
        searchButton.Text = "Search";
        searchButton.Click += 
            new EventHandler( this.OnSearchButtonClicked );
        form.Controls.Add( searchBox );
        form.Controls.Add( searchButton );
        form.Controls.Add( new LiteralControl("<br>") );
    }

    protected void OnSearchClick( object sender, EventArgs e )
    {
        // Do the search here
    }

    private void AddControlsFromDerivedPage(HtmlForm form)
    {
        int count = this.Controls.Count;
        for( int i = 0; i<count; ++i )
        {
            System.Web.UI.Control ctrl  = this.Controls[0];
            form.Controls.Add( ctrl );
            this.Controls.Remove( ctrl );
        }
    }
}

Remember that I said there are many ways to solve the <form> tag placement problem. In this solution we create an HtmlForm control, populate it with the contents of the ASPX page and then insert it into the template.

This system works very well and allows us to create ASPX pages that are completely independent of the template. Here is the ASPX file:

VB
<%@ Page language="c#" Codebehind="AdvancedPageInheritance.aspx.cs" 
            AutoEventWireup="false" 
       Inherits="PageInheritanceSample.AdvancedPageInheritance" %>
<h1>Advanced Page Inheritance</h1>
<p>This demonstrates the Advanced Page Inheritance Technique.</p>

The code-behind file is no different than the one used in the Simple Page Inheritance example. Notice that our ASPX file doesn't have a <form> tag in it.

Performance

I was surprised to find that there is almost no performance difference when using page inheritance. Using Microsoft Application Center Test, I ran page load tests on three different versions of similar page structures:

  • The first page used user controls to encapsulate the header, left navigation and footer sections. It did not use any page inheritance.
  • The second page had the same look & feel and the same content but used the method outlined in Simple Page Inheritance.
  • The third page had a similar look & feel (it included a search box like in the example code) and used the method outlined in Advanced Page Inheritance.

The following table shows the results after loading each page continuously for 5 minutes on my development workstation. As you can see, the differences are minimal.

Page # of Requests Avg. Response Time (ms) Avg. Requests per Sec.
WebUserControl.aspx 16,693 10.53 55.64
SimplePageInheritance.aspx 16,636 10.54 55.45
AdvancedPageInheritance.aspx 16,965 10.20 56.55

Conclusion

Developing websites and web applications that are easy to maintain has always been a challenge. Over time the techniques used have evolved from almost nothing to modern object-oriented techniques like those presented in this article. These techniques can be used by anyone with a basic understanding of object-oriented programming and will almost certainly help you produce better factored, easier to maintain code.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Peter Provost has been programming computers since he was 10 years old. Currently a C# and C++ junkie, he has been developing on the Windows platform since Windows 286.

Peter is a Software Design Engineer with Microsoft's patterns and practices team where he works on guidance for .NET developers.

Peter maintains an active weblog about technology, .NET and other interesting stuff at http://www.peterprovost.org

Comments and Discussions

 
GeneralSOLUTION: Using the Designer Pin
Peter Provost13-Nov-02 11:31
Peter Provost13-Nov-02 11:31 
GeneralRe: SOLUTION: Using the Designer Pin
leppie13-Nov-02 12:23
leppie13-Nov-02 12:23 
GeneralEscaping Pin
leppie18-Oct-02 7:24
leppie18-Oct-02 7:24 
GeneralRe: Escaping Pin
Peter Provost18-Oct-02 10:02
Peter Provost18-Oct-02 10:02 
GeneralRe: Escaping Pin
leppie20-Oct-02 13:12
leppie20-Oct-02 13:12 
GeneralPage_Load twice Pin
Patrick Long16-Oct-02 20:19
Patrick Long16-Oct-02 20:19 
GeneralRe: Page_Load twice Pin
Peter Provost18-Oct-02 9:59
Peter Provost18-Oct-02 9:59 
GeneralRe: Page_Load twice Pin
Patrick Long19-Oct-02 13:25
Patrick Long19-Oct-02 13:25 
GeneralRe: Page_Load twice Pin
Peter Provost20-Oct-02 4:31
Peter Provost20-Oct-02 4:31 
GeneralRe: Page_Load twice Pin
Paddeo11-Nov-02 4:23
sussPaddeo11-Nov-02 4:23 
GeneralRe: Page_Load twice Pin
Peter Provost11-Nov-02 4:47
Peter Provost11-Nov-02 4:47 
GeneralRe: Page_Load twice Pin
Patrick Long11-Nov-02 10:33
Patrick Long11-Nov-02 10:33 
GeneralRe: Page_Load twice Pin
Topper Price2-Nov-02 11:11
Topper Price2-Nov-02 11:11 
GeneralRe: Page_Load twice Pin
Peter Provost20-Dec-02 4:49
Peter Provost20-Dec-02 4:49 
GeneralRe: Page_Load twice Pin
Patrick Long4-Feb-03 23:45
Patrick Long4-Feb-03 23:45 
Generaloutput.write and user controls Pin
Paul Watson6-Oct-02 23:37
sitebuilderPaul Watson6-Oct-02 23:37 
GeneralRe: output.write and user controls Pin
Peter Provost11-Oct-02 8:22
Peter Provost11-Oct-02 8:22 
GeneralRe: output.write and user controls Pin
BEPO28-May-03 8:37
BEPO28-May-03 8:37 
GeneralEscaping // and a small bug Pin
Paul Watson3-Oct-02 23:24
sitebuilderPaul Watson3-Oct-02 23:24 
GeneralRe: Escaping // and a small bug Pin
Peter Provost11-Oct-02 8:19
Peter Provost11-Oct-02 8:19 
GeneralRe: Escaping // and a small bug Pin
stephen woolhead24-Jan-03 1:37
stephen woolhead24-Jan-03 1:37 
GeneralPostBack Problem Pin
Peet Schultz1-Oct-02 20:38
Peet Schultz1-Oct-02 20:38 
GeneralRe: PostBack Problem Pin
Nick Parker2-Oct-02 1:56
protectorNick Parker2-Oct-02 1:56 
GeneralRe: PostBack Problem Pin
Peet Schultz2-Oct-02 3:39
Peet Schultz2-Oct-02 3:39 
GeneralRe: PostBack Problem Pin
Peter Provost2-Oct-02 4:46
Peter Provost2-Oct-02 4:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.