Four Helpful Features to Add to Your Base Page Class

Published: 12/18/2008
By: Scott Mitchell

Scott Mitchell explains how to create an ASP.NET base Page class that includes extra functionality.

Contents [hide]

Introduction

All ASP.NET pages extend the Page class in the System.Web.UI namespace, which defines the base functionality, events, methods, and properties common to all ASP.NET pages. This includes common objects like Request, Response, and Session, along with the mechanisms for serializing view state, injecting client-side script, and managing the ASP.NET page's lifecycle. You can provide additional functionality to all your ASP.NET pages in your application by:

  1. Creating your own class that includes the extra functionality desired,
  2. Have this class derive from the .NET Framework's Page class, and
  3. Configure your ASP.NET pages to extend the custom class created in step 1, rather than the .NET Framework's Page class.

Such custom classes are referred to as base page classes. For more information on creating and using base page classes check out Using a Custom Base Class for your ASP.NET Pages' Code-Behind Classes.

Base page classes usually contain customizations that are specific to the application for which they were created, but can also contain more common functionality that, for one reason or another, was not provided by Microsoft in their Page class. This article shares four helpful features you can add to your base page class.

Note:

The complete code for the following features is available for download from this article, along with a demo page. The code snippets below show just the code relevant to the particular feature being examined.

Display a JavaScript Alert

The JavaScript alert(message) function displays the specified message in a modal dialog and is commonly used to display status information. For example, you can have any user input validation errors displayed in a client-side dialog box by adding a ValidationSummary control to your page and setting its ShowMessageBox property to true. Doing so causes the ValidationSummary control to inject JavaScript on the page that uses the alert function to display a summary of validation errors.

Much like how the ValidationSummary works, you can display your own client-side dialog boxes by injecting JavaScript from the page's server-side code. This involves using the ClientScript class and entering the JavaScript syntax to emit, and is rather cumbersome. A better approach is to encapsulate this logic into a method in the base page class. After doing this, any ASP.NET page that extends the base page class can display a client-side dialog box by calling this method.

The base page class in the download includes a method named DisplayAlert that takes a string as input and emits the client-side script to display the passed-in string in a dialog box.

The DisplayAlert method can be called from any ASP.NET page that extends the custom base page class with the following code:

When the page is visited the necessary JavaScript code is sent back with the page's HTML and the browser, upon receiving and interpreting the JavaScript, displays a message box with the greeting, "Hello, world!"

Recursively Search the Control Hierarchy

All ASP.NET controls include a FindControl(id) method that searches the children controls for a one whose ID property is equal to the passed-in id string. Unfortunately, FindControl does not penetrate through naming containers, which are commonly used in templated controls. As a result, it can be a pain to get a programmatic reference to those controls within templated controls. The good news is that it's not at all hard to write a recursive FindControl method, and such a method is a good fit for a base page class.

My implementation below has two overloads: the first accepts just an id input parameter and starts the search at the root of the page's control hierarchy; the second one allows for a starting node in the hierarchy. If you are searching within a particular control, use the second method so as to narrow the search space.

The demo available for download shows this method in action with a LoginView control. The LoginView control is composed of templates and renders one of its templates based on the user visiting the page. For example, if the visitor is anonymous then the LoginView's AnonymousTemplate is rendered. Because the LoginView control is composed of templates and because these templates act as naming containers, you have to use the FindControl method to programmatically work with any of the Web controls within its templates. However, you have to be certain to use the FindControl method from the parent container because it does not penetrate naming containers.

To better understand this, consider the following LoginView, which has a TextBox named UserNameInTextBox in its LoggedInTemplate and a Label named CurrentTime in its AnonymousTemplate.

To set the CurrentTime Label control's Text property you need to first use the FindControl method to get a reference to it. But you need to be careful here because the FindControl method's search does not penetrate naming containers and the LoginView's template constitutes a naming container. Consequently, the following call to FindControl will return null:

Instead, you have to start at the parent container, LoginView:

Note the difference in the two code snippets above: one calls the Page class's FindControl method, the other calls the LoginView's FindControl method. The first returns null because the search does not drill into the LoginView's templates to find CurrentTime, but the second one succeeds because it starts its search from that same naming container that the Label exists in.

A simpler approach is to use our new recursive method, FindControlRecursive. Because this method plows through naming containers you can search starting at the top of the page's control hierarchy (the default behavior) and it will still find the control nestled within the LoginView's template (assuming that the page is being requested by an anonymous visitor).

It may seem a bit superfluous to create a FindControlRecursive method for this particular example; after all, we can reference the Label by calling the LoginView control's FindControl method. But imagine that the LoginView control was itself within a naming container. Then in order to reference the Label you would have to first reference the LoginView using FindControl from its parent naming container and then use FindControl again to get the Label. Our recursive FindControl implementation does not require this extra FindControl call.

A Generics-based implementation of a recursive FindControl method can be found at Steve Smith's blog: Recursive FindControl.

Record Page Execution Times

It's helpful at times to know how long it took a particular ASP.NET page to be processed on the server, or how long a particular operation took. While ASP.NET's tracing feature provides this information and more, sometimes that blob of information is much more than is needed. A simpler approach is to have the base page optionally record and display the page's execution time.

Because we might not want to measure and record the execution time on all pages, it's a good idea to add a property to the base page class that indicates whether these measurements are to be made. The base page class available for download has a Boolean property named MeasureExecutionTime that does just that; it defaults to false.

A Stopwatch class instance named watch is used to measure the execution time. This object is instantiated and started in the base page's OnInit method, which executes during the Init stage of the page lifecycle.

The base page class includes a method named RecordTimestamp that, when called, stores the number of elapsed milliseconds and passed-in description in a List of TimestampInfo objects. The TimestampInfo class includes properties that record the elapsed time (in milliseconds) along with a human-friendly description. A page developer can call RecordTimestamp whenever he would like to record the elapsed time. During the PreRenderComplete event, the Stopwatch object is stopped and the times are displayed on the page. Specifically, the times are tacked on immediately before the closing </form> tag using a series of LiteralControl instances.

Note that the MeasureExecutionTime property defaults to false. Therefore, to use this functionality you will need to explicitly set this property to true in your ASP.NET pages in the Page_Init event handler. The following code shows an ASP.NET page that uses this page execution timing feature to not only track the total page load time, but also to determine how long a particular operation takes. In this case we are timing a call to Thread.Sleep; in practice you would be interested in timing potentially long running tasks, like Web Service calls or database queries.

As the screen shot in Figure 2 shows, the output isn't pretty, but the idea is that this information is used solely for diagnostic purposes and is not intended to be displayed on a production website.

Setting the Page Title

The Page class in the .NET Framework includes a Title property that can be used to programmatically set the text in the page's <title> element. The browser displays the text within the <title> element in the window's title bar, it is the default text used when a page is bookmarked, and search engines typically display this title when listing search results. Consequently, it's a good idea to give your page's meaningful titles. Unfortunately, Visual Studio gives each new ASP.NET page a default title of "Untitled Page," which means if you forget to change this you'll end up with a lot of pages on your site with the same meaningless title.

Note:

Here's a fun bit of trivia: you can use Google's search filter allintitle:"title" to search for web pages with a particular title. A search for "Untitled Page" returns over 8.1 million results! You can use the site:domainName filter to see how many pages in Google's index are named "Untitled Page" on your site (or anyone else's). As of the time of this writing Google reports that there are 7,920 web pages titled "Untitled Page" on microsoft.com.

An easy fix is to beef up your base page class so that it sets the page's title automatically (if it was not already set). The base page class available for download has a method called SetPageTitle that is automatically called during the Load event. If the base page class property AutoSetPageTitle is true (the default) and the page's Title property has not yet been set or is set to "Untitled Page", then the page's Title property is set based on the site map or the page's file name.

If your site is configured to use ASP.NET's site map feature then those pages that are referenced in the site map will have their title set to the value: Title (RootTitle :: GrandParentTitle :: ... :: ParentTitle)

Where Title is the title of the current page in the site map, RootTitle is the title of the root node in the site map, and GrandParentTitle down to ParentTitle are the titles of the current node's ancestors. The demo web page is located in the site map in the following hierarchy:

The title of the demo page in the site map is: "Teach Yourself ASP.NET 3.5 in 24 Hours." As a result, the base page sets this page's title to: Teach Yourself ASP.NET 3.5 in 24 Hours (Home :: Books :: Non Fiction)

If the page is not in the site map, or if the site map feature is disabled, then the Title property is set to the file name of the page (less the extension). In other words, if the page is titled About.aspx the title will be set to About.

Happy Programming!

Further Reading

Please visit the link at the below url for any additional user comments.

Original Url: http://dotnetslackers.com/articles/aspnet/Four-Helpful-Features-to-Add-to-Your-Base-Page-Class.aspx