Earlier this week I posted an entry titled Control Building and ViewState Lesson for the Day, in which I discussed when working with dynamic controls the importance of the order with which a control is added and its properties are set. My end recommendation was to use the following pattern:
- Create an instance of the control (i.e., TextBox tb = new TextBox();)
- Add the control to the appropriate Controls collection (i.e., PlaceHolderID.Controls.Add(tb);)
- Set the dynamic control's properties (i.e., tb.ForeColor = Color.Red;)
The key lesson was to make sure you do step 2 before step 3, or you may be plauged with view state issues. In that previous blog entry I mentioned that the reason this happens is because if the control is added after the TrackViewState stage in the page lifecycle (such as in the Load stage), then any changes to the control's properties are not recorded by view state, so nothing gets saved to view state and so nothing on postback gets populated back into the control's view state. However, I said that adding a control via the Controls.Add() method causes the controls TrackViewState() method to be called. This is correct, but it's not a complete answer.
Not only is the TrackViewState() method being called for the added control, but it goes through the entire control lifecycle, if needed. Using Reflector you can see that the control's InitRecursive() method is called, which fires the Init event for the added control and all of its children controls (this also is where the control's TrackViewState() method is invoked). If the control that is having the dynamic control added to it has a non-null ViewState, the control being added has it's LoadViewStateRecursive() method called, which loads the view state into the added control and its child controls. But things don't end there necessarily. If the control that had the dynamic control added to it has already fired its Load event, the added control's Load event is fired (recursively, again), and even the PreRender event is fired if needed.
One final point to clear up: in my previous blog entry I credited Alex Lowe with being the one whose words from 2001 had reminded me that I needed to add the control first and then set its properties. Memory's a funny thing, really, since I remembered Alex's words, but I had totally blanked on an email Wessam Zeidan had sent me in late September 2004 where he talked about the same thing, saying: “... some how when we add a dynamic control to the Controls collection, the TrackViewState method gets called, and that explains why the backcolor property of the textbox gets saved to the viewstate if we add the textbox to the controls collection before setting it.” And if that wasn't enough, I realized that I said the same thing myself in my MSDN article Understanding ASP.NET View State:
You may be able to get away with loading your controls in the Page_Load event handler and maintaining the view state properly. It all depends on whether or not you are setting any properties of the dynamically loaded controls programmatically and, if so, when you're doing it relative to the Controls.Add(dynamicControl) line. A thorough discussion of this is a bit beyond the scope of this article, but the reason it may work is because the Controls property's Add() method recursively loads the parent's view state into its children, even though the load view state stage has passed.
It's funny how our minds work, and how fluid memory can truly be.