|
|||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionAnyone 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
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 ControlsOne 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
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 InheritanceIf 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
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 Now we will create an ASPX page that derives from
<%@ 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
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 InheritanceThe 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
There are many different ways we could solve this problem. For this example, we will move the
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 This system works very well and allows us to create ASPX pages that are completely independent of the template. Here is the ASPX file:
<%@ 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. PerformanceI 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 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.
ConclusionDeveloping 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. | ||||||||||||||||||||||||||||||||||||