C# – Why does the ASP.Net custom control render two id attributes on the first child control

asp.netccustom-controlsnet

I'm writing a small ASP.Net custom control inheriting from CompositeControl. The control is just a panel containing two subpanels with a label in each subpanel. When rendered, I am seeing in the HTML source that the first child control of my custom control gets two id attributes – the first being the id of the custom control itself and the second being the ID property I assign to the first child control. Why is this happening?

Code:

[ToolboxData("<{0}:MessageBox runat=server></{0}:MessageBox>")]
public class MessageBox : CompositeControl {

    private Panel _MessageHeaderContainer = null;
    private Label _MessageHeaderLabel = null;
    private Panel _MessageDetailsContainer = null;
    private Label _MessageDetailsLabel = null;

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

    protected override void CreateChildControls() {

        // Message header area.
        _MessageHeaderContainer = new Panel();
        _MessageHeaderContainer.ID = "HeaderContainer";
        _MessageHeaderContainer.CssClass = "__MessageBox_Container";
        this.Controls.Add(_MessageHeaderContainer);

        // Message header text.
        _MessageHeaderLabel = new Label();
        _MessageHeaderLabel.ID = "HeaderLabel";
        _MessageHeaderLabel.Text = "[ Header ]";
        _MessageHeaderContainer.Controls.Add(_MessageHeaderLabel);

        // Message details area.
        _MessageDetailsContainer = new Panel();
        _MessageDetailsContainer.ID = "DetailsContainer";
        this.Controls.Add(_MessageDetailsContainer);

        // Message details text.
        _MessageDetailsLabel = new Label();
        _MessageDetailsLabel.ID = "DetailsLabel";
        _MessageDetailsLabel.Text = "[ Details ]";
        _MessageDetailsContainer.Controls.Add(_MessageDetailsLabel);

    }

    protected override void RenderContents(HtmlTextWriter output) {
        AddAttributesToRender(output);

        // Render the box.
        _MessageHeaderContainer.RenderControl(output);
        _MessageDetailsContainer.RenderControl(output);
    }
}

Usage in ASPX page:

<cc:MessageBox ID="ctlMessageBox" runat="server" />

HTML output:

<div id="ctl00_ctl00_ctlMessageBox">
    <div id="ctl00_ctl00_ctlMessageBox" id="ctl00_ctl00_ctlMessageBox_HeaderContainer" class="__MessageBox_Container">
        <span id="ctl00_ctl00_ctlMessageBox_HeaderLabel">[ Header ]</span>
    </div><div id="ctl00_ctl00_ctlMessageBox_DetailsContainer">
        <span id="ctl00_ctl00_ctlMessageBox_DetailsLabel">[ Details ]</span>
    </div>
</div>

Best Answer

You can fix this by removing the RenderContents method entirely - don't replace it with anything. By default, composite controls render all their child controls. If you do this, your control produces clean markup:

<div id="test">
    <div id="test_HeaderContainer" class="__MessageBox_Container">
     <span id="test_HeaderLabel">[ Header ]</span>
    </div>
    <div id="test_DetailsContainer">
     <span id="test_DetailsLabel">[ Details ]</span>
    </div>
</div>

As you've noted, the AddAttributesToRender method is the culprit - it's a convenience method renders all the top-level control's attributes (whether the current context is the top-level control or not). The only reason you would call this is if you were taking full control of the rendering process yourself and wanted to render all the attributes for the top-level control in a single statement.

Related Topic