When a browser makes requests to an ASP.NET page, communication takes place over the stateless HTTP protocol. However, the page framework creates an illusion of stateful execution that enables a page to provide a user experience similar to that of a continuously executing desktop process. To create the illusion of continuity, on each subsequent request after the initial request, the page effectively begins execution where it left off at the end of the previous request. The page saves its state at the end of processing a request and, upon postback, uses the saved state to restore its state before processing the new request. This process of saving and restoring state is the most important aspect of the life cycle of a page and its controls. The page framework divides request processing into several distinct, logical phases so that it can re-create and save the page and its control tree in a predictable manner. Once the page framework restores the control tree, controls can execute and interact as though in a continuously executing process. As a control developer, you must understand a control's life cycle so that you know which logic to implement in the various phases. However, if you are new to control development, you do not have to understand the full complexity of the control life cycle before you begin implementing useful controls. To get started, you merely need an overview of the various phases in a control's life cycle, as Figure 9-1 depicts. Figure 9-1. Control life cycle
The methods that begin with On (such as OnInit ) are part of your control's event infrastructure and raise the corresponding event (such as Init ). Some of the phases are implemented as events so that a page developer can respond to them by attaching event handlers. Here's an overview of what happens in each phase and what you need to implement in your control. If you are new to control authoring, the phases that you must understand to get started are Instantiate, Initialize, Load, PreRender, and Render. You can skim the descriptions of the other phases for now and return to them when you create more advanced controls.
We'll now examine the mechanics of the page execution cycle. As we described in Chapter 2, a page is an HTTP handler and is responsible for processing an HTTP request to an .aspx file. When it starts processing a request, the page creates its control tree. It then sequentially executes the phases shown in Figure 9-1 by recursively invoking the methods appropriate to each phase on the control tree. Note that a page is a control (derives from Control ) and, like any other control, inherits the methods and events shown in Figure 9-1. Because several of the methods listed in Figure 9-1 are protected, you might wonder how a page can invoke them on its child controls. This invocation is possible because every control inherits ”from the Control class ”some internal (assembly- accessible) framework methods that recursively invoke the protected methods. Note The execution sequence that we just described applies to controls that are created declaratively on the page. But what if a control is created in an event handler and dynamically added to the control tree? In that case, the control plays catch-up. As soon as it is added to the control tree, it starts to execute its phases until it reaches the current phase of the page. At that point, it follows along with the page. To demonstrate the execution sequence, we'll create the simple LifeCycleDemo control shown in Listing 9-1. LifeCycleDemo uses the tracing feature of ASP.NET to write a simple message from each execution phase by overriding the relevant methods (such as OnInit and OnLoad ). Listing 9-1 LifeCycleDemo.csusing System; using System.Web.UI; namespace MSPress.ServerControls { public class LifeCycleDemo : Control { public LifeCycleDemo() { } protected override void OnInit(EventArgs e) { base.OnInit(e); Page.Trace.Write(this.ID, "In Init..."); } protected override void TrackViewState() { base.TrackViewState(); Page.Trace.Write(this.ID, "In TrackViewState..."); } protected override void LoadViewState(object savedstate) { base.LoadViewState(savedstate); Page.Trace.Write(this.ID, "In LoadViewState..."); } protected override void OnLoad(EventArgs e) { base.OnLoad(e); Page.Trace.Write(this.ID, "In Load..."); } protected override object SaveViewState() { Page.Trace.Write(this.ID, "In SaveViewState..."); return base.SaveViewState(); } protected override void OnPreRender(EventArgs e) { base.OnPreRender(e); Page.Trace.Write(this.ID, "In PreRender..."); } protected override void Render(HtmlTextWriter writer) { Page.Trace.Write(this.ID, "In Render..."); } public override void Dispose() { base.Dispose(); } } } Listing 9-2 shows a page that uses the LifeCycleDemo control. The page has three instances of the LifeCycleDemo control ”one created declaratively and two created dynamically in its button1_Click event handler. The page adds one of the dynamically created instances of LifeCycleDemo to its control tree, but not the other. You'll see that the execution phases occur only in the instance that is added to the control tree. Tracing is enabled on the page by setting trace="true" in the Page directive. Figures 9-2 and 9-3 show the trace log for the page on initial request and after postback. Listing 9-2 LifeCycleDemoTest.aspx<%@ Page Language="C#" Trace="true"%> <%@ Register TagPrefix="msp" Namespace="MSPress.ServerControls" Assembly="MSPress.ServerControls" %> <html> <head> <script runat="server" > void button1_Click(object sender, EventArgs e) { LifeCycleDemo dynamic1 = new LifeCycleDemo(); dynamic1.ID = "dynamic1"; this.Controls.Add(dynamic1); // The next instance of LifeCycleDemo is not added to the // control tree of the page. LifeCycleDemo dynamic2 = new LifeCycleDemo(); dynamic2.ID = "dynamic2"; } </script> </head> <body> <form runat="server"> <asp:Button id="button1" OnClick="button1_Click" Text="Submit" runat="server" /> <br <msp:LifeCycleDemo runat="server" id="declarative1" /> </form> </body> </html> Figure 9-2. A section of the trace log from LifeCycleDemoTest.aspx on first request. The messages corresponding to aspx.page in the Category column are written by the page. The other messages are written by the declarative instance of LifeCycleDemo .
On first request, you can see the execution sequence of the declarative instance of LifeCycleDemo with ID="declarative1" in the trace log. On postback, another instance of LifeCycleDemo with ID="dynamic1" is created in the Raise Postback Event phase, as shown in Figure 9-3. This instance plays catch-up, and three of its execution phases occur in succession after it is created and added to the control tree. Note that the instance of LifeCycleDemo with ID="dynamic2" that is created in the button1_Click event handler (shown in Listing 9-2) does not execute its phases after instantiation because the page did not add the control to the control tree. A control cannot participate in request processing unless it is added to the control tree. When a control is created declaratively on a page, the page parser adds it to the control tree. If you create a control dynamically, you are responsible for adding it to the control tree yourself. Figure 9-2 contains two calls to the SaveViewState method of the declarative1 control instance. The first call is an artifact of tracing and does not occur in normal execution. When tracing is enabled, the page makes the additional call to estimate the size of the view state. Although tracing helps you to understand processing of the page and its controls, you should not include trace statements in production-quality code. Trace output is not rendered when tracing is disabled on the page; nonetheless, the trace statements execute and add an unnecessary overhead. Figure 9-3. A section of the trace log from LifeCycleDemoTest.aspx on postback. This log shows the additional phases that occur in a postback scenario. It also contains messages from a second (dynamically added) instance of LifeCycleDemo .
|