Every once in a while I see code which looks like that:

public class MyCustomControl : WebControl
{
    protected override void Render(HtmlTextWriter output)
    {
         SomeControl someControl = new SomeControl ();
         someControl.ID = "SomeID";
         someControl.SomeProperty = "SomeValue";
         someControl.RenderControl(output);
    }
}

There are two problems with this code:
  1. The child control is instantiated in the Render method
    From control execution lifecycle’s point of view it is too late to instantiate controls in the Render method. Even if you add them in the controls collection somefeatures won’t work just because it’s too late – postbacks, viewstate to name a few.
  2. 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 won’t work – saving and loading viewstate, postbacks. Heck, there is a good chance that the control won’t 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:

public class MyControl : WebControl
{
    protected override void CreateChildControls()
    {

         SomeControl someControl = new SomeControl ();
         someControl.ID = "SomeID";
         someControl.SomeProperty = "SomeValue";

         Controls.Add(someControl);
    }
}

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).

public class MyControl : WebControl, INamingContainer
{
      .....
}

or

public class MyControl : CompositeControl
{
      .....
}

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:

public class MyControl : WebControl, INamingContainer // or : CompositeControl
{
    private SomeControl someControl;
    public string SomeProperty
    {
         get
         {
              EnsureChildControls(); //Make sure CreateChildControls is called.  
              return  someControl.SomeProperty;                        
         }
         set
         {
              EnsureChildControls(); //Make sure CreateChildControls is called.  
              someControl.SomeProperty = value;                                              
         }
    }

    protected override void CreateChildControls()
    {
         someControl = new SomeControl ();
         someControl.ID = "SomeID";
         someControl.SomeProperty = "SomeValue";            

         Controls.Add(someControl);
    }
}

I highly recommend some additional reading - the excellent series of articles TRULY Understanding Dynamic Controls

rahnev
About the Author

Stefan Rahnev

Stefan Rahnev (@StDiR) is Product Manager for Telerik Kendo UI living in Sofia, Bulgaria. He has been working for the company since 2005, when he started out as a regular support officer. His next steps at Telerik took him through the positions of Technical Support Director, co-team leader in one of the ASP.NET AJAX teams and unit manager for UI for ASP.NET AJAX and Kendo UI. Stefan’s main interests are web development, agile processes planning and management, client services and psychology.

Related Posts

Comments