Home   Articles We've Written   Web Templates

Creating a Visual Web Page Template

Web developers moving to ASP.NET face the problem of creating a Web page template to enforce a consistent look and feel across a Web site. When your organization or customer wants to tweak the appearance of a site, you don't want to go through large numbers of Web pages to make identical changes. You want to change a single template used by all of your Web pages.

With the inheritance capability built into the .NET languages, you have several choices available to you for creating such a template. At the end of this article, you'll find links to other articles that explain different Web template approaches. Each approach has its advantages and disadvantages, and you'll want to know the options available to you and your team. Compare them all and select the approach that makes sense for your Web application.

The advantage of the technique I present in this article is its simplicity. You create your visual Web page template as a single ASPX in the Visual Studio .NET designer. Then you route each incoming page request for handling by the template, which displays the content of the specific page requested.

Except for the source of the content, the visual Web page template described in this article resembles the templates used for database-driven Web sites. This technique avoids the need to hard-code lots of HTML. (Check out the amount of hard-coded HTML required by other popular approaches.)

The VbTemplate demonstration project (view demo in new browser window) illustrates the technique that I explain in this article. Take a look at the different pages in the demo, and then keep the demonstration window open to experiment with while you read the explanation of how this template works. For this demonstration I used Visual Basic .NET, but you can use the .NET language of your choice.

Designing the Template

All five pages in the VbTemplate demonstration share a single ASPX file, index.aspx. The design of index.aspx (see Figure 1) controls the appearance of all pages.

Figure 1. The index.aspx file is the only ASPX file in the VbTemplate demonstration project.
Creating a Web Page Template, Figure 1.

By using Visual Studio .NET, you gain the ability to drag and drop controls on the designer and you get a visual impression of how the pages in the Web site will look. For this demo, I dropped the default Styles.css file on the designer to give the template some style, then added CSS styles—only three, as it turned out—when I needed them.

You can see the CSS styles I added (see Listing 1) in the Visual Studio screenshot in Figure 1. The Selected style applies to the bold font labels in the Pages cell. When a page is rendered, a bold font label names the page to be displayed in the browser, and all of the other pages show up as links. As I'll demonstrate shortly, the code behind index.aspx determines which alternative in the page list is visible, depending upon which page the visitor has requested.

Listing 1. CSS classes added to Styles.css for the VbTemplate demo.

.Selected 
{
  font-weight: bold;
  color: #003366;
}

.Time 
{
  color: #003366;
}

.Copyright 
{
  font-size: .7em;
  color: #003366;
  text-align: center;
}

The Time style applies only to the lblTime label control in the top cell of the template. I added the time stamp to demonstrate that output caching works correctly using this visual Web page template technique. The Copyright style applies to copyright notice on the bottom line in Figure 1.

Because index.aspx acts as a template for all of the Web pages in the site, its content cannot be determined until the visitor selects a particular page. Therefore I added the PlaceHolder control plcContent to hold the actual content once known. In the VbTemplate demo, the content for each page resides in a separate ASCX user control file. (At the end of this article, I discuss a variant of this technique that does not use ASCX files for content.)

Looking at the Template Structure

As the only ASPX file in the VbTemplate demo, index.aspx naturally receives the startup page designation. You can see at the top of Listing 2, however, that the Page directive specifies VbTemplate.PageBase as the base class for the page instead of Visual Studio's default value of VbTemplate.index. I reserved the latter class name for the user control that contains the content for index.aspx.

Listing 2. The HTML listing for index.aspx.

<%@ Page Language="vb" AutoEventWireup="false" 
  Codebehind="index.aspx.vb" 
  Inherits="VbTemplate.PageBase" %>
<%@ OutputCache Duration="1000" Location="Server" VaryByParam="*"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>
      <asp:literal id="litTitle" runat="server">Template</asp:literal>
    </title>
    <meta name="description" content="Visual Web Template Demo">
    <meta name="keywords" content="ASP.NET, VB.NET, Visual, Template">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta content="Microsoft Visual Studio.NET 7.0" name="GENERATOR">
    <meta content="Visual Basic 7.0" name="CODE_LANGUAGE">
    <meta content="JavaScript" name="vs_defaultClientScript">
    <meta content="http://schemas.microsoft.com/intellisense/ie5" 
      name="vs_targetSchema">
    <link href="Styles.css" type="text/css" rel="stylesheet">
  </head>
  <body>
    <form id="Form1" method="post" runat="server">
      <table id="tblMain" cellspacing="1" cellpadding="1" 
        width="100%" border="0">
        <tr>
          <td align="middle" colspan="2">
            <h1>Visual Web Page Template</h1>
            <p><asp:label id="lblTime" runat="server" cssclass="Time">
              11:35:10 AM</asp:label></p>
            <hr width="100%">
          </td>
        </tr>
        <tr>
          <td valign="top" width="25%">
            <h2>Pages</h2>
            <asp:label id="lbl_index" runat="server" 
              visible="False" cssclass="Selected">Home
            </asp:label>
            <asp:hyperlink id="lnk_index" runat="server" 
              navigateurl="index.aspx">Home
            </asp:hyperlink><br>
            <asp:label id="lbl_static_text" runat="server" 
              visible="False" cssclass="Selected">Static Text
            </asp:label>
            <asp:hyperlink id="lnk_static_text" runat="server" 
              navigateurl="static_text.aspx">Static Text
            </asp:hyperlink><br>
            <asp:label id="lbl_dynamic_text" runat="server" 
              visible="False" cssclass="Selected">Dynamic Text
            </asp:label>
            <asp:hyperlink id="lnk_dynamic_text" runat="server" 
              navigateurl="dynamic_text.aspx">Dynamic Text
            </asp:hyperlink><br>
            <asp:label id="lbl_button_click" runat="server" 
              visible="False" cssclass="Selected">Button Click
            </asp:label>
            <asp:hyperlink id="lnk_button_click" runat="server" 
              navigateurl="button_click.aspx">Button Click
            </asp:hyperlink><br>
            <asp:label id="lbl_text_input" runat="server" 
              visible="False" cssclass="Selected">Text Input
            </asp:label>
            <asp:hyperlink id="lnk_text_input" runat="server" 
              navigateurl="text_input.aspx">Text Input
            </asp:hyperlink>
          </td>
          <td valign="top" width="75%">
            <h2>Content</h2>
            <asp:placeholder id="plcContent" runat="server">
            </asp:placeholder>
          </td>
        </tr>
        <tr>
          <td align="middle" colspan="2">
            <hr width="100%">
            <div class="Copyright">
              Copyright
              <asp:label id="lblYears" runat="server">© 2001 - 2002
              </asp:label>
              &nbsp;SoftMedia Artisans, Inc. All Rights Reserved.
            </div>
          </td>
        </tr>
      </table>
    </form>
  </body>
</html>

The OutputCache directive in Listing 2 specifies server caching as the default condition for all pages generated by this template. If you check the time stamp on the Home page, then return to the Home page after viewing other pages, you'll see that you receive a cached version of the Home page on your second request. In a later example, I show how to turn off output caching for individual pages where it is not appropriate.

Depending upon your application's requirements, you might need a more sophisticated OutputCache directive or you might need to create user controls for finer control over caching. (In Chapter 10 of our book Programming the Web with Visual Basic .NET, we explain, with code examples, the output caching options available.)

Designers commonly use HTML tables to provide a liquid design structure for their Web pages. When you resize the browser window, the text flows within the table to accomodate its new size. As you can see in Listing 2, I've adopted that strategy for this demo.

In the head section of the HTML, the title element contains the following line:

<asp:literal id="litTitle" runat="server">Template</asp:literal>

Because the title will change depending upon the page actually requested, I inserted a Literal control to receive the appropriate title programmatically.

In the Pages cell, all of the links start with Visible=True (the default) and all of the corresponding Selected labels start with Visible=False. Once the page requested is known, the template code reverses the values of the Visible properties corresponding to that page. Of course the code cannot do that unless it knows what page the visitor requested. Let's take a look at how that's done.

Routing an Incoming Request to the Template

The Global.asax.vb file contains handlers for events that fire before and after page processing. The event to use for routing an incoming request to the template is Application.BeginRequest. Listing 3 contains the code that does this routing in the VbTemplate demo.

Listing 3. The handler for the Application.BeginRequest event in the global.asax.vb file.

Sub Application_BeginRequest( _
  ByVal sender As Object, ByVal e As EventArgs)

  ' Fires at the beginning of each request
  Dim sPath As String = Request.Path.ToLower
  Dim iStart As Integer = sPath.LastIndexOf("/") + 1
  Dim sPage As String = sPath.Substring( _
    iStart, sPath.IndexOf(".aspx") - iStart)
  If Not sPage = "index" Then
    Context.RewritePath(sPath.Substring(0, iStart) _
      & "index.aspx?page=" & sPage)
  End If
End Sub

The code in Listing 3 extracts the name of the page requested from the Request.Path property. If it is not the index page, the code uses the Context.RewritePath method to route the request to the template, appending a query string with the name of the page actually requested. If the request is for the index page, the code does not reroute it.

Note that on a postback, the Request.Path will request the index page and will be accompanied by a query string identifying the page originally requested. In the next section we look at how the template code uses the query string, however it originated, to determine the content of the response.

Handling the Incoming Request

As I mentioned earlier, the code behind the template bears the class name PageBase even though it resides in the index.aspx.vb file. The template code for this demo, shown in Listing 4, does its work in the handlers for the Page.Load and Page.PreRender events.

Listing 4. The PageBase code in index.aspx.vb.

Option Strict On
Imports System.Web.UI.WebControls

Public Class PageBase
  Inherits System.Web.UI.Page
  Protected WithEvents litTitle As Literal
  Protected WithEvents lblTime As Label
  Protected WithEvents lbl_index As Label
  Protected WithEvents lnk_index As HyperLink
  Protected WithEvents lbl_static_text As Label
  Protected WithEvents lnk_static_text As HyperLink
  Protected WithEvents lbl_dynamic_text As Label
  Protected WithEvents lnk_dynamic_text As HyperLink
  Protected WithEvents lbl_button_click As Label
  Protected WithEvents lnk_button_click As HyperLink
  Protected WithEvents lbl_text_input As Label
  Protected WithEvents lnk_text_input As HyperLink
  Protected WithEvents lblYears As Label
  Protected WithEvents plcContent As PlaceHolder

#Region " Web Form Designer Generated Code "
  'This call is required by the Web Form Designer.
  <System.Diagnostics.DebuggerStepThrough()> _
  Private Sub InitializeComponent()

  End Sub

  Private Sub Page_Init(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles MyBase.Init
    'CODEGEN: This method call is required 
    '  by the Web Form Designer
    'Do not modify it using the code editor.
    InitializeComponent()
  End Sub
#End Region

  Private _sPage As String
  Private _oContent As ControlBase

  Private Sub Page_Load(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) _
  Handles MyBase.Load

    'Determine the page requested
    _sPage = Request.QueryString("page")
    If _sPage Is Nothing Then _sPage = "index"

    'Use time stamp to show caching
    Me.lblTime.Text = Now.ToLongTimeString

    'Set copyright years
    Me.lblYears.Text = "© 2001 - " & Year(Today).ToString

    Try
      'Add content from user control
      _oContent = CType( _
        Page.LoadControl(_sPage & ".ascx"), ControlBase)
      Me.plcContent.Controls.Add(_oContent)

      'Show the page selected in bold text
      Me.ShowSelection(_sPage)
    Catch
      'Unsupported page request
      Me.plcContent.Controls.Add(New LiteralControl( _
        "Please select a link from the list."))
    End Try
  End Sub

  Private Sub Page_PreRender(ByVal sender As Object, _
  ByVal e As System.EventArgs) Handles MyBase.PreRender
    If (Not _oContent Is Nothing) Then
      'Page request is supported
      If (_oContent.Title <> String.Empty) Then
        'Get page title from user control
        Me.litTitle.Text = _oContent.Title
      Else
        'Get page title from link text
        Dim oLink As HyperLink = _
          CType(Me.FindControl("lnk_" & _sPage), HyperLink)
        Me.litTitle.Text = oLink.Text
      End If
    End If
  End Sub

  Private Sub ShowSelection(ByVal Page As String)
    'Turn off link for the current selection
    Dim oLink As HyperLink = _
      CType(Me.FindControl("lnk_" & Page), HyperLink)
    oLink.Visible = False
    Dim oLabel As Label = _
      CType(Me.FindControl("lbl_" & Page), Label)
    oLabel.Visible = True
  End Sub
End Class

The Page_Load code in Listing 4 retrieves the query string passed by the Application_BeginRequest procedure like this:

_sPage = Request.QueryString("page")
If _sPage Is Nothing Then _sPage = "index"

If the visitor requests the index page directly, the query string will be empty, so the second line is necessary.

Using this visual Web page template technique, the content for each page resides in an ASCX (user control) file. For example, the content for index.aspx resides in the index.ascx file. When the ASCX corresponding to the request exists, the following lines add the user control to the PlaceHolder located in the Content section of the template:

_oContent = CType( _
  Page.LoadControl(_sPage & ".ascx"), ControlBase)
Me.plcContent.Controls.Add(_oContent)

Note that ControlBase, rather than UserControl, is the base class for all of the ASCX files in this demo. I'll explain why in the next section of this article.

If the ASCX does not exist, the Catch block adds a generic message instead. (Because the Application_BeginRequest procedure routes all incoming ASPX requests to the template, ASP.NET cannot identify a Page Not Found error.)

That's all it takes to add the requested content to the page. However, the display of the template itself needs to be changed to reflect the actual page requested, like this:

Me.ShowSelection(_sPage)

The ShowSelection procedure (see the bottom of Listing 4) replaces the link to the selected page with a bold font label.

Finally, the Page_PreRender procedure performs any tasks required because of actions taken by the code behind the ASCX file. For this demo, we take this opportunity to set the page title (which displays at the top of the browser window) to the value of the user control's Title property, like this.

Me.litTitle.Text = _oContent.Title

If the user control doesn't supply a Title value, the procedure simply sets the page title to the text used for the page link.

Having looked at how the template code uses the Title property, we're now going to examine the mechanism actually used to supply the page title and to provide other common functionality for the ASCX files.

Subclassing the UserControl Class

By default, the code behind a user control inherits directly from the UserControl class. If the content portions of your Web application contain no common processing requirements, that default works fine.

Usually, however, you'll want to provide some common functionality beyond that provided by the UserControl class. To do so, you create a custom base class like that shown in Listing 5. The ControlBase class in the demo inherits from UserControl, but exposes two properties and adds the link to this article that appears at the bottom of the content.

Listing 5. The ControlBase class code in ControlBase.vb.

Option Strict On
Imports System
Imports System.Web
Imports System.Web.UI

Public Class ControlBase
  Inherits UserControl
  
  Private _sTitle As String

  Public Property Title() As String
    Get
      Return _sTitle
    End Get
    Set(ByVal Value As String)
      _sTitle = Value
    End Set
  End Property

  Private _sAppend As String

  Public Property Append() As String
    Get
      Return _sAppend
    End Get
    Set(ByVal Value As String)
      _sAppend = Value
    End Set
  End Property

  Private Sub Page_PreRender(ByVal sender As Object, _
  ByVal e As System.EventArgs) Handles MyBase.PreRender
    If Append <> String.Empty Then
      Me.Controls.Add(New LiteralControl(Append & vbCrLf))
    End If

    Dim sFoot As String = _
        "<hr width='100%' />" & vbCrLf _
      & "<div>Read about this template: " _
      & "<a href='http://www.smartisans.com/" _
      & "articles/vb_templates.aspx'>" _
      & "Creating a Visual Web Page Template" _
      & "</a>.</div>" & vbCrLf
    Me.Controls.Add(New LiteralControl(sFoot))
  End Sub
End Class

When the code behind a user control inherits from ControlBase, it inherits all of the UserControl functionality as well as the functionality provided in Listing 5.

The Title property allows the user control to specify the title to be displayed at the top of the browser window. At the end of the last section we looked at the code that uses this property to set the page title.

The Append property provides a means to append text or HTML to the static content of the ASCX page. The dynamic_content page demonstrates the use of this property.

The Page_PreRender procedure appends the text, if any, to the Controls collection. Then it adds the link at the bottom of the content.

Providing Static Content

Aside from the link to this article, which always appears, two of the Web pages in this demo display static text only. The first is the Home page, index.aspx. Figure 2 shows how its text appears in the Visual Studio .NET designer.

Figure 2. The index.ascx file provides the content for the index.aspx Web page.
Creating a Web Page Template, Figure 2.

When you work in the designer, you can have Visual Studio .NET create HTML for you. The italicized text in Figure 2, for example, was formatted by highlighting the desired text and clicking the italics icon. You can create a link by highlighting some text and pressing Ctrl-L, and so on. Listing 6 provides the HTML that was created by typing the text shown in Figure 2.

Listing 6. The HTML listing for the index.ascx user control.

<%@ Control Language="vb" AutoEventWireup="false" 
  Codebehind="index.ascx.vb" Inherits="VbTemplate.index" 
  TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%>
<p>
  This project uses a single ASPX template file, 
  <em>index.aspx</em>, to display all five of its content pages. 
  The actual content for each page resides in its ASCX file. 
  The content for this page, for example, can be found 
  in <em>index.ascx</em>.
</p>
<p>
  The mechanism used to forward each incoming request is the 
  Context.RewritePath method in the Application_BeginRequest 
  event handler. A query string supplies the name of the page 
  originally requested by the visitor.
</p>
<p>
  Click the links to see how the template handles 
  different types of content.
</p>

The second of the text-only Web pages in this demo is static_text. This Web page displays the content of the static_text.ascx file. Figure 3 shows its content in the Visual Studio .NET designer.

Figure 3. The static_text.ascx file provides the content for the static_text.aspx Web page.
Creating a Web Page Template, Figure 3.

I don't show the code behind either index.ascx or static_text.ascx in this article because both listings do nothing but set the page title property in the same manner as the listings that follow. Let's move on to the user controls that do a bit more.

Appending Content Dynamically

The content for the dynamic_text Web page resides in the dynamic_text.ascx file. This content includes a short unnumbered list, as shown in Figure 4.

Figure 4. The dynamic_text.ascx file provides the content for the dynamic_text.aspx Web page.
Creating a Web Page Template, Figure 4.

If you request the Dynamic Text page of the demo, you see an additional paragraph appended to the text in Figure 4. Listing 7 shows how the ControlBase.Append property was set. Note that, as I promised, the dynamic_text class inherits from ControlBase instead of UserControl.

Listing 7. Code in the Page_Load event handler in dynamic_text.ascx.vb specifies content to be appended by ControlBase.vb.

Option Strict On
Public MustInherit Class dynamic_text
  Inherits ControlBase

#Region " Web Form Designer Generated Code "
  'This call is required by the Web Form Designer.
  <System.Diagnostics.DebuggerStepThrough()> _
  Private Sub InitializeComponent()

  End Sub

  Private Sub Page_Init(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles MyBase.Init
    'CODEGEN: This method call is required 
    '  by the Web Form Designer
    'Do not modify it using the code editor.
    InitializeComponent()
  End Sub
#End Region

  Private Sub Page_Load(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles MyBase.Load
    Me.Title = "Visual Web Page Template Demo: Dynamic Text"
    Me.Append = "<p>But this sentence was appended " _
      & "programmatically during the Page.Load event " _
      & "in <em>dynamic_text.ascx.vb</em>.<p>"
  End Sub
End Class

You can also see in Listing 7 how the Page_Load procedure sets the Title property. Similar statements exist in all of the ASCX.VB code files in this demo.

To this point, none of the pages we've discussed post back to the server. And in Web applications that supply content to visitors, that is the normal situation. As I'm about to show, however, this technique can also handle postbacks.

Posting Back to the Template

Using the visual Web page template technique described in this article, postbacks are handled in the ASCX.VB code files for their respective pages. The button_click page illustrates the use of an HTML input button to post back to the server. Figure 5 depicts the content for the page as it appears in the Visual Studio .NET designer.

Figure 5. The button_click.ascx file provides the content for the button_click.aspx Web page.
Creating a Web Page Template, Figure 5.

Working in the designer, you can double-click a button control to direct Visual Studio .NET to stub in the proper event handler for you. I did this with the button in Figure 5 to start the btnClick_ServerClick procedure at the bottom of Listing 8.

Listing 8. Code in the btnClick_ServerClick event handler in button_click.ascx.vb reports when the user control recognizes a button click in the browser.

Option Strict On
Imports System.Web.UI.HtmlControls

Public MustInherit Class button_click
  Inherits ControlBase
  Protected WithEvents btnClick As HtmlInputButton
  Protected WithEvents pReply As HtmlGenericControl

#Region " Web Form Designer Generated Code "
  'This call is required by the Web Form Designer.
  <System.Diagnostics.DebuggerStepThrough()> _
  Private Sub InitializeComponent()

  End Sub

  Private Sub Page_Init(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles MyBase.Init
    'CODEGEN: This method call is required 
    '  by the Web Form Designer
    'Do not modify it using the code editor.
    InitializeComponent()
  End Sub
#End Region

  Private Sub Page_Load(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles MyBase.Load
    Me.Title = "Visual Web Page Template Demo: Button Click"
  End Sub

  Private Sub btnClick_ServerClick( _
  ByVal sender As System.Object, _
  ByVal e As System.EventArgs) _
  Handles btnClick.ServerClick
    pReply.InnerText = "You clicked the button!"
  End Sub
End Class

If you request the Button Click page of the demo, you can verify that the event handler in Listing 8 gets control. Click the button and note the response.

Now take a look at the URL displayed in the address box of your browser. The postback returns the URL built in the Application_BeginRequest procedure when it routed the original request to the template.

If your Web application requires postbacks and you want to conceal the postback URL from your visitors, you can do so. The next example shows how.

Accepting Input from the Visitor

The text_input.aspx page provides an HTML input text box for entering text plus an HTML input submit button for posting the text back to the server. Figure 6 shows the appearance of the text_input content in Visual Studio .NET.

Figure 6. The text_input.ascx file provides the content for the text_input.aspx Web page.
Creating a Web Page Template, Figure 6.

If you request the Text Input page of the demo, you can test its operation for yourself. The server returns the text you enter while retaining the original URL. In the code behind the text_input.ascx file, shown in Listing 9, the Page_Load and btnSubmit_ServerClick procedures work together to accomplish this result.

Listing 9. The event handlers in text_input.ascx.vb return data entered at the browser while concealing the URL of the template.

Option Strict On
Imports System.Web.UI.HtmlControls

Public MustInherit Class text_input
  Inherits ControlBase
  Protected WithEvents txtInput As HtmlInputText
  Protected WithEvents btnSubmit As HtmlInputButton
  Protected WithEvents pReply As HtmlGenericControl

#Region " Web Form Designer Generated Code "
  'This call is required by the Web Form Designer.
  <System.Diagnostics.DebuggerStepThrough()> _
  Private Sub InitializeComponent()

  End Sub

  Private Sub Page_Init(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles MyBase.Init
    'CODEGEN: This method call is required 
    '  by the Web Form Designer
    'Do not modify it using the code editor.
    InitializeComponent()
  End Sub
#End Region

  Private Const sPage As String = "text_input"

  Private Sub Page_Load(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles MyBase.Load
    Me.Title = "Visual Web Page Template Demo: Text Input"
    Response.Cache.SetNoServerCaching()
    Dim sInput As String = CStr(Session(sPage))
    If Not sInput = String.Empty Then
      txtInput.Value = sInput
      pReply.InnerText = "You submitted: " & sInput
      Session(sPage) = Nothing
    End If
  End Sub

  Private Sub btnSubmit_ServerClick( _
  ByVal sender As System.Object, _
  ByVal e As System.EventArgs) _
  Handles btnSubmit.ServerClick
    Session(sPage) = txtInput.Value
    Response.Redirect(sPage & ".aspx")
  End Sub
End Class

Here is the example I promised where output caching should be turned off. If you plan to return data to the visitor in the same Web page that captured the data, output caching will defeat your purpose: ASP.NET will return the original page from the cache without ever executing your code. In Listing 9, the Page_Load procedure kills output caching (for the text_input page only) like this:

Response.Cache.SetNoServerCaching()

Check the time stamps on the demo pages to verify that output caching does not apply to the text_input page.

The text_input page conceals the template URL after postback by means of these two statements in the btnSubmit_ServerClick procedure:

Session(sPage) = txtInput.Value
Response.Redirect(sPage & ".aspx")

The code saves the value entered by the visitor in a session state variable, then uses the Response.Redirect method to request a fresh copy of the Web page. When issuing the response to this request, the Page_Load procedure retrieves the value from session state and returns it to the visitor.

I've taken you through this demo to illustrate the basic concepts of this simple visual Web page template technique. You can, of course, add many bells and whistles to the basic structure if your Web application needs them.

But there are many ways to skin a cat. To conclude this article, I'm going to provide some information about alternative approaches that you might take.

Selecting a Template Approach

If the content for your Web site resides in a database, you can use a visual Web page template technique identical to the one described in this article, except that the content comes from the database instead of from ASCX files. Here too, a single ASPX file serves up many different Web pages.

In Chapter 15 of our book Programming the Web with Visual Basic .NET, we provide a complete case study that contains two visual Web page templates of this type. One template provides database content selected by the visitor via either a search or a browse. The other template provides browse listings by category. Our sample chapter contains the Application_BeginRequest procedure needed to distinguish between the templates, plus other code required for the database approach.

The visual Web page template technique explained in this article works well for many Web sites, particularly content-centric sites. However, there is no "one size fits all" in Web programming. I recommend that you look at and evaluate the approaches described by other authors too before you settle upon an approach for your project.

Jonathan Goodyear wrote offsite link.Create Page Templates and, later, offsite link.Page Templates Revisited in Visual Studio Magazine. Philip Quinn wrote offsite link.Page Templates, available at ASPalliance.com. Peter Provost wrote offsite link.ASP.NET Page Templates, available at The Code Project. All of these authors describe ways to use inheritance to build a Web site with a consistent look and feel. Paul Wilson maintains a offsite link.Web site that uses inheritance throughout, with source code available for download. By reading the information at these links, you can determine how other Web page template approaches compare with the technique described in this article.

Only you can determine the best approach for your project. Factors to consider include the development time available, the programming skills of your team, and the unique requirements of your Web application. Whichever approach you choose, creating a Web page template will improve both the consistency and maintainability of your Web site.

Downloading the Source Code

To try out the demo I used in this article, and to experiment with it yourself, download the source code and give it a test drive. Here's how to install it:

  1. In Visual Studio .NET, create a Visual Basic ASP.NET Web Application named VbTemplate.
  2. Unzip the files in VbTemplate_Code.zip and copy them to Inetpub\wwwroot\VbTemplate (overwrite existing files).
  3. In the Visual Studio Solution Explorer, click the "Show All Files" icon.
  4. Select the new ASPX, ASCX, and VB files, right click, then choose "Include In Project".
  5. Toggle off the "Show All Files" options (unless you prefer otherwise).
  6. Right click index.aspx and select "Set As Start Page".
  7. Delete WebForm1.aspx.

Home   Articles We've Written   Web Templates

Copyright © 1996 - 2014 SoftMedia Artisans, Inc. All Rights Reserved.