Much ado about Page Templates...

Standardize the look and feel of your site using page templates. Use the Page class to build a reusable template for all your pages.


By: Victor Rubba Date: February 12, 2004 Download the code.

Once again on the quest for knowledge, I wanted to learn a little more about how to standardize the look and feel of my site. There are quite a few articles out there and I read through most of them. But I found they just assumed too much. What I really needed was a tutorial designed for the "absolute beginner".

So who should read this article? Well I'm a big VS.NET 2003 user and I love code-behind. I usually create a basic C# web application with the typical webform1.aspx. And that's where I begin. If you know what I'm talking about then this article is for you!

My dream was to come up with a way to have frame like functionality, without frames. You know… a nice header, footer and a side bar... and standardized throughout my application. I wanted to be able to have one template that did all that, and then I could spend the rest of my time focusing on individual page functionality.

Part 1 – Basics

So let's get started. Create a new C# Web Application in VS.Net... call it WebTemplate. Then click File | Add New Item... | click on Class, and call it PageTemplate. You should now see a PageTemplate.cs file in your Solution Explorer. Let's open it up.

Ok, since this is going to be our standard page template, it's actually going to be based off the asp.net Page class. So let's start by changing line 8 to read:

public class PageTemplate : Page

That's easy; now, delete lines 10 thru 15, leaving only the empty class. Now before we get any deeper into this I think I need to talk a little bit about the System.Web.UI.WebControls.PlaceHolder class. According to MSDN, the PlaceHolder control is used simply as a container to store dynamically added server controls to the Web page. It doesn't have any visible output and acts purely as a container.

Hmm, interesting... all this time and I've never needed to use a placeholder before. This really threw me off; especially while reading articles that make heavy use of placeholders. So to put it simply... we need it here to do what we want. We can shove whatever controls we need into a placeholder. Let's continue.

So as everyone knows (I hope), your standard basic HTML page has a Head section and a Body section. Let's start there and create one placeholder for the Head and one placeholder for the Body. Follow these steps:

The next step is to declare a control collection, which we will need later as well. This is just a container that holds control objects. I will demonstrate later on how this control collection is used to render the controls that were added on the child page.

Keeping in mind that we are essentially putting together all the building blocks of an asp.net page, let's not forget the form control. After all, the page template we are creating will ultimately be responsible for building the basic HTML building blocks for every page, including the form tag.

Ok now comes the time to start overriding some methods of the Page class we inherited from. Add the following code to your class:

    protected override void AddParsedSubObject(Object obj)
    {
      if (ControlBin == null)
        ControlBin = new ControlCollection(this);
      this.ControlBin.Add((System.Web.UI.Control)obj);
    }

This will grab any and all objects you will have created on your webform1.aspx (a.k.a. child page) and add it to the ControlBin collection. It will do this for any child page which inherits from our template. Finally, we need to overwrite the OnInit event, so we can customize our page before the OnInit event fires on the base Page class. Add the following to your class and save:

    protected override void OnInit(EventArgs e)
    {
      this.BuildPage();
      base.OnInit(e);
    }

Part 2 – Controls

Before we go any further, let's talk briefly about controls. Everything in an asp.net page is a control. Even the page itself is a control. Controls may be part of or contain within them a collection of further controls. The placeholders we created in part one are controls. The form we created is a control. The control bin is a collection of controls. The page is a control that can contain multiple collections of controls. And so on and so on…

So let's look at a typical aspx page. When building a typical aspx page we can add labels, textboxes, datagrids, etc. There are basically 2 types of controls... server controls (i.e. asp:textbox) and html controls (i.e. table). By adding the attributes id="<value>" and runat="server", we can change any HTML control into a server control. What's important to understand is that pretty much anything you see on a web page, from script blocks to tables, can be created and rendered server side.

And that's basically what we are going to do with our PageTemplate.cs. We are going to, in code, create our own controls, add controls from the child aspx page, manipulate collections... and ultimately produce a web page to the client.

Part 3 – Building the Page

This is where all the fun begins. I'm going to start with a really basic implementation and then build on it.

Let's create the following BuildPage() function inside the class.

private void BuildPage()
{
  this.Controls.Add(new LiteralControl(
    "<html>\n" +
    "<head>\n" +
"<title>Page Title </title>\n" +
"<body>\n" +
"<P> This is the body </P>\n" +
"</body>\n" +
"</html>")); }

Now save and test-compile your project. At this point you should be compiling error free!

Here's the complete listing for PageTemplate.cs:

All right, let's go back to WebForm1.aspx. Open it up in designer mode and switch to the HTML view. Delete everything except the first line:

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

Then open the code behind and change the line:

public class WebForm1 : Page  TO  public class WebForm1 : WebTemplate.PageTemplate

Now save. Set Webform1 as your Startup Page and run. You should get a blank webpage with only one line of text: "This is the body". Check the source code behind this page. Congratulations, you've created your first, albeit, completely functionless Page Template. Time to dig a little deeper...

Part 4 – Getting Fancy

Open WebForm1.aspx in Designer mode and add the following line of text:

Welcome to the Real world.

Save and Run. You'll notice the above text disappears on the compiled page. It's time to add more functionality. Let's add a new BuildAdvancedPage() function to our PageTemplate.cs file. This time we are going to attempt to do the following:

Here's the code:

A quick breakdown of what happens here:

Line 44: Similar to what we did in BuildPage() function, setup basic HTML tags
Line 49: Add a Literal control to the control collection within the Head placeholder
Line 50: Add the Head Placeholder to the Page Control collection
Line 52: Add a literal control that closes the head tag and opens the body tag
Line 54 - 57: Loop through all the controls that were collected from child page (WebForm1.aspx) and add them to the Body Placeholder’s control collection
Line 59: Add the Body Placeholder to Form1’s Control Collection
Line 60: Set the value of Form1’s ID attribute.
Line 61: Add the Form1 control to the Page Control Collection
Line 62: Finish up by adding a final Literal Control that completes the page tags.

Now change the OnInit event to point to BuildAdvancedPage() instead of BuildPage(). Save and run the project. You should now see the text from the child WebForm1.aspx. Right click and view source of the page, so you can see how our code was transformed into this end product.

Part 5 – Final Words

This article is meant to be a very basic introduction to page templates. Feel free to download the sample code and try it out. You can take it from here. You could add properties to your page template class and use these to customize things like the window title or the stylesheet. Or you could add custom user controls (ascx). You could integrate a table into the page template, and render all the controls from the child form into one cell of the table, giving each page that frame like appearance without the frames. You could add standardized error, session or viewstate handling to your template. And if you need assistance, there are tons of articles out there to help you take this to the next step.

You may download the code here.