views:

566

answers:

2

I've build a custom WebControl, which has the following structure:

<gws:ModalBox ID="ModalBox1" HeaderText="Title" runat="server">
    <Contents>
        <asp:Label ID="KeywordLabel" AssociatedControlID="KeywordTextBox" runat="server">Keyword: </asp:Label><br />
        <asp:TextBox ID="KeywordTextBox" Text="" runat="server" />
    </Contents>
    <Footer>(controls...)</Footer>
</gws:ModalBox>

The control contains two ControlCollection properties, 'Contents' and 'Footer'. Never tried to build a control with multiple control collections, but solved it like this (simplified):

[PersistChildren(false), ParseChildren(true)]
public class ModalBox : WebControl
{
    private ControlCollection _contents;
    private ControlCollection _footer;

    public ModalBox()
        : base()
    {
        this._contents = base.CreateControlCollection();
        this._footer = base.CreateControlCollection();
    }

    [PersistenceMode(PersistenceMode.InnerProperty)]
    public ControlCollection Contents { get { return this._contents; } }

    [PersistenceMode(PersistenceMode.InnerProperty)]
    public ControlCollection Footer { get { return this._footer; } }

    protected override void RenderContents(HtmlTextWriter output)
    {
        // Render content controls.
        foreach (Control control in this.Contents)
        {
            control.RenderControl(output);
        }

        // Render footer controls.
        foreach (Control control in this.Footer)
        {
            control.RenderControl(output);
        }
    }
}

However it seems to render properly, it doesn't work anymore if I add some asp.net labels and input controls inside the property (see above asp.net code). I'll get the HttpException:

Unable to find control with id 'KeywordTextBox' that is associated with the Label 'KeywordLabel'.

Somewhat understandable, because the label appears before the textbox in the controlcollection. However, with default asp.net controls it does work, so why doesn't this work? What am I doing wrong? Is it even possible to have two control collections in one control? Should I render it differently?

Thanks for replies.

A: 

I'm not sure that will work. If you make use of templates however you can get the control to render output correctly.

First, define a class to be used as the type for the container control:

public class ContentsTemplate : Control, INamingContainer
{
}

And now the custom control:

[PersistChildren(false), ParseChildren(true)]
public class ModalBox : CompositeControl
{

  [PersistenceMode(PersistenceMode.InnerProperty)]
  [TemplateContainer(typeof(ContentsTemplate))]
  public ITemplate Contents { get; set; }

  [PersistenceMode(PersistenceMode.InnerProperty)]
  [TemplateContainer(typeof(ContentsTemplate))]
  public ITemplate Footer { get; set; }

  protected override void CreateChildControls()
  {
    Controls.Clear();

    var contentsItem = new ContentsTemplate();
    Contents.InstantiateIn(contentsItem);
    Controls.Add(contentsItem);

    var footerItem = new ContentsTemplate();
    Footer.InstantiateIn(footerItem);
    Controls.Add(footerItem);
  }

}
David Gardiner
I'm hitting a similar problem as op and this will not work. You can't reference any controls inside the ITemplates as they aren't instantiated properly, which defeats the purpose of this type of control.
mattmanser
+1  A: 

You can use two Panels as parents of your two control collections (and they would provide grouping and improved readability). Add your controls from each collection to the Controls collection of respective panel, and in the Render method just call Render methods of each panel. Panels will automatically render their children, and will provide them with their own namespace, so, you can have controls with similar IDs in different panels.

Kaerber
Yes this will work!
DocV