This will be the first in series of posts I’ll be writing about creating server controls.  I’ll try to share some of what I learned from my research while I prepared for a recent presentation. 

With most software development, there isn’t always a simple answer to a question.  This is of course true for creating Composite Controls for ASP.Net 2.0.  One area where this was apparent was the proper way to render a control.  Of the many examples I have found on the web it seems that most people put their rendering code within CreateChildControls() but some override the Render() method.  I’ll show examples below.  So which is correct?  You know the answer…It Depends!   My personal preference seems to be outside of the norm but I prefer to include rendering code in the Render() method (as in Option A below) unless there is a compelling reason not to.

My sample CompositeControl will have 2 controls within it:

        private TextBox _textBox = new TextBox();
        private Label _label = new Label();

Option A:

        protected override void CreateChildControls()
        {
            Controls.Clear();
            _textBox.ID = "txtBox1";
            _label.ID = "label1";
            Controls.Add(_textBox);
            Controls.Add(_label);
            base.CreateChildControls();
        }
        protected override void Render(HtmlTextWriter writer)
        {
            EnsureChildControls();
            AddAttributesToRender(writer);
            writer.RenderBeginTag(HtmlTextWriterTag.Div);
            writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "1", false);
            writer.RenderBeginTag(HtmlTextWriterTag.Table);
            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            _label.RenderControl(writer);
            writer.RenderEndTag(); //</td>
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            _textBox.RenderControl(writer);
            writer.RenderEndTag(); //</td>
            writer.RenderEndTag(); //</tr>
            writer.RenderEndTag(); //</table>
            writer.RenderEndTag();
        }

In option A, CreateChildControls is used simple to set properties on the controls and add them to the Controls Collection.  The Render() method takes care of layout.  In particular, I like being able to use the HtmlTextWriterTag enumeration with the RenderBeginTag() method.  By the way, the RenderEndTag() method will “close” whatever tag is next in line to be closed within the nested hierarchy of tags.  Note that the Div tag I render isn’t really necessary.  But I want all of my samples to render the same markup, so I put that in.  Use the AddAttribute() method to add attributes to whichever tag is written next after the AddAtttribute() call.  So in this sample, the CellPadding attribute will get written into the table as <table cellpadding=”1″>.

Option B:

        protected override void CreateChildControls()
        {
            Controls.Clear();
            _textBox.ID = "txtBox1";
            _label.ID = "label1";

            Controls.Add(new LiteralControl("<table cellpadding='1'><tr>"));
            Controls.Add(new LiteralControl("<td>"));
            Controls.Add(_label);
            Controls.Add(new LiteralControl("</td>"));
            Controls.Add(new LiteralControl("<td>"));
            Controls.Add(_textBox);
            Controls.Add(new LiteralControl("</td>"));
            Controls.Add(new LiteralControl("</tr></table>"));
            base.CreateChildControls();
        }

In option B, all of the rendering is done within CreateChildControls().  There is nothing wrong with this method, however, I don’t like it as much.  I like my methods to do one thing only.  And since there is a method named Render(), doesn’t it make sense to put the render code in it?  Also, within CreateChildControls, you don’t get to use the RenderBeginTag() method with the enumerations.  That means more chances for mistakes, in my opinion.  If you are using this method within a CompositeControl, it is not necessary to override Render().  The default implementation of Render() will be called. 

Option C:

        protected override void CreateChildControls()
        {
            Controls.Clear();
            _textBox.ID = "txtBox1";
            _label.ID = "label1";
            
            Table table = new Table();
            table.CellPadding = 1;
            TableRow row1 = new TableRow();
            TableCell cell1 = new TableCell();
            cell1.Controls.Add(_label);
            TableCell cell2 = new TableCell();
            cell2.Controls.Add(_textBox);
            row1.Cells.Add(cell1);
            row1.Cells.Add(cell2);
            table.Rows.Add(row1);

            Controls.Add(table);
            base.CreateChildControls();
        }

Lastly, I put Option C to show what NOT to do (most of the time).  In this case, I have created my Table, Row and Cells as actual controls, not literals.  This is extra overhead and it is not needed because the only purpose for my table is to control layout.  The literal controls in options A and B will render quicker.  However, there are times when you NEED to have your Table, etc, included in the Controls collection.  In those cases, this technique can be used.  Once again, there is no need to override the Render() method with this technique.

Results:

So how does this all render in the browser?  I put this markup into an ASP.Net Page:

    <!--Option A -->
    <cc1:MyControl1 runat="server" ID="mc1" />
    <br />
    ------------------------------------------
    <!--Option B -->
    <cc1:MyControl2 runat="server" ID="mc2" />
    <br />
    ------------------------------------------
    <!--Option C -->
    <cc1:MyControl3 runat="server" ID="mc3" />

In the browser, I used “View Source” and got the following:

        <div id="mc1">
            <table cellpadding="1">
                <tr>
                    <td>
                        <span id="mc1_label1"></span>
                    </td>
                    <td>
                        <input name="mc1$txtBox1" type="text" id="mc1_txtBox1" /></td>
                </tr>
            </table>
        </div>
        <br />
        ------------------------------------------
        <div id="mc2">
            <table cellpadding='1'>
                <tr>
                    <td>
                        <span id="mc2_label1"></span>
                    </td>
                    <td>
                        <input name="mc2$txtBox1" type="text" id="mc2_txtBox1" /></td>
                </tr>
            </table>
        </div>
        <br />
        ------------------------------------------
        <div id="mc3">
            <table cellpadding="1" border="0">
                <tr>
                    <td>
                        <span id="mc3_label1"></span>
                    </td>
                    <td>
                        <input name="mc3$txtBox1" type="text" id="mc3_txtBox1" /></td>
                </tr>
            </table>
        </div>

Looks pretty close, huh?  There was one extra trick I did to make these match up.  First, remember that in Option A, I added an extra <div> tag when I rendered my control.  Without it, my option A control’s outer tag would have been <table>.  Either way may work fine in your application.  Second, Options B and C also have the following code included:

        protected override HtmlTextWriterTag TagKey
        {
            get { return HtmlTextWriterTag.Div; }
        }

Without this override, my controls in Options B and C would have an outermost tag of <span> instead of <div>.  Many people prefer to have controls render with <div> tags and this is how to do it!

I hope this information is helpful to you.  Unfortunately, there isn’t always a clear answer of which method is best. 

7 thoughts on “Custom Server Controls: CreateChildControls() or Render()?

  1. Great post, there is much confusion surrounding which is best to use on the Web. I guess it does really come down to preference, before I really only used CreateChildControls but I found that things could get a bit messy combining the HTML rendering and Controls, understanding how to override the Render method gives greater separation between Control wire ups and the eventual UI shown to the user.

    Reply
  2. I create an address server control I am loosing the data during post backs.

    (1) Now i a problem i.e. not able to retain the data modified during postbacks which i need while saving the address of a company in saveaddres
    method. I read the "IPostBackDataHandler" interface neeeds to be implemeted to do this. Please help me how to retain the data. (I read some where that though ViewState you can
    manage the data during post backs. Please provide me a sample for just Address1, Address2 and i will use them for the rest of the address fields.

    I am setting the displayMode "R" ==> read mode (pure display) and "E" ==> edit mode. I can store this info in Session or ViewState variable.
    Please see the contructor code. Is it wrong if yes what other way i need to write it.
    Thank You

    public class Address1 : WebControl, INamingContainer, IPostBackDataHandler
    {
    private Label lblAddress1 = null;
    public TextBox txtAddress1 = null;
    public Label lblAddress1Data = null;
    private string strlblAddress1KeyName = "lblAddress1";
    private string strlblAddress2KeyName = "lblAddress2";

    public Address1()
    {
    lblAddress1 = new Label();
    txtAddress1 = new TextBox();
    lblAddress1Data = new Label();
    }

    public string DisplayMode
    {
    get { return displayMode; }
    set { displayMode = value; }
    }

    protected override void RenderContents(HtmlTextWriter writer)
    {

    EnsureChildControls();
    writer.RenderBeginTag(HtmlTextWriterTag.Tr);
    writer.RenderBeginTag(HtmlTextWriterTag.Td);
    lblAddress1.RenderControl(writer);
    writer.RenderEndTag(); //</td>

    if (displayMode.ToUpper() == "E")
    {
    writer.RenderBeginTag(HtmlTextWriterTag.Td);
    txtAddress1.RenderControl(writer);
    writer.RenderEndTag(); //</td>
    }
    else
    {
    writer.RenderBeginTag(HtmlTextWriterTag.Td);
    lblAddress1Data.RenderControl(writer);
    writer.RenderEndTag(); //</td>
    }

    // NOTE:- SIMILARLY for Address2 and controls of address …….
    writer.RenderEndTag(); //</tr>
    writer.RenderEndTag(); //</table>
    writer.RenderEndTag(); //</Div>
    }

    protected override void CreateChildControls()
    {
    Controls.Clear();
    lblAddress1.ID = strlblAddress1KeyName;
    Controls.Add(lblAddress1);

    if (displayMode.ToUpper() == "E")
    {
    txtAddress1.ID = "txtAddress1"; // +Convert.ToString(HttpContext.Current.Session["AddressCtrlUniqueID"]);
    Controls.Add(txtAddress1);
    }
    else
    {
    lblAddress1Data.Text = "lblAddress1Data"; // +Convert.ToString(HttpContext.Current.Session["AddressCtrlUniqueID"]);
    lblAddress1Data.ID = "lblAddress1Data";
    Controls.Add(lblAddress1Data);
    }

    // NOTE:- SIMILARLY for Address2 and controls of address …….
    base.CreateChildControls();
    }

    public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
    {
    throw new NotImplementedException();
    }

    public void RaisePostDataChangedEvent()
    {
    throw new NotImplementedException();
    }

    (II) .aspx side:-
    __________________
    <cc1:Address1 ID="Address11" runat="server" />
    <cc1:Address1 ID="Address12" runat="server" />

    (III) .aspx.cs code :-
    _______________________

    protected void Page_Load(object sender, EventArgs e)
    {
    if (!Page.IsPostBack)
    {
    Address11.DisplayMode = "E";
    Address11.txtAddress1.Text = "1234 Address1";
    }
    }

    protected void SaveAddress_Click(object sender, EventArgs e)
    {
    string s = Address11.txtAddress1.Text;
    SaveAddress(s); // Assume it is the save method.
    }

    Reply
  3. This article is good and basing on this i created my address server control, but the state/data of my controls txtAddress1 and txtAddress2 TextBox controls in my Composite Address control is getting lost. One of the reason’s is that during postback of any of my other server side controls event the Address control is getting recreated and the state ( whatever data newly modified/newly entered in txtAddress1 or txtAddress2 by me is getting lost). The viewState or State of controls is not getting maintained. My question is how to maintain the state of the composite control. How to stop the Composite control’s recreation or if that is how it works then how to maintain the view state of my address contrlo’s data (like txtAddress1 and txtAddress2) until i click on SaveAddress button which reads data from these control’s and save’s the data to database. All other control’s on form are maintaining their viewstate, why not address control’s txtAddress1 and txtAddress2. I will post my code soon in my comments.

    Reply
  4. I strongly recomend the Option A.

    I read your article this morning but at that time I had already programmed Option C and so I kept it.

    I am facing a problem for maybe 3 hours and just know I found that I can’t use Option C (the framework is not rendering validators – none of them). So, I tested using the Render() method and everything worked fine.

    Best regards,
    Marco Alves from Brazil.

    Reply

Leave a reply

required

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>