Handling Control Events

I l @ ve RuBoard

All custom controls inherit from the System.Web.UI.Control class, which defines the events described in Table 12.1.

Table 12.1. System.Web.UI.Control Events
Event Method To Override Description
DataBinding OnDataBinding(EventArgs e) Occurs when the server control binds to a data source.
Dispose Dispose() Occurs when a server control is released from memory, after the Unload() event.
Init OnInit(EventArgs e) Occurs when the server control is initialized .
Load OnLoad(EventArgs e) Occurs when the server control is loaded into the Page object.
PreRender OnPreRender(EventArgs e) Occurs when the server control is about to render to its containing Page object.
Unload OnUnload(EventArgs e) Occurs when the server control is unloaded from memory.

These events describe the life cycle of all controls in ASP.NET, including all the built-in controls. As a control moves through its life cycle of creation and destruction, which happens on every page request, it will go through some or all of the following phases in the order described in Table 12.2.

Table 12.2. Control Life Cycle
Phase What the Control Should Be Doing in This Phase
Constructor Anything required for the entire state of the control that cannot be put off until the Initialize phase. This should be used sparingly, since acquiring resources could probably be delayed until a later phase, resulting in a more efficient control.
Declarative Properties Declarative properties set on the ASP.NET page are set. Any objects that need to exist for this stage should be created either in the constructor or, optimally, should be created by a separate method called by the property setter only if needed.
Initialize (Init) Initialize settings needed during the rest of the lifetime of the event.
Track ViewState Perform custom logic to manage saving ViewState for the control.
Load ViewState Use the LoadPostData method to perform custom logic to load ViewState into the control's properties. Only occurs if IPostBackDataHandler is implemented.
Programmatic Properties Properties set programmatically in the ASP.NET page's Load event are process.
Load Perform actions common to all requests . At this point, the state of the control has been restored and all form controls reflect client-side data.
PostBack DataChanged Event Raise events in response to changes in state between the current and previous PostBack . Only occurs if IPostBackDataHandler is implemented.
Raise PostBack Event Handle the client-side event (such as OnClick ) that caused the PostBack . Only occurs if IPostBackEventHandler is implemented.
PreRender Last chance to modify the contents of a control, and have those changes persisted to ViewState , before the control is rendered.
Save ViewState The ViewState collection is converted to a string and persisted to the client as a hidden form field.
Render The HTML output for the control is sent to the client.
OnUnload Occurs as the control is about to be released from memory.
Dispose Called immediately after OnUnload .
Class Destructor Typically should not be used. The Dispose event tends to take its place and in fact where Dispose is used, it seems that the class's destructor is not executed.

Notably missing from this list is the CreateChildControls() method, which is used to create the controls tree for a control. It is not listed in this life cycle diagram because it could be called during one of several phases, including the Load , DataBinding , and Render phases. Basically, this control is called whenever the ASP.NET page framework needs the controls tree to exist. You can ensure that this event is called prior to your accessing properties of child controls in your property setters and getters by calling the EnsureChildControls() method within these routines. This method checks to see if the CreateChildControls() method has already been called, and calls it only if it hasn't already been called.

Understanding the life cycle of a control can be very useful when developing a complex custom control of your own. It can be very frustrating when you are trying to reference child controls in your declarative properties and you are getting null reference exceptions because the child controls don't exist yet. A good way to debug such situations is to trace through your code so you can see the order in which the events are being performed. You can easily do this using the VS.NET debugger, but that doesn't make nearly as good an example as demonstrating how to do this using the Trace feature that is built into ASP.NET.

We're going to build an extension to our Hello World control that handles all the events described above and output Trace statements as each event is begun and completed. Some of these events will only be fired on PostBack , and some, like the ones that occur after Render , will not appear at all in the trace results because the page has already been rendered by the time these events are executing. In order to show the difference between setting properties declaratively within our control tag on the .aspx page, and setting them programmatically in our Page_Load event, we are also tracing whenever our property is set. Listing 12.7 shows the complete source code of our new class, HelloLifeCycle.cs.

Listing 12.7 HelloLifeCycle.cs
 using System; using System.Collections.Specialized; using System.Web.UI; namespace ASPNETByExample {     public class HelloLifeCycle : System.Web.UI.Control,         IPostBackEventHandler, IPostBackDataHandler     {         System.Web.HttpContext cx;         public HelloLifeCycle()         {             cx = System.Web.HttpContext.Current;             cx.Trace.Write("HelloLifeCycle","Begin Constructor");             cx.Trace.Write("HelloLifeCycle","End Constructor");         }         ~HelloLifeCycle()         {             cx.Trace.Write("HelloLifeCycle","Begin Destructor");             cx.Trace.Write("HelloLifeCycle","End Destructor");         }         public string property         {             set             {                 cx.Trace.Write("Property Set","Set Property: " + value);             }         }         protected override void OnInit(EventArgs e)         {             cx.Trace.Write("OnInit","Begin OnInit");             base.OnInit(e);             cx.Trace.Write("OnInit","End OnInit");         }         protected override void OnLoad(EventArgs e)         {             cx.Trace.Write("OnLoad","Begin OnLoad");             base.OnLoad(e);             cx.Trace.Write("OnLoad","End OnLoad");         }         protected override void OnDataBinding(EventArgs e)         {             cx.Trace.Write("OnDataBinding","Begin OnDataBinding");             base.OnDataBinding(e);             cx.Trace.Write("OnDataBinding","End OnDataBinding");         }         protected override void OnPreRender(EventArgs e)         {             cx.Trace.Write("OnPreRender","Begin OnPreRender");             base.OnPreRender(e);             cx.Trace.Write("OnPreRender","End OnPreRender");         }         protected override void Render(System.Web.UI.HtmlTextWriter htwOutput)         {             cx.Trace.Write("Render","Begin Render");             htwOutput.Write("Hello World Life Cycle");             cx.Trace.Write("Render","End Render");         }         protected override void OnUnload(EventArgs e)         {             cx.Trace.Write("OnUnload","Begin OnUnload");             base.OnUnload(e);             cx.Trace.Write("OnUnload","End OnUnload");         }         public override void Dispose()         {             cx.Trace.Write("Disposed","Begin Disposed");             base.Dispose();             cx.Trace.Write("Disposed","End Disposed");         }         protected override void LoadViewState(object savedState)         {             cx.Trace.Write("LoadViewState","Begin LoadViewState");             base.LoadViewState(savedState);             cx.Trace.Write("LoadViewState","End LoadViewState");         }         protected override object SaveViewState()         {             cx.Trace.Write("SaveViewState","Begin SaveViewState");             try{ return base.SaveViewState();}             finally{                 cx.Trace.Write("SaveViewState","End SaveViewState");             }         }         protected override void TrackViewState()         {             cx.Trace.Write("TrackViewState","Begin TrackViewState");             base.TrackViewState();             cx.Trace.Write("TrackViewState","End TrackViewState");         }         public virtual void RaisePostDataChangedEvent()         {             cx.Trace.Write("RaisePostDataChangedEvent","Begin RaisePostDataChangedEvent");             cx.Trace.Write("RaisePostDataChangedEvent","End RaisePostDataChangedEvent");         }         public virtual bool LoadPostData(string postDataKey,             NameValueCollection postCollection)         {             cx.Trace.Write("LoadPostData","Begin LoadPostData");             try{ return true;}             finally             {                 cx.Trace.Write("LoadPostData","End LoadPostData");             }         }         public void RaisePostBackEvent(String eventArgument)         {             cx.Trace.Write("RaisePostBackEvent","Begin RaisePostBackEvent");             cx.Trace.Write("RaisePostBackEvent","End RaisePostBackEvent");         }     } } 

As you can see, this class has a lot of repeated code. Basically, it inherits from Control and implements the IPostBackEventHandler and IPostBackDataHandler interfaces. Next, it establishes a class variable, cx , to hold a reference to the System.Web.HttpContext.Current object, which lets us access the calling page's intrinsic properties and methods , such as Trace . The rest of the code is fairly self-explanatory. For the property, I output the value of the property in the trace output so that we can see whether it was set declaratively or programmatically (because I'm going to pass it the string "Declarative" or "Programmatic" depending on where I am setting it). For the methods that override the Control class, I make sure to call the base.MethodName() method, which is just a good habit to get into for these methods. Now, to actually see this control in action, we need an ASP.NET page to hold the control and implement tracing so we can follow the life cycle of the control. Listing 12.8 describes our ASP.NET page for testing this control.

Listing 12.8 HelloEvents.aspx
 <%@ Page Language="C#" Trace="True" %> <%@ Register TagPrefix="Hello" Namespace="ASPNETByExample" Assembly="HelloWorld" %> <%@ Import Namespace="ASPNETByExample" %> <script runat="server"> void Page_Load(){     hello.property = "Programmatic"; } </script> <html>     <body>         <form runat="server">             <Hello:HelloLifeCycle runat="server" ID="hello" property="Declarative" />             <asp:Button runat="server" Text="PostBack" ID="btnPostBack" />         </form>     </body> </html> 

As you can see, in this code I am just placing the control on the page, with a declarative property setting of Declarative and a programmatic property setting (in Page_Load ) of Programmatic . The btnPostBack button is there so that we can generate a PostBack and observe the object life cycle both with and without a PostBack . Since the PostBack is more interesting, you can see the trace output for this page after a PostBack in Figure 12.2.

Figure 12.2. Trace information for HelloEvents.aspx.

Now that we have seen when these events take place, let's examine how to implement some of them, starting with using PostBacks and ViewState in your own custom controls.

I l @ ve RuBoard


Asp. Net. By Example
ASP.NET by Example
ISBN: 0789725622
EAN: 2147483647
Year: 2001
Pages: 154

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net