Learning to play "catch-up"

Note: this entry has moved.

The control execution lifecycle has proven to be one of the most difficult things to grasp by letting beginners totally disconcerted about how the webform model works. About every control and page developer that is messing with dynamically created controls has already played –knowingly or unconsciously- the “catch up” game. Playing it knowing its rules will always make you a winner, on the contrary, playing it without that knowledge will cause you anything but trouble.

 

Here is some code that dynamically creates a textbox control and adds it to the form control:

 

TextBox tb = new TextBox ();

tb.Text = “blah”;
tb.ID = “mytb”;
Control frm = FindControl (“FormID”);

frm.Controls.Add (tb);

 

As you can see there is nothing extraordinary about it, on the contrary it’s pretty darn simple; the difficult part is answering the question: “where can I put that code?

 

Reading the docs is just not enough

 

By looking at the docs we can see that the Load phase is happening past the Load viewstate which would imply that viewstate data is always loaded before the Load event. If this is true, adding our previous snippet of code at Page_Load should cause our textbox to not load its viewstate properly, right? Wrong. The textbox, when added to a ControlCollection collection (in our example it’s the one for our form control), will immediately play “catch-up” and perform every action that it has missed due to its late insertion in the control tree (Init and LoadViewState in our example) thus properly restoring its viewstate data.

 

If you’re into control development chances are that you already own Nikhil’s book. It’s the only source I’ve found that briefly touches this subject with the following paragraph on page 179:

 

“…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…”

 

It is also the source from where I had stolen the “catch-up” term for my post’s title. J

 

Now, where is this “catch-up” game being played…?

 

Entering the “Catch-up” Stadium

 

The catch up happens at the internal protected Control.AddedControl method which –after removing the control from any previous parent and taking care of its ID generation- will examine the current status of the control owning the ControlCollection and based on it determine all the steps the added control has already missed forcing it to “play” them at once. So following our previous example, it means that when we add tb to the ControlCollection of the form control (through the Controls property), Control.AddedControl will immediately call tb’s InitRecursive and LoadViewStateRecursive methods. But who is calling AddedControl in the first place? It’s the ControlsCollection.Add method which makes that call as its last step.

 

What exactly can be “catched up”?

 

The internal ControlState enumeration defines the five possible statuses a control may have during its lifetime:

 

Status

Value

Constructed

0

Initialized

1

ViewStateLoaded

2

Loaded

3

PreRendered

4

 

So, depending on the current status of the parent control, Control.AddedControl will call the following internal methods on the control being added: Control.InitRecursive (if parent is already initialized), Control.LoadViewStateRecursive (if parent’s viewstate is already loaded), Control.LoadRecursive (if parent loading phase has been already executed) and Control.PreRenderRecursiveInternal (if parent pre-rendering phase has been already executed)

 

When is this game played?

 

This “catch up” game is not always played. If the parent’s status is Constructed meaning it has not been even initialized yet the Control.AddedControl will just return without playing any games because there is nothing to catch up for the child control.

 

A curiosity

 

If you were paying real attention to the method names listed in the above paragraph you may have noticed that they all follow the naming pattern “phase in the control execution lifecycle” + the word “Recursive” to clearly denote they’re recursive methods. The only exception is PreRenderRecursiveInternal that happens to include its access modifier of internal as part of its name. But all of the above mentioned methods are internal so why only this one got such a postfix?

8 Comments

  • I've seen the catch-up concept mentioned before (maybe on Nikhil's site).



    ...but it seems that not everything is handled by the catch up, and that if it were, people would not have so much trouble with dynamically added/manipulated controls.



    Many people seem more baffled by the fact that they must re-add the dynamically added control each subsequent request.



  • Hi Andrew -- yes, this catch-up doesn't ('cos it isn't its job) "automagically" recreate any previously dynamically created controls. And I agree that having to manually recreate controls seems difficult to grasp for lots of people (as can be seen daily on the public ng).

  • I give the MS newsgoops a try from time to time, but spend more time on www.aspmessageboard.com



    How do I solve it when:

    They insist on naming things RenderAbcDef, calling them during Load events when what the code really does is initialize controls ...



    Marooned on Monday ;)



    -A

  • I have been playing "catch up"and I am stuck with the following problem. I stored an array of object from which I construct the controls dynamically every time I postback the page.



    But when I click a button (a "static one") to make a different request, I do not want to load the dynamic controls but handle the request, and then add the new controls, but since the load event happens before the click event, the old dynamic controls always get loaded.



    I think I can get rid of the problem if I could identify the control that caused the postback, but so far I have failed miserabily to do so. (I think that should be an easy one).



    Well best regards to all of you

    Alfonso

  • Victor, the problem Andrew mentioned is not about the need to re-adding controls. It is the timing of dynamicaly loading contgrols to get right about the viewstate.



    Try to dynamicaly load a control with a textbox and button on it. Put something on the textbox and click on button, see if the value are still in the textbox. The result is up to when the control is loaded before viewstate is solved.

  • Does this "catch-up" thing related to this error at all?

    Failed to load viewstate. The control tree into which viewstate is being loaded must match the control tree that was used to save viewstate during the previous request. For example, when adding controls dynamically, the controls added during a post-back must match the type and position of the controls added during the initial request.



    Only some of the users are getting this problem, not sure what to do. Here's my code that causing a problem:



    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    'Put user code to initialize the page here

    Select Case Me.Page.SiteType

    Case Common.eSiteType.PublicSite

    Me.Controls.Add(LoadControl(Me.Page.pageURLBase & "/Utilities/UserControls/CookieLeeControls/PublicSiteHeader.ascx"))

    Case Common.eSiteType.Consultant

    Me.Controls.Add(LoadControl(Me.Page.pageURLBase & "/Utilities/UserControls/CookieLeeControls/ConsultantHeader.ascx"))

    End Select

    End Sub

  • [href=//www.dmoz.net.cn/ wangzhidaquang]

    [href=//www.86dmoz.com/ jingpingwangzhi]

    [href=//www.kamun.com/ mianfeidianying]

    [href=//www.kamun.com/ dianyingxiazai]

    [href=//www.kamun.com/ MP3 free download]

    [href=//www.pc530.net/ diannaoaihaozhe]

    [href=//www.5icc.com/ duangxingcaixingxiazha]

    [href=//www.dianyingxiazai.com/ dianyingxiazai]

    [href=//www.yinyuexiazai.com/ yinyuexiazai]

  • Thanks for every information.
    These are very useful!

    King Regards!

Comments have been disabled for this content.