On dynamically creating server controls
Posted by: the telerik blogs,
on 28 Sep 2007 |
View original | Bookmarked: 0 time(s)
Every once in a while I see code which looks like that:
There are two problems with this code:
- The child control is instantiated in the Render method
From control execution lifecycles point of view it is too late to instantiate controls in the Render method. Even if you add them in the controls collection somefeatures wont work just because its too late postbacks, viewstate to name a few.
- The child control is never added to the controls collection. This means the following:
- Lots of the properties of that child control will be null: Page, Parent etc.
- A control which is not a child of the the page does not participate of the control execution lifecycle no one calls its OnInit, LoadViewState, SaveViewState, OnPreRender and the rest of the lifecycle supporting methods. As a result most of the features probably wont work saving and loading viewstate, postbacks. Heck, there is a good chance that the control wont work at all. Take the LinkButton for example it just renders its text if you use it in the aforementioned way. Nothing (ok, almost) in the world would make it postback unless you make it part of the controls collection of the page.
- Web Resources won't work. They require the Page property of the control to be valid. Take the TreeView control for example it will fail with a NullReferenceException in the aforementioned scenario.
If this is not the correct way to instantiate child server controls in composite controls what is?
It is always a good idea to instantiate child server controls inside the CreateChildControls method of your composite control (kudos to the MS guys for such a descriptive name). If you need to instantiate server controls in a page or user control you can still use CreateChildControls or the Init / Load events. Here is the aforementioned control done in a better way:
If you plan to add more than one instance of your custom control in your page it is a good idea to implement the INamingContainer interface. That interface will tell the ASP.NET runtime to generate a unique ID for your controls by prefixing their ID's with the ID of its parent. Fortunately INamingContainer is a marker interface which means you don't have to write any code. You can alternatively inherit from CompositeControl (which also implements INamingContainer).
How about exposing properties of the child control? You need to make sure you call the EnsureChildControls method before you access the child control instance. Never call CreateChildControls directly - otherwise the child controls will be added more than once. Here is the modified example:
EnsureChildControls(); //Make sure CreateChildControls is called.
I highly recommend some additional reading - the excellent series of articles TRULY Understanding Dynamic Controls