In order to understand the process of loading user controls in a dynamic manner, we should inspect the ASP.NET page life cycle. ASP.NET pages have a very short lifespan, due to their stateless HTML-based nature. Their life begins when somebody makes a request for them to the server. Then the page goes through the following stages:
- Request stage: compile page (if not precompiled), or pull from cache (if available)
- Start stage: set Response and Request, determine IsPostBack
- page_init: initialize page controls (without the associated properties), set page theme
- page_load: if the request is a PostBack, loads the control properties from the ViewState
- Validation: determines the validity of the page and its controls
- PostBack event handling stage: call event handlers
- page_render: saves the values to the ViewState, renders the controls and the output
- page_unload: cleanup stage, then discard
If you want to memorize this, use SILVER: Start Init Load Validate Events Render.
So far for the page life cycle, but when should we load dynamic controls? The answer hides in the page life cycle events. Table:
Page Life Cycle Events:
|Event||What is it good for?|
|PreInit||Occurs before page initialization. Add your dynamically generated controls at this stage. Also, you can set themes and master pages in the appropriate event handler.|
|Init||The page initialization, controls’ initialization values can be changed here. Beware: as the ViewState hasn’t been loaded at this point, some values specified here may be overridden by its data!|
|InitComplete||All of the page’s controls and the page itself are fully initialized. Hail!|
|PreLoad||ViewState has been loaded, but no postback logic took place.|
|Load||Page is fully loaded. Time to bind data and set the controls’ properties.|
|Postback events||Calling the appropriate events which caused a postback (if any).|
|LoadComplete||All controls are loaded, if you still need to change something, do it now!|
|PreRender||Last chance to do some changes on the page. Also, this is the last stage, before ViewState is saved, so anything changed here will be writed back to it,|
|SaveStateComplete||There’s no point in changing any control property by this time. However, if you need to do something, which requires a saved ViewState, the time has come for it.|
|Render||Not an event, but a method. At this stage, the HTML is generated. This is useful when you create custom controls. You should override their render methods to get the desired output.|
|UnLoad||The page is being discarded. Release any managed resources!|
So now, we know what and when happens at our page. But how to add a custom control? The easiest way is to simply add it to the page’s form, syntax is the following:
Button b = new Button();
However, this approach is less than ideal. Our newly created button will end up somewhere near the end of the page. That is the point where a good PlaceHolder control comes to place. As its name suggests, a PlaceHolder control is a control used for holding place for other controls. With it, we can specify exactly where do we want to insert our control, even spice it up with some styling, not just throw it to the bottom of the page. The syntax is exactly the same as when we added directly to the form. Assume we have a PlaceHolder called Placy:
Button b = new Button();
It is important to talk a little about this Controls property of the form or PlaceHolder. It returns a ControlCollection class, owned by the control specified. Every child control of a given control is the member of its ControlCollection.
Now consider User Controls. We’ll deal with the creation of them later, in fact, they are a group of controls designed in Visual Studio, which share a .asmx (or Web User Control) file. There are a bunch of reasons why they should be used, from caching to avoid repeating objects on pages. You can load these types of controls with a special method of the Page class: LoadControl. In the simpler constructor, LoadControl needs a string, specifying the relative path of our .ascx file, containing the Web User Control. LoadControl is of type Control, which is a little too thick for most of the scenarios. So it’s a good practice to cast it into an appropriate type.
OurCustomControl custCon = Page.LoadControl(“OurCustomControl.ascx”) as OurCustomControl;
Adding the following code with any proper initialization won’t work. We need to specify that we want to use OurCustomControl, and where it’s located. The simplest way is to simply drag the Web User Control file to a .aspx page, and Visual Studio will handle the situation. However, we can write a <%@ Register directive in our page, specifying the source, tagname and tagprefix of our control.
We can also specify a @Reference directive, which enables you to load controls programmatically, but not declaratively(such as @Register), so the referenced control will be compiled dynamically with the containing page.