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...?
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.
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.
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.
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> { }
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With