Click here to Skip to main content
Click here to Skip to main content
Go to top

Documenting the Life Cycle of an ASP Page

, 2 Sep 2010
Rate this:
Please Sign up or sign in to vote.
A simple framework for documenting the events of master and child pages and web controls.

Introduction

When coordinating code between a master page, a child page and several controls, it can be very useful to have a listing of when each event is fired. There are a lot of lists on the web but they are rarely complete, and I have yet to find the code on how the lists were created. In the end, I wrote my own testing framework, which is the topic of this article.

It turns out to be very simple: override methods to make a note of what is happening, then continue. I did this by creating custom classes to replace the standard Page and MasterPage objects, and by writing "wrapper" web controls. If you are familiar with these techniques or are just looking for a reference, feel free to go to the Results sections at the bottom.

This article is targeted towards beginner-ish web programmers who already know the basics of web programming and have a test site where they can experiment. It was written and tested using ASP.NET 2.0, but there's nothing here that should break with any later versions of .NET. I used VB because that is the language I use every day; the code is simple and should translate into C# very easily.

TestChild and TestMaster

The first thing I did was create the TestChild and TestMaster classes, which will replace the regular Page and MasterPage classes. The advantage to putting the code in separate classes is that we can have several different test pages without having to copy the code. They start off looking like this:

Public Class TestChild
    Inherits System.Web.UI.Page

End Class

Public Class TestMaster
    Inherits System.Web.UI.MasterPage

End Class

Next is to override the methods we want to document. All of these overrides look the same: they write some text into the response stream, then call the same method in the base class. Here is a typical example:

Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
    Response.Write("Child Load<br/>" + vbCrLf)
    MyBase.OnLoad(e)
End Sub

The break tag will put the text on its own line when the page is delivered to the browser, and the carriage return/line feed will put a break in the page's source. These just make the text easier to read. The OnLoad override in TestMaster is identical, except that it writes "Master Load".

In TestChild, I overrode these methods:

  • LoadControlState
  • LoadViewState
  • OnDataBinding
  • OnInit
  • OnInitComplete
  • OnLoad
  • OnLoadComplete
  • OnPreInit
  • OnPreLoad
  • OnPreRender
  • OnPreRenderComplete
  • OnSaveStateComplete
  • Render
  • RenderChildren
  • RenderControl
  • SaveControlState
  • SaveViewState

Master pages do not have as many interesting methods. Here is what I overrode in TestMaster:

  • LoadControlState
  • LoadViewState
  • OnDataBinding
  • OnInit
  • OnLoad
  • OnPreRender
  • Render
  • RenderChildren
  • RenderControl
  • SaveControlState
  • SaveViewState

You may have noticed that I did not override the OnUnload method. This is because the Unload event is fired after the page has been rendered; Response no longer exists so an error gets thrown.

TestButton and TestGrid

To get information on control events, we need to create custom controls that will report back. I chose to write a button and a grid, because the ordering of Click and DataBinding is usually where I go wrong (I'm not the only one to change a grid's data before the data is reloaded, am I?)

Namespace TestControls

    Public Class TestButton
        Inherits System.Web.UI.WebControls.Button

        Protected Function Response() As HttpResponse
            Return HttpContext.Current.Response
        End Function

    End Class

    Public Class TestGrid
        Inherits System.Web.UI.WebControls.GridView

        Protected Function Response() As HttpResponse
            Return HttpContext.Current.Response
        End Function

    End Class

End Namespace

Web controls do not have a Response method so I added one; this is just to make the code a bit cleaner and easier to maintain.

By putting the controls in a namespace, we can configure the website to attach a prefix for IntelliSense. I'm not sure if this is actually necessary, but it is useful enough that I always do it. In the web.config file, find the <pages> block inside <system.web>, and add this:

<controls>
    <add tagPrefix="test" namespace="TestControls"/>
</controls>

As with TestChild and TestMaster, we override the methods we want to document so they write to the response stream and call the underlying method. The only difference is that we use the ID property of the control rather than static text; this way, we can have several controls on a page and know which message belongs to which control.

Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
    Response.Write(Me.ID + " PreRender<br/>" + vbCrLf)
    MyBase.OnPreRender(e)
End Sub

Most of the interesting methods I wanted to document were in both controls:

  • LoadControlState
  • LoadViewState
  • OnDataBinding
  • OnInit
  • OnLoad
  • OnPreRender
  • Render
  • RenderChildren
  • RenderContents
  • RenderControl
  • SaveControlState
  • SaveViewState

In TestButton, I also overrode OnClick and OnCommand. In TestGrid, I overrode OnDataBound.

For the grid, let's create a very simple XML data file that we can link to.

<?xml version="1.0" encoding="utf-8" ?>
<People>
    <Person firstName="Gregory" lastName="Gadow"/>
</People>

Test.master and Default.aspx

Now we are ready to actually create some pages. For this demo, I am using a single master page and a single child page.

<%@ Master Language="VB" Inherits="TestMaster"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
	"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
</head>
<body>
<form id="form1" runat="server">
    <asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server" />
</form>
</body>
</html>
<%@ Page Language="VB" Inherits="TestChild" 
	MasterPageFile="~/Test.master" Title="Untitled Page" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
</asp:Content>

The key here is the Inherits attribute in the <%@ Master %> and <%@ Page %> directives. If you are using the single-page code model (which is what I'm doing here), Inherits tells the compiler to use a class other than the standard one as the page's base; you will need to add the tag yourself. If you are using separate code-behind files, the tag is inserted automatically and will point to your code-behind; you do NOT want to change it there. Instead, go to the code-behind file and change it to be based on your custom classes.

Results, Test 1 - Master and Child

We are ready for our first test. Open a browser and point it to the child page you just created.

Child PreInit
Master Init
Child Init
Child InitComplete
Child PreLoad
Child Load
Master Load
Child LoadComplete
Child PreRender
Master PreRender
Child PreRenderComplete
Child SaveViewState
Master SaveViewState
Child SaveStateComplete
Child RenderControl
Child Render
Child RenderChildren
Master RenderControl
Master Render
Master RenderChildren

There you are: the events of a master and child page, documented in the order they occurred. If you look at the browser source, you will notice that the text occurs before even the DOCTYPE declaration. This makes sense, because the messages were sent to the response stream before the page was rendered.

Results, Test 2a - Controls, First Load

Now we add some controls to the child page:

<%@ Page Language="VB" Inherits="TestChild" MasterPageFile="~/Test.master" 
	Title="Untitled Page" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
    <hr />
    <h3>Child page controls</h3>
    <div style="margin-bottom:1em;">
        <test:TestGrid ID="Child_Grid" runat="server" DataSourceID="TestData" />
        <asp:XmlDataSource ID="TestData" runat="server" 
		DataFile="~/App_Data/TestData.xml"/>
    </div>
    <test:TestButton ID="Child_Button" runat="server" />
</asp:Content>

The horizontal rule is just a visual separator from the text and our controls, and the div tag groups the grid and its data source while providing a bit of spacing. The documentation now shows:

Child PreInit
Child_Grid Init
Child_Button Init
Master Init
Child Init
Child InitComplete
Child PreLoad
Child Load
Master Load
Child_Grid Load
Child_Button Load
Child LoadComplete
Child PreRender
Master PreRender
Child_Grid DataBinding
Child_Grid DataBound
Child_Grid PreRender
Child_Button PreRender
Child PreRenderComplete
Child_Grid SaveControlState
Child SaveViewState
Master SaveViewState
Child_Grid SaveViewState
Child_Button SaveViewState
Child SaveStateComplete
Child RenderControl
Child Render
Child RenderChildren
Master RenderControl
Master Render
Master RenderChildren

The events for Child_Grid occur before Child_Button because the grid is above the button. If you reverse these controls, you will see that the order of their events also gets reversed.

There are a few interesting things to note. The grid's DataBinding and DataBound events occur after the master page's PreRender but before the child page's PreRender. The grid saves its control state, but the button does not. And the controls save their view state after the master page does, which saves its view state after the child page.

Test2a.jpg

Below the line, we see how the controls got rendered. Notice that the grid gets put on the page during RenderChildren while the button is written during Render.

Results, Test 2a - Controls, PostBack

Click on the button to create a postback, and look at the events.

Child PreInit
Child_Grid Init
Child_Button Init
Master Init
Child Init
Child InitComplete
Child_Grid LoadControlState
Child_Grid LoadViewState
Child_Button LoadViewState
Child PreLoad
Child Load
Master Load
Child_Grid Load
Child_Button Load
Child_Button Click
Child_Button Command
Child LoadComplete
Child PreRender
Master PreRender
Child_Grid PreRender
Child_Button PreRender
Child PreRenderComplete
Child_Grid SaveControlState
Child SaveViewState
Master SaveViewState
Child_Grid SaveViewState
Child_Button SaveViewState
Child SaveStateComplete
Child RenderControl
Child Render
Child RenderChildren
Master RenderControl
Master Render
Master RenderChildren

The grid does not bind to any data this time; instead, it reloads the data from the control state. Because this is a postback, there is a view state that can be loaded so the controls do just that. After the button gets loaded, its Click event is processed, then its Command event. The controls are rendered just as they were before.

Moving On

There are many more tests that can be done: put controls on the master page above and below the content placeholder, try nested master pages, track different controls. Please experiment, and if you get any interesting results, please post them as comments to this article.

History

  • Version 2 - September 2, 2010 - Initial release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Gregory Gadow
Software Developer (Senior)
United States United States
Gregory Gadow lives in Seattle, Washington and has been writing code for almost 25 years in more than a dozen programming languages. He works for a mid-size brokerage firm and holds the Series 7 and Series 66 brokerage licenses, but much prefers working as the company's programming department doing VB6, VB.Net, ASP, HTML, XML and SQL.

Comments and Discussions

 
GeneralMy vote of 5 Pinmemberlinuxjr10-Oct-10 18:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 2 Sep 2010
Article Copyright 2010 by Gregory Gadow
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid