Search code examples
.netasp.netasp.net-controls

Is there a way to put inner controls inside a ASP.NET Custom Control?


I want to do something like (Updated example):

<uc:Tabs>
  <Tab Name="A handy tab">
    <Node Url="~/Default.aspx" />
    <Node Url="~/Node2.aspx" />
  </Tab>      
  <Tab Name="Another handy tab">
    <Node Url="~/Neato.aspx" />
    <Node Url="~/Node3.aspx" />
    <Node Url="~/Node4.aspx" />
  </Tab>
<uc:Tabs>

Possible? Any tutorials or how-to's? I'm not sure what to even search on or what this is called so haven't found anything so far. Inner controls? Inner collection something something...?


Solution

  • Use the ParseChildrenAttribute and PersistChildrenAttribute attributes:

    [ParseChildren(false)]
    [PersistChildren(true)]
    public class MyControl : UserControl { }
    

    This will cause any controls you put inside the reference:

    <uc:MyControl runat="server">
      <asp:TextBox runat="server" />
    <uc:MyControl>
    

    To be appended to the end of the Controls collection of your UserControl contents.

    However, if you want to have a collection of controls, you should probably use a server control and not a user control. For a control that works like this:

    <foo:TabControl runat="server">
        <Tabs>
            <foo:Tab CssClass="myclass" Title="Hello World" />
        </Tabs>
    </foo:TabControl>
    

    You need a Control class that has a Tabs property; the Tabs property should be a Collection; and it should contain objects of type Tab. I've created the three classes here:

    [ParseChildren(true, "Tabs")]
    public class TabControl: WebControl, INamingContainer
    {
        private TabCollection _tabs;
    
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public TabCollection Tabs
        {
            get
            {
                if (_tabs == null)
                {
                    _tabs = new TabCollection();
                }
                return _tabs;
            }
        }
    
        protected override void Render(HtmlTextWriter writer)
        {
            foreach (Tab tab in Tabs)
            {
                writer.WriteBeginTag("div");
                writer.WriteAttribute("class", tab.CssClass);
                writer.Write(HtmlTextWriter.TagRightChar);
                writer.Write("this is a tab called " + tab.Title);
                writer.WriteEndTag("div");
            }
        }
    }
    

    And the tab class:

    public class Tab
    {
        public string CssClass { get; set; }
        public string Title { get; set; }
    }
    

    And the tab collection:

    public class TabCollection : Collection<Tab> { }