Your application uses a common page format for most of its pages, and you want to reuse and simplify the maintenance of the shared HTML.
Review the HTML to determine what is common on all of your pages, and then create a user control for each of the common sections. The HTML page used as the basis for our example is shown in Example 18-21. Our user control for the top of the page is shown in Example 18-22 through Example 18-24 and for the bottom of the page in Examples Example 18-25 through Example 18-27.
Next, create a base page class that inherits from
System.Web.UI.Page
and loads the user controls you
created at runtime. This base page class, along with the user
controls containing the HTML for your pages, constitutes a
“reusable HTML page template.”
Example 18-28 and Example 18-29 show the VB and C# versions of our base page
class that demonstrate this solution.
You can then create the pages for your application by inheriting from
the base class you have created instead of
System.Web.UI.Page
and add only the HTML that is
unique to each page. Example 18-30 through
Example 18-32 show the .aspx
file
and VB and C# code-behind file for an application that demonstrate
the use of the base page class.
Most applications use a common HTML design for the majority of their pages, and developers often find themselves cutting and pasting the common HTML in order to repeat the design on each page. Because of the replication, maintenance of the HTML is time consuming, expensive, and prone to mistakes. What is needed is the ability to code the HTML in one place and reuse it many times. This recipe describes how to implement a common design by reusing HTML.
The first step to creating reusable HTML is to analyze the HTML of your application pages to determine what elements are common across all of them. In our example that illustrates this solution, the two sections highlighted in Example 18-21 have been identified as being common across all pages. Figure 18-7 shows the output of the page in our example.
For each common section, you need to create a user control. In our
example, the first common section is placed in the
CH18PageTopVB
(VB) or
CH18PageTopCS
(C#) user control and the second
common section is placed in the CH18PageBottomVB
(VB) or CH18PageBottomCS
(C#) user control, as
shown in Example 18-22 and Example 18-25. The title element and the table cell used to
place a label at the top of a page in the
CH18PageTopxx
user control have been modified from
the original HTML to provide the ability to change the values of the
title and page heading in the code-behind.
It may look a little odd to have HTML fragments in the user controls, but it is important to remember that the browser will only see the rendered page, which includes the HTML for each of the user controls, resulting in proper, well-formed HTML.
To provide the ability to change the values in the pages that use our
example template, we have added the pageTitle
and
pageHeading
properties to the code-behind of the
CH18PageTopxx
user control.
Our CH18PageBottomxx
user control does not require
any variation in the pages using the template; therefore, the
code-behind for the user control does not require any additional
code.
After creating the user controls containing the HTML to be reused, a
new class must be created that will be used as the base class for all
pages in your application. This class must inherit from the
System.Web.UI.Page
class to provide the base
functionality needed for a page.
To provide the ability to change the properties in the pages that use
our example template, the base page class must contain a property for
each of the properties in the user controls. In our example, because
a pageTitle
and a pageHeading
property were provided in the CH18PageTopxx
user
control, they must be added to the base page class.
The check at the beginning of each of the properties is necessary to
avoid an error message indicating an object reference is not set to
an instance of an object when using Visual Studio .NET. The error is
caused by the designer accessing the properties for display purposes,
but the user controls are not created in the designer, resulting in
mPageTop
and mPageHeading
being
null.
The OnInit
method in our example must be
overridden from the Page
class to load the
CH18PageTopVB
(or
CH18PageTopCS
) and
CH18PageBottomVB
(or
CH18PageBottomCS
) user controls when the
CH18BaseClassVB
(or
CH18BasePageCS
) object is created.
Protected Overrides Sub OnInit(ByVal e As System.EventArgs) 'load the user control containing the template for the top of the page 'and add it as the first control in the pagemPageTop = CType(LoadControl("CH18PageTopVB.ascx"), _
CH18PageTopVB)
MyClass.Controls.AddAt(0, mPageTop)
'take care of the base class initialization MyBase.OnInit(e) 'load the user control containing the template for the bottom of the page 'and add it at the end of the controls in the pagemPageBottom = CType(LoadControl("CH18PageBottomVB.ascx"), _
CH18PageBottomVB)
MyClass.Controls.Add(mPageBottom)
End Sub 'OnInit protected override void OnInit(System.EventArgs e) { // take care of the base class initialization base.OnInit(e); // load the user control containing the template for the top of the page // and add it as the first control in the pagemPageTop = (CH18PageTopCS)(LoadControl("CH18PageTopCS.ascx"));
base.Controls.AddAt(0, mPageTop);
// load the user control containing the template for the bottom of the page // and add it at the end of the controls in the pagemPageBottom = (CH18PageBottomCS)(LoadControl("CH18PageBottomCS.ascx"));
base.Controls.Add(mPageBottom);
} // OnInit
In our example, The CH18PageTopxx
user control
must be the first control on the page because it contains the HTML
for the top of the page. In addition, the
CH18PageBottomxx
user control must be the last
control on the form because it contains the HTML for the bottom of
the page. Placing these controls at any other position in the
controls collection will result in invalid HTML being rendered.
To use the page template, you need to create a web form as you
normally would, but with two changes. First, the
.aspx
file should only include the HTML that is
unique to the page being implemented, as shown in Example 18-30. In our example, the unique HTML is a simple
table to provide the ability to format the content for the page.
The second modification is to change the inherited class for the
code-behind class. This should be changed from the normal
System.Web.UI.Page
to the base class you created.
In our example, the code-behind class inherits from
CH18BasePageVB
(or
CH18BasePageCS
for C#), which will result in the
common HTML being output for the page.
In our example, two properties were included in the base page class
to demonstrate how to reuse the HTML yet provide the ability to
customize the output for each page. The properties must be set in the
PageLoad
method, as shown here:
Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load If (Not Page.IsPostBack) Then 'set the page titlepageTitle = "Using the page template"
'set the page headingpageHeading = "Simple Example Of Using A Page Template"
... End If 'If (Not Page.IsPostBack) End Sub 'Page_Load private void Page_Load(object sender, System.EventArgs e) { if (!Page.IsPostBack) { // set the page titlepageTitle = "Using the page template";
// set the page headingpageHeading = "Simple Example Of Using A Page Template";
... } // if (!Page.IsPostBack)
Our example uses fairly simple HTML to demonstrate the implementation of a reusable HTML page template, but the technique is not limited to simple implementations. In fact, the more complicated the HTML, the more important it is to apply reuse to reduce the maintenance cost of the presentation portion of your application.
Chapter 4 for user controls
Example 18-21. Original HTML page
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Original HTML Template</title>
<link rel="stylesheet" href="css/ASPNetCookbook.css">
</head>
<body leftmargin="0" marginheight="0" marginwidth="0" topmargin="0">
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td align="center">
<img src="images/ASPCookbookHeading_blue.gif">
</td>
</tr>
<tr>
<td bgcolor="#6B0808"><img src="images/spacer.gif" height="6"></td>
</tr>
</table>
<table width="90%" align="center" border="0">
<tr>
<td><img src="images/spacer.gif" height="10" border="0"></td>
</tr>
<tr>
<td align="center" class="PageHeading">
<!-- Put page heading here -->
</td>
</tr>
<tr>
<td><img src="images/spacer.gif" height="10" border="0"></td>
</tr>
<tr>
<td align="center">
<form method="post"> <!-- Put page content here --> </form></td>
</tr>
</table>
</body>
</html>
Example 18-22. User control for top of page (.ascx)
<%@ Control Language="vb" AutoEventWireup="false" Codebehind="CH18PageTopVB.ascx.vb" Inherits="ASPNetCookbook.VBExamples.CH18PageTopVB" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head><title id="mPageTitle" runat="server"></title>
<link rel="stylesheet" href="css/ASPNetCookbook.css"> </head> <body leftmargin="0" marginheight="0" marginwidth="0" topmargin="0"> <table width="100%" cellpadding="0" cellspacing="0" border="0"> <tr> <td align="center"> <img src="images/ASPNETCookbookHeading_blue.gif"> </td> </tr> <tr> <td class="dividerLine"> <img src="images/spacer.gif" height="6" border="0"></td> </tr> </table> <table width="100%" align="center" border="0"> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <tr><td id="mPageHeading" runat="server"
align="center" class="PageHeading">
</td> </tr> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <tr> <td align="center">
Example 18-23. User control for top of page code-behind (.vb)
Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: CH18PageTopVB.ascx.vb ' ' Description: This class provides the code-behind for CH18PageTopVB.ascx ' '***************************************************************************** Namespace ASPNetCookbook.VBExamples Public Class CH18PageTopVB Inherits System.Web.UI.UserControl 'controls in the user controlProtected mPageTitle As System.Web.UI.HtmlControls.HtmlGenericControl
Protected mPageHeading As System.Web.UI.HtmlControls.HtmlTableCell
'************************************************************************* ' ' ROUTINE: pageTitle ' ' DESCRIPTION: This routine provides the ability to get/set the ' pageTitle property '-------------------------------------------------------------------------Public Property pageTitle( ) As String
Get
Return (mPageTitle.InnerText)
End Get
Set(ByVal Value As String)
mPageTitle.InnerText = Value
End Set
End Property 'pageTitle
'************************************************************************* ' ' ROUTINE: pageHeading ' ' DESCRIPTION: This routine provides the ability to get/set the ' pageHeading property '-------------------------------------------------------------------------Public Property pageHeading( ) As String
Get
Return (mPageHeading.InnerText)
End Get
Set(ByVal Value As String)
mPageHeading.InnerText = Value
End Set
End Property 'pageHeading
'************************************************************************* ' ' ROUTINE: Page_Load ' ' DESCRIPTION: This routine provides the event handler for the page load ' event. It is responsible for initializing the controls ' on the page. '------------------------------------------------------------------------- Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load 'Put user code to initialize the page here End Sub 'Page_Load End Class 'CH18PageTopVB End Namespace
Example 18-24. User control for top of page code-behind (.cs)
//---------------------------------------------------------------------------- // // Module Name: CH18PageTopCS.ascx.cs // // Description: This module provides the code behind for CH18PageTopCS.ascx // //**************************************************************************** namespace ASPNetCookbook.CSExamples { using System; public abstract class CH18PageTopCS : System.Web.UI.UserControl { // controls in the user controlprotected System.Web.UI.HtmlControls.HtmlGenericControl mPageTitle;
protected System.Web.UI.HtmlControls.HtmlTableCell mPageHeading;
//************************************************************************ // // ROUTINE: pageTitle // // DESCRIPTION: This routine provides the ability to get/set the // pageTitle property // //------------------------------------------------------------------------public String pageTitle
{
get
{
return(mPageTitle.InnerText);
}
set
{
mPageTitle.InnerText = value;
}
} // pageTitle
//************************************************************************ // // ROUTINE: pageHeading // // DESCRIPTION: This routine provides the ability to get/set the // pageHeading property // //------------------------------------------------------------------------public String pageHeading
{
get
{
return(mPageHeading.InnerText);
}
set
{
mPageHeading.InnerText = value;
}
} // pageHeading
//************************************************************************ // // ROUTINE: Page_Load // // DESCRIPTION: This routine provides the event handler for the page // load event. It is responsible for initializing the // controls on page. // //------------------------------------------------------------------------ private void Page_Load(object sender, System.EventArgs e) { // Put user code to initialize the page here } // Page_Load } // CH18PageTopCS }
Example 18-25. User control for bottom of page (.ascx)
<%@ Control Language="vb" AutoEventWireup="false" Codebehind="CH18PageBottomVB.ascx.vb" Inherits="ASPNetCookbook.VBExamples.CH18PageBottomVB" %> </td> </tr> </table> </form> </body> </html>
Example 18-26. User control for bottom of page code-behind (.vb)
Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: CH18PageBottomVB.ascx.vb ' ' Description: This class provides the code-behind for CH18PageBottomVB.ascx ' '***************************************************************************** Namespace ASPNetCookbook.VBExamples Public Class CH18PageBottomVB Inherits System.Web.UI.UserControl '************************************************************************* ' ' ROUTINE: Page_Load ' ' DESCRIPTION: This routine provides the event handler for the page load ' event. It is responsible for initializing the controls ' on the page. '------------------------------------------------------------------------- Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load 'Put user code to initialize the page here End Sub 'Page_Load End Class 'CH18PageBottomVB End Namespace
Example 18-27. User control for bottom of page code-behind (.cs)
//---------------------------------------------------------------------------- // // Module Name: CH18PageBottomCS.ascx.cs // // Description: This module provides the code behind for // CH18PageBottomCS.ascx // //**************************************************************************** using System; namespace ASPNetCookbook.CSExamples { public abstract class CH18PageBottomCS : System.Web.UI.UserControl { //************************************************************************ // // ROUTINE: Page_Load // // DESCRIPTION: This routine provides the event handler for the page // load event. It is responsible for initializing the // controls on the page. //------------------------------------------------------------------------ private void Page_Load(object sender, System.EventArgs e) { // Put user code to initialize the page here } // Page_Load } // CH18PageBottomCS }
Example 18-28. Base page class (.vb)
Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: CH18BasePageVB.vb ' ' Description: This class provides the class used as the base class from ' which all ASPX pages in the application are derived. It ' provides the basic template for all of the common user ' interface. ' '***************************************************************************** Imports Microsoft.VisualBasic Namespace ASPNetCookbook.VBExamples Public Class CH18BasePageVB Inherits System.Web.UI.Page'the following variables are used for references to the user controls
'that contain the top and bottom sections of the page template
Private mPageTop As CH18PageTopVB
Private mPageBottom As CH18PageBottomVB
'************************************************************************* ' ' ROUTINE: pageTitle ' ' DESCRIPTION: This routine provides the ability to get/set the ' pageTitle property '-------------------------------------------------------------------------Public Property pageTitle( ) As String
Get
Dim title As String = String.Empty
If (Not IsNothing(mPageTop)) Then
title = mPageTop.pageTitle
End If
Return (title)
End Get
Set(ByVal Value As String)
If (Not IsNothing(mPageTop)) Then
mPageTop.pageTitle = Value
End If
End Set
End Property 'pageTitle
'************************************************************************* ' ' ROUTINE: pageHeading ' ' DESCRIPTION: This routine provides the ability to get/set the ' pageHeading property '-------------------------------------------------------------------------Public Property pageHeading( ) As String
Get
Dim title As String = String.Empty
If (Not IsNothing(mPageTop)) Then
title = mPageTop.pageHeading
End If
Return (title)
End Get
Set(ByVal Value As String)
If (Not IsNothing(mPageTop)) Then
mPageTop.pageHeading = Value
End If
End Set
End Property 'pageHeading
'************************************************************************* ' ' ROUTINE: OnInit ' ' DESCRIPTION: This routine provides the event handler for the OnInit ' event. It is responsible for loading the user controls ' containing the page templates and adding them to the ' page controls collection. '-------------------------------------------------------------------------Protected Overrides Sub OnInit(ByVal e As System.EventArgs)
'load the user control containing the template for the top of the page
'and add it as the first control in the page
mPageTop = CType(LoadControl("CH18PageTopVB.ascx"), _
CH18PageTopVB)
MyClass.Controls.AddAt(0, mPageTop)
'take care of the base class initialization
MyBase.OnInit(e)
'load the user control containing the template for the bottom of the page
'and add it at the end of the controls in the page
mPageBottom = CType(LoadControl("CH18PageBottomVB.ascx"), _
CH18PageBottomVB)
MyClass.Controls.Add(mPageBottom)
End Sub 'OnInit
End Class 'CH18BasePageVB End Namespace
Example 18-29. Base page class (.cs)
//---------------------------------------------------------------------------- // // Module Name: CH18BasePageCS.cs // // Description: This class provides the class used as the base class from // which all ASPX pages in the application are derived. It // provides the basic template for all of the common user // interface. // //**************************************************************************** using System; namespace ASPNetCookbook.CSExamples { public class CH18BasePageCS : System.Web.UI.Page {// the following variables are used for references to the user controls
// that contain the top and bottom sections of the page template
private CH18PageTopCS mPageTop;
private CH18PageBottomCS mPageBottom;
//************************************************************************ // // ROUTINE: pageTitle // // DESCRIPTION: This routine provides the ability to get/set the // pageTitle property //------------------------------------------------------------------------public String pageTitle
{
get
{
String title = String.Empty;
if (mPageTop != null)
{
title = mPageTop.pageTitle;
}
return(title);
}
set
{
if (mPageTop != null)
{
mPageTop.pageTitle = value;
}
}
} // pageTitle
//************************************************************************ // // ROUTINE: pageHeading // // DESCRIPTION: This routine provides the ability to get/set the // pageHeading property //------------------------------------------------------------------------public String pageHeading
{
get
{
String title = String.Empty;
if (mPageTop != null)
{
title = mPageTop.pageHeading;
}
return(title);
}
set
{
if (mPageTop != null)
{
mPageTop.pageHeading = value;
}
}
} // pageHeading
// *********************************************************************** // // ROUTINE: OnInit // // DESCRIPTION: This routine provides the event handler for the OnInit // event. It is responsible for loading the user controls // containing the page templates and adding them to the // page controls collection. //---------------------------------------------------------------------------protected override void OnInit(System.EventArgs e)
{
// take care of the base class initialization
base.OnInit(e);
// load the user control containing the template for the top of the page
// and add it as the first control in the page
mPageTop = (CH18PageTopCS)(LoadControl("CH18PageTopCS.ascx"));
base.Controls.AddAt(0, mPageTop);
// load the user control containing the template for the bottom of the page
// and add it at the end of the controls in the page
mPageBottom = (CH18PageBottomCS)(LoadControl("CH18PageBottomCS.ascx"));
base.Controls.Add(mPageBottom);
} // OnInit
} // CH18BasePageCS }
Example 18-30. Using the base page class (.aspx)
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="CH18UsingThePageTemplateVB.aspx.vb" Inherits="ASPNetCookbook.VBExamples.CH18UsingThePageTemplateVB"%> <form method="post" id="frmUsingPageTemplate" runat="server"> <!-- Put page content here --> <table width="80%" border="0" align="center"> <tr> <td> <asp:Xml ID="xmlTransform" Runat="server"/> </td> </tr> </table> </form>
Example 18-31. Using the base page class code-behind (.vb)
Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: CH18UsingThePageTemplateVB.aspx.vb ' ' Description: This class provides the code-behind for ' CH18UsingThePageTemplateVB.aspx ' '***************************************************************************** Imports System Namespace ASPNetCookbook.VBExamples Public ClassCH18UsingThePageTemplateVB
Inherits CH18BasePageVB 'controls on the form Protected xmlTransform As System.Web.UI.WebControls.Xml '************************************************************************* ' ' ROUTINE: Page_Load ' ' DESCRIPTION: This routine provides the event handler for the page load ' event. It is responsible for initializing the controls ' on the page. '------------------------------------------------------------------------- Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load If (Not Page.IsPostBack) Then'set the page title
pageTitle = "Using the page template"
'set the page heading
pageHeading = "Simple Example Of Using A Page Template"
'initialize controls on the specific page 'set the names of the XML and XSLT documents used in the 'transformation xmlTransform.DocumentSource = "xml/books.xml" xmlTransform.TransformSource = "xml/books.xslt" End If 'If (Not Page.IsPostBack) End Sub 'Page_Load End Class 'CH18UsingThePageTemplateVB End Namespace
Example 18-32. Using the base page class code-behind (.cs)
//---------------------------------------------------------------------------- // // Module Name: CH18UsingThePageTemplateCS.aspx.cs // // Description: This module provides the code behind for the // CH18UsingThePageTemplateCS.aspx page // //**************************************************************************** using System; namespace ASPNetCookbook.CSExamples { public class CH18UsingThePageTemplateCS :CH18BasePageCS
{ // controls on the form protected System.Web.UI.WebControls.Xml xmlTransform; //************************************************************************ // // ROUTINE: Page_Load // // DESCRIPTION: This routine provides the event handler for the page // load event. It is responsible for initializing the // controls on the page. //------------------------------------------------------------------------ private void Page_Load(object sender, System.EventArgs e) { if (!Page.IsPostBack) {// set the page title
pageTitle = "Using the page template";
// set the page heading
pageHeading = "Simple Example Of Using A Page Template";
// initialize controls on the specific page // set the names of the XML and XSLT documents used in the // transformation xmlTransform.DocumentSource = "xml//books.xml"; xmlTransform.TransformSource = "xml//books.xslt"; } // if (!Page.IsPostBack) } // Page_Load } // CH18UsingThePageTemplateCS }