This article was originally published on DNN Developer Zone (www.dnndevzone.com). This version has been edited slightly.
Introduction
As a control developer it is important to understand a control's life cycle. The ASP.NET Framework breaks the controls life cycle into distinct logical phases. In this article I will describe these phases, what happens in each phase and what kinds of things the control developer can and should do in each phase.
The Control Life Cycle
Instantiate
The first phase is the Instantiate Phase. In this phase the control is constructed. This instantiation can happen because the control is placed on an ASP.NET Page (aspx) or User Control (ascx), using declarative syntax. In this situation the ASP.NET Framework automatically instantiates the control.
Alternatively, the control can be instantiated programmatically by invoking its Constructor. As a developer, you can customize this phase by adding statements to the control's constructor. For instance you can set your control's properties to default values in the constructor.
Initialize
The second phase is the Initialize Phase. In this phase the page framework calls the OnInit method of all the controls in the control tree of the page. It should be noted that properties assigned (as attributes) in the declarative syntax are loaded before the framework calls this method. As a developer you can provide initialization logic for your control in this phase by overriding the OnInit method (see Listing 1).
Listing 1: The OnInit method |
1: protected override void OnInit(EventArgs e)
2: {
3: base.OnInit(e);
4: }
|
Note that this method calls the base class's OnInit method. This allows the page framework to trigger a Control.Init event, which can be trapped by the Page or User Control that contains this control. If you do not call the base class's method at some stage in your override, the framework will not trigger an event. If you want something to be executed before the Control.Init event is fired, you should add logic before base.Init, if you want logic to execute after the Control.Init event you will need to add logic after base.Init.
Begin Tracking ViewState
This phase occurs immediately after the Initialize phase. In this phase, the framework automatically calls the TrackViewState method of every control in the page's control tree (see Listing 2).
Listing 2: The TrackViewState method
|
1: protected override void TrackViewState()
2: {
3: base.TrackViewState();
4: }
|
The TrackViewState method ensures that any changes made to properties that use the ViewState dictionary during the control life cycle are persisted to the control's ViewState and rendered to the Client.
Load ViewState (Postback Only)
This phase occurs on postback only - not during the initial request. If there is a postback, the ViewState from the previous request is returned to the server, and the page framework calls the LoadViewState method (see Listing 3) of every control participating in ViewState, passing in an object representing the saved state of the control.
Listing 3: The LoadViewState method
|
1: protected override void LoadViewState(object savedState)
2: {
3: base.LoadViewState(savedState);
4: }
|
The framework identifies the control based on its UniqueID (which is generated from the control's ID in combination with the ID's of its ancestors) and LoadViewState rebuilds the state of the control to provide the illusion of state across postbacks. If your control does not need to maintain state or if it only uses the ViewState dictionary to maintain state, then you do not have to implement the LoadViewState method, as the base class's method will handle the loading for you.
You only need to implement this method if you wish to store something in the ViewState that you have not saved to the ViewState dictionary. One example would be, if you had a complex object you needed to persist. In this scenario the default serializer used by the base class might not be appropriate (especially in partial trust environments).
Load Postback Data (Postback Only)
This phase also only occurs on postback. It is also optional. In order to participate in this phase a control must implement the IPostBackDataHandler interface. The IPostBackDataHandler interface consists of two methods, LoadPostData (see Listing 4) and RaisePostDataChangedEvent (this method is used in a later phase).
Listing 4: The LoadPostData method
|
1: Public Overridable Function LoadPostData(ByVal postDataKey As String, _ ByVal postCollection As NameValueCollection) As Boolean
2: Dim dataChanged As Boolean = False
3: Dim presentValue As String = CStr(Value)
4: Dim postedValue As String = postCollection(postDataKey)
5: If Not presentValue.Equals(postedValue) Then
6: Value = postedValue
7: dataChanged = True
8: End If
9: Return dataChanged
10: End Function
|
In this phase the page framework will call the LoadPostData method of every control in the control tree that implements the IPostBackDataHandler interface. The difference between this phase and the previous phase is that in this phase we are loading values from the posted form data, rather than the ViewState.
As before the page framework identifies the control based on its UniqueID. The "name" part of the name/value dictionary for the form data is passed to the control as the postDataKey parameter, and the name/value dictionary is passed to the control as the postCollection parameter. This enables the method to compare the previous value of the property (which could have been retrieved from the database or loaded from the ViewState), to be compared with the new value of the property which the user entered on the form. The method returns true or false indicating to the framework whether the data changed.
Load
The next phase is the Load phase. All controls participate in this phase. By the time this phase executes all controls in the control tree should be initialized and be restored to the state they had at the end of the previous cycle. In addition, controls that implement the IPostBackDataHandler interface have been updated with the posted data. In this phase the page framework calls the OnLoad method of the control (see Listing 5).
Listing 5: The OnLoad method
|
1: protected override void OnLoad(EventArgs e)
2: {
3: base.OnLoad(e);
4: }
|
As with the OnInit method, if you wish the framework to fire the Control.Load event, you need to call the base class's OnLoad method. If you need to add logic that only runs on the first request then you can check the IsPostBack property of the page (ie. Page.IsPostBack == false). Alternatively you might want logic to run only if this is the second or subsequent request (ie.Page.IsPostBack == true).
Raise Changed Events (PostBack only)
If your control implements the IPostBackDataHandler interface, then it will participate in this optional phase. To be more precise the control only participates in this phase, if the LoadPostData method called in the Load Postback Data phase (see above) returned true indicating that the data has changed.
Listing 6: The RaisePostDataChangedEvent method
|
1: Public Sub RaisePostDataChangedEvent()
2: 'Raise the DataChanged Event
3: OnDataChanged(System.EventArgs.Empty)
4: End Sub
|
In this example the RaisePostDataChangedEvent method calls the OnDataChanged method which presumably raises a DataChanged event. However, the body of this method is entirely up to you. A drop-down list control would raise a SelectedIndexChanged event, a checkbox control would raise a CheckedChanged event and a textbox control would raise a TextChanged event.
Raise PostBack Event (PostBack Only)
This is another optional phase that only occurs on postback. In order to particpate in this phase the control must implement the IPostBackEventHandler interface. This, interface allows a control developer to hook server-side events to client-side events. It is beyond the scope of this article to describe how this is implemented, but an example of this is the Click event for a button, which signifies to the server that an event was raised on the client. No data changed and no name/value pair is returned. The IPostBackEventHandler interface contains a single method - RaisePostBackEvent (see Listing 7).
Listing 7: The RaisePostBackEvent method
|
1: Public Sub RaisePostBackEvent(ByVal eventArgument As String)
2: If AutoPostBack Then
3: OnItemChanged(GetEventArgs())
4: End If
5: End Sub
|
The eventArgument is used to identify the event that was triggered in the client, allowing the control to handle more than one client-side event. If a control does handle more than one event, RaisePostBackEvent could then direct control flow to the appropriate method based on the value of the eventArgument.
PreRender
The next phase is the PreRender phase. All controls participate in this phase. In this phase the page framework calls the OnPreRender method of all the controls in the control tree of the page (see Listing 8).
Listing 8: The OnPreRender method
|
1: protected override void OnPreRender(EventArgs e)
2: {
3: base.OnPreRender(e);
4: }
|
In this phase you should implement anything that needs to be completed before the control is rendered.
Save View State
As with the Load View State phase, as long as you only use the ViewState dictionary to save state you do not need to implement this phase. In this phase the framework calls the SaveViewState method for all controls in the control tree, and the base method automatically saves the ViewState dictionary.
Listing 9: The SaveViewState method
|
1: Protected Overrides Function SaveViewState() As Object
2: Return MyBase.SaveViewState()
3: End Function
|
If you wish to save View State data that is not stored in the ViewState you can override this method (see Listing 9).
Render
In this phase the control writes markup text to the output stream. There are a number of "Render" methods that can can be overridden. The base Render methods calls these "render" methods in turn, so if you intend to override some of these methods, you should always call the base Render method if you need to override the base methods implementation (see Listing 10).
Listing 10: The Render method
|
1: protected override void Render(HtmlTextWriter writer)
2: {
3: base.Render(writer);
4: }
|
UnLoad
After it has rendered the page, the framework performs cleanup by calling the OnUnload event of each control.
Listing 11: The OnUnload method
|
1: protected override void OnUnload(EventArgs e)
2: {
3: base.OnUnload(e);
4: }
|
Dispose
In the final phase the page framework calls the Dispose method of each control. In this phase you should dispose of any resources that your control uses.
Listing 12: The Dispose method
|
1: public override void Dispose()
2: {
3: base.Dispose();
4: }
|
This sequence applies to controls that are created declaratively on the page. It often happens, however, that control are dynamically injected into the page at a later stage of the Control Life Cycle, for instance in Page_Load or in response to an event triggered by a different control.
In this case the control plays catch-up. For example, if a control is dynamically injected during Page_load then the control executes its Instantiate, Initialize, Begin Tracking View State, Load View State and Load Postback Data, so it can be in sync with all the other controls on the page.
For More Information:
For more information on the Control Life Cycle, Events and Postback please see Chapter 9 of "Developing Microsoft ASP.NET Server Controls and Components", by Nikhil Kothari and Vandana Datye, Microsoft Press.