Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 785
rball Avatar asked Mar 03 '09 00:03

rball


People also ask

How many types of ASP NET controls are there?

The ASP.NET Framework contains more than 90 controls. These controls can be divided into seven groups: Standard Controls—Enable you to render standard form elements such as buttons, input fields, and labels.

What is difference between ASP.NET control and HTML control?

HTML controls are client side controls, so it does not provide STATE management. ASP.Net Controls are Server side controls, provides STATE management. HTML control does not require rendering. ASP.Net controls require rendering.

What is the use of Runat property in ASP NET controls?

The runat="server" tag in ASP.NET allows the ability to convert/treat most any HTML element as a server-side control that you can manipulate via code at generation time. Some controls have explicit implementations, others simply revert to a generic control implementation.


2 Answers

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> { }
like image 61
Rex M Avatar answered Nov 06 '22 15:11

Rex M


Follow up

Rex M provided the answer but just wanted to follow up on what I've also found for posterity.

It seems like you can do either:

<ui:Tabs runat="server">
    <ui:Tab TabText="Blah">
        <ui:Node Url="~/Default.aspx" />
        <ui:Node Url="~/More.aspx" />
        <ui:Node Url="~/Another.aspx" />
    </ui:Tab>
    <ui:Tab TabText="Nanner">
        <ui:Node Url="~/Default.aspx" />
        <ui:Node Url="~/More.aspx" />
        <ui:Node Url="~/Another.aspx" />
    </ui:Tab>
    <ui:Tab TabText="High There">
        <ui:Node Url="~/Default.aspx" />
        <ui:Node Url="~/More.aspx" />
        <ui:Node Url="~/Another.aspx" />
    </ui:Tab>
</ui:Tabs>

OR

<ui:Tabs runat="server">
    <TabItems>
        <ui:Tab InnerHeading="Big Huge Heading" TabText="Big">
            <NodeItems>
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
            </NodeItems>
        </ui:Tab>
    </TabItems>
    <TabItems>
        <ui:Tab InnerHeading="Hi ya" TabText="Hi">
            <NodeItems>
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
            </NodeItems>
        </ui:Tab>
    </TabItems>
</ui:Tabs>

Then within code:

namespace Controls
{
    [ToolboxData("<{0}:Tabs runat=server></{0}:Tabs>"), ParseChildren(true, "TabItems")]
    public class Tabs : BaseControl, INamingContainer
    {
        private TabCollection tabItems;
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public TabCollection TabItems
        {
            get
            {
                if (tabItems == null) 
                { 
                    tabItems = new TabCollection(); 
                } 
                return tabItems;
            }
        }

        protected override void Render(HtmlTextWriter writer)
        {
            writer.Write("<div id=\"tabs\" class=\"pane\" style=\"display: none\"><ul>");
            int tabNumber = 1;
            foreach (Tab tab in TabItems)
            {
                string li = string.Format("<li id=\"tab_{0}\"><a href=\"#tab{0}\"><span>{1}</span></a></li>", tabNumber, tab.TabText);
                tabNumber++;
                writer.Write(li);
            }
            writer.Write("</ul>");
            tabNumber = 1;
            foreach (Tab tab in TabItems)
            {
                string div = string.Format("<div id=\"tab{0}\" class=\"pane\"><h1>{1}</h1>", tabNumber, tab.InnerHeading);
                tabNumber++;
                writer.Write(div);
                foreach (Node node in tab.NodeItems)
                {
                    string a = string.Format("<a href='{0}'>{1}</a>", node.Url, "Text holder");
                    writer.Write(a);
                }
                writer.Write("</div>");
            }
            writer.Write("</div>");
        }
    }

    public class TabCollection : List<Tab> { }

    [ParseChildren(true, "NodeItems")]
    public class Tab
    {
        private string tabText = string.Empty;
        private string innerHeading = string.Empty;
        private string showOn = string.Empty;
        public string TabText
        {
            get { return tabText; }
            set { tabText = value; }
        }
        public string InnerHeading
        {
            get { return innerHeading; }
            set { innerHeading = value; }
        }

        public string ShowOn
        {
            get { return showOn; }
            set { showOn = value; }
        }

        private NodeCollection nodeItems;
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public NodeCollection NodeItems
        {
            get
            {
                if (nodeItems == null)
                {
                    nodeItems = new NodeCollection();
                }
                return nodeItems;
            }
        }
    }

    public class NodeCollection : List<Node> { }

    public class Node
    {
        private string url = string.Empty;
        public string Url
        {
            get { return url; }
            set { url = value; }
        }
    }
}

This will obviously be changing (mine's going to be reading from the web.sitemap among other changes), but this should get anyone with the same needs well under their way.

like image 42
rball Avatar answered Nov 06 '22 15:11

rball