I want to do something like (Updated example):
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.
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> { }
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;
using System.Web.UI.HtmlControls;
namespace Gui.Controls
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:GPMenu runat=server></{0}:GPMenu>")]
[ParseChildren(false)]
public class GPMenu : WebControl
{
private void CreateDynamicControls()
{
//some code for Creating Dynamic Controls
} //eof method
#region FieldsAndProps
#region ActiveTab
protected int _ActiveTab;
[Bindable(true)]
[Category("Appearance")]
public int ActiveTab // Public properties
{
get { return _ActiveTab; }
set { _ActiveTab = value; }
}
#endregion ActiveTab
#region Caption
protected string _Caption = "";
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
public string Caption // Public properties
{
get { return _Caption; }
set { _Caption = value; }
}
#endregion Caption
#region MenuId
protected string _MenuId = "0";
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
public string MenuId // Public properties
{
get { return _MenuId; }
set { _MenuId = value; }
}
#endregion MenuId
#region Text
protected string _Text = "";
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
public string Text // Public properties
{
get { return _Text; }
set { _Text = value; }
}
#endregion Text
#region UserObj
private FB.User userObj;
public FB.User UserObj
{
get
{
return userObj;
} //eof get
set
{
userObj = value;
} //eof set
} //eof prop
#endregion UserObj
#region ListTabWizard
private List<App.Models.TabWizard> _ListWizard;
[Bindable(true)]
[Category("Appearance")]
[Description(" The data structure used for databinding the menu ")]
public List<App.Models.TabWizard> ListWizard
{
get { return _ListWizard; }
set { _ListWizard = value; }
}
#endregion ListWizard
#endregion FieldsAndProps
#region Constructors
public GPMenu()
{
//Statement to run while initialize of control
}
#endregion Constructors
#region Overrides
public override void RenderControl(HtmlTextWriter writer)
{
RenderContents(writer);
}
protected override void RenderContents(HtmlTextWriter output)
{
//Codes for rendering html to be written here
output.WriteBeginTag("div");
output.WriteAttribute("class", _Caption);
output.WriteAttribute("ID", this.ID);
RenderChildren(output); // Render Child Control is Exists
output.WriteEndTag("div");
}
public override void DataBind()
{
this.Controls.Clear();
base.DataBind();
#region Debug
//old configure the logger
//logger.Debug("Custom GPMenu DataBind called ");
#endregion Debug
#region AddTheTopDiv
Panel panTabMenuTop = new Panel();
panTabMenuTop.ID = "panTabMenuTop";
panTabMenuTop.CssClass = "CssClassPanTabMenuTop";
this.Controls.Add(panTabMenuTop);
#endregion AddTheTopDiv
#region AddTheUlOpenTag
HtmlGenericControl conGenUi = new HtmlGenericControl("ul");
conGenUi.ID = "paakallo";
#endregion AddTheUlOpenTag
#region ForEachMenuItemInTheListAddTheLinks
if (this.ListWizard == null)
throw new Exception(" The List of menu items is null for " + this.ID);
#region ForEachWizardItem
for (int iListMenuItem = 0; iListMenuItem < this.ListWizard.Count; iListMenuItem++)
{
App.Models.TabWizard objMenuItem = this.ListWizard[iListMenuItem];
#region AddTheLiOpenTag
PlaceHolder objPlaceHolderLiOpen = new PlaceHolder();
objPlaceHolderLiOpen.Controls.Add(new LiteralControl("<li>"));
conGenUi.Controls.Add(objPlaceHolderLiOpen);
#endregion AddTheLiOpenTag
#region BuildTheLocalizedLink
#region Check that the whole MenuItem data structure is not null
if (objMenuItem == null)
throw new Exception(
"The building of the GPMenu failed since \n: " +
" null MenuItem at " + iListMenuItem.ToString() + " for " + this.ID);
#endregion Check that the whole MenuItem data structure is not null
//the variant with LinkButton does PostBack == BAD !!!
PlaceHolder objPlaceHolderLink = new PlaceHolder();
#region BuildTheLinkId_with_MenuItemId
if (String.IsNullOrEmpty(objMenuItem.Link_Id.ToString()))
{
throw new Exception(
"The building of the GPMenu failed since \n: " +
" null or empty MenuItemId for " + iListMenuItem.ToString() + " for " + this.ID);
}
#endregion BuildTheLinkId_with_MenuItemId
#region AddTheTextForTheLink_with_GuiPageLinkURL
//check that this LinkItem points to a registered page
if (objMenuItem.Link_Id == null)
{
throw new Exception(
"The building of the GPMenu failed since \n: " +
" null GuiPage for " + iListMenuItem.ToString() + " for " + this.ID);
}
string strActiveLinkCssClass = String.Empty;
string strTabTxt = String.Empty;
try
{
strTabTxt = objMenuItem.TabTitle;
}
catch (System.Exception ex)
{
continue;
}
#region BuildTheActualLink
string strLinkTxt = String.Empty;
strLinkTxt += "<a " + strActiveLinkCssClass + " href=\"";
string strLinkUrl = Http.UrlHandler.ExpandUrl(objMenuItem.LinkURL);
#endregion BuildTheActualLinks
#region AddTheColorForTheLinkAndRemoveTheBorderOfActiveTab
if (this.ActiveTab != null && strLinkUrl.Contains("tabid=" + this.ActiveTab.ToString()))
strActiveLinkCssClass = "class=\"CssClassGPMenuActiveLink\"";
#endregion AddTheColorForTheLinkAndRemoveTheBorderOfActiveTab
strLinkTxt += strLinkUrl + "\"" + strActiveLinkCssClass;
#endregion AddTheTextForTheLink_with_GuiPageLinkURL
#region AddTheToolTip
string strVisibleToolTipTxt = String.Empty;
try
{
strVisibleToolTipTxt = objMenuItem.ItemButtonToolTip;
}
catch (System.Exception ex)
{
continue;
}
strLinkTxt += "\" title=\"" + strVisibleToolTipTxt +
"\"><span>" + strTabTxt + "</span></a>";
#endregion AddTheToolTip
#region AddTheLabel_used_in_The_TabMenu
objPlaceHolderLink.Controls.Add(new LiteralControl(strLinkTxt));
conGenUi.Controls.Add(objPlaceHolderLink);
#endregion AddTheLabel_used_in_The_TabMenu
#endregion BuildTheLocalizedLink
#region AddTheLiCloseTag
PlaceHolder objPlaceHolderLiClose = new PlaceHolder();
objPlaceHolderLiClose.Controls.Add(new LiteralControl("</li>"));
conGenUi.Controls.Add(objPlaceHolderLiClose);
#endregion AddTheLiCloseTag
} //eof foreach MenuItem in List
#endregion ForEachWizardItem
#endregion ForEachMenuItemInTheListAddTheLinks
#region AddTheUlCloseTag
this.Controls.Add(conGenUi);
#region AddTheBottomDiv
Panel panTabMenuBottom = new Panel();
panTabMenuBottom.ID = "panTabMenuBottom";
panTabMenuBottom.CssClass = "CssClassPanTabMenuBottom";
this.Controls.Add(panTabMenuBottom);
#endregion AddTheBottomDiv
#endregion AddTheUlCloseTag
} //eof method
#region TemplateMethods
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
} //eof method
protected override void CreateChildControls()
{
base.CreateChildControls();
this.CreateDynamicControls();
} //eof method
protected override object SaveViewState()
{
return new Pair(base.SaveViewState(), null);
} //eof method
protected override void LoadViewState(object savedState)
{
base.LoadViewState(((Pair)savedState).First);
EnsureChildControls();
} //eof method
#endregion TemplateMethods
#endregion Overrides
} //eof class
} //eof namespace
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Collections.Specialized;
namespace App.Models
{
/// <summary>
/// Serves as the model for the tabwizard page
/// </summary>
public class TabWizard
{
#region Properties
#region TabWizardId
public long tbGuiTabWizardId { get; set; }
#endregion TabWizardId
#region TabId
public int TabId { get; set; }
#endregion TabId
#region Link_Id
public int Link_Id { get; set; }
#endregion Link_Id
#region WizardId
public int WizardId { get; set; }
#endregion WizardId
#region TabOrder
public int TabOrder { get; set; }
#endregion TabOrder
#region TabTitle
public string TabTitle { get; set; }
#endregion TabTitle
#region TabHasManyButtons
public bool TabHasManyButtons { get; set; }
#endregion TabHasManyButtons
#region ButtonTitle
public string ButtonTitle { get; set; }
#endregion ButtonTitle
#region Description
public string Description { get; set; }
#endregion Description
#region ProcToStart
public string ProcToStart { get; set; }
#endregion ProcToStart
#region ProcId
public string ProcId { get; set; }
#endregion ProcId
#region ItemMetaId
public string ItemMetaId { get; set; }
#endregion ItemMetaId
#region ItemId
public string ItemId { get; set; }
#endregion ItemId
#region ItemStartProc
public string ItemStartProc { get; set; }
#endregion ItemStartProc
#region ItemButtonToolTip
public string ItemButtonToolTip { get; set; }
#endregion ItemButtonToolTip
#region ItemButtonText
public string ItemButtonText { get; set; }
#endregion ItemButtonText
#region TypeOfTab
public string TypeOfTab { get; set; }
#endregion TypeOfTab
#region WizardHasStepping
public bool WizardHasStepping { get; set; }
#endregion WizardHasStepping
#region RunProcOnTabClick
public bool RunProcOnTabClick { get; set; }
#endregion RunProcOnTabClick
#region TabIsPRS
public bool TabIsPRS { get; set; }
#endregion TabIsPRS
#region ItemIsPRS
public bool ItemIsPRS { get; set; }
#endregion ItemIsPRS
#region FlagPresentAreYouSurePrompt
public bool FlagPresentAreYouSurePrompt { get; set; }
#endregion FlagPresentAreYouSurePrompt
#region PromptAreYouSureText
public string PromptAreYouSureText { get; set; }
#endregion PromptAreYouSureText
#region PageId
public int PageId { get; set; }
#endregion PageId
#region MenuId
public int MenuId { get; set; }
#endregion MenuId
#endregion Properties
#region Props
#region Constructor
public TabWizard(System.Data.DataRow dr)
{
if (dr["tbGuiTabWizardId"] != null && !(dr["tbGuiTabWizardId"] is DBNull))
this.tbGuiTabWizardId = System.Convert.ToInt64(dr["tbGuiTabWizardId"]);
if (dr["TabId"] != null && !(dr["TabId"] is DBNull))
this.TabId = System.Convert.ToInt32(dr["TabId"]);
if (dr["Link_Id"] != null && !(dr["Link_Id"] is DBNull))
this.Link_Id = System.Convert.ToInt32(dr["Link_Id"]);
if (dr["WizardId"] != null && !(dr["WizardId"] is DBNull))
this.WizardId = System.Convert.ToInt32(dr["WizardId"]);
if (dr["TabOrder"] != null && !(dr["TabOrder"] is DBNull))
this.TabOrder = System.Convert.ToInt32(dr["TabOrder"]);
if (dr["TabTitle"] != null && !(dr["TabTitle"] is DBNull))
this.TabTitle = System.Convert.ToString(dr["TabTitle"]);
if (dr["TabHasManyButtons"] != null && !(dr["TabHasManyButtons"] is DBNull))
this.TabHasManyButtons = System.Convert.ToBoolean(dr["TabHasManyButtons"]);
if (dr["ButtonTitle"] != null && !(dr["ButtonTitle"] is DBNull))
this.ButtonTitle = System.Convert.ToString(dr["ButtonTitle"]);
if (dr["Description"] != null && !(dr["Description"] is DBNull))
this.Description = System.Convert.ToString(dr["Description"]);
if (dr["ProcToStart"] != null && !(dr["ProcToStart"] is DBNull))
this.ProcToStart = System.Convert.ToString(dr["ProcToStart"]);
if (dr["ProcId"] != null && !(dr["ProcId"] is DBNull))
this.ProcId = System.Convert.ToString(dr["ProcId"]);
if (dr["ItemMetaId"] != null && !(dr["ItemMetaId"] is DBNull))
this.ItemMetaId = System.Convert.ToString(dr["ItemMetaId"]);
if (dr["ItemStartProc"] != null && !(dr["ItemStartProc"] is DBNull))
this.ItemStartProc = System.Convert.ToString(dr["ItemStartProc"]);
if (dr["ItemButtonToolTip"] != null && !(dr["ItemButtonToolTip"] is DBNull))
this.ItemButtonToolTip = System.Convert.ToString(dr["ItemButtonToolTip"]);
if (dr["ItemButtonText"] != null && !(dr["ItemButtonText"] is DBNull))
this.ItemButtonText = System.Convert.ToString(dr["ItemButtonText"]);
if (dr["TypeOfTab"] != null && !(dr["TypeOfTab"] is DBNull))
this.TypeOfTab = System.Convert.ToString(dr["TypeOfTab"]);
if (dr["WizardHasStepping"] != null && !(dr["WizardHasStepping"] is DBNull))
this.WizardHasStepping = System.Convert.ToBoolean(dr["WizardHasStepping"]);
if (dr["RunProcOnTabClick"] != null && !(dr["RunProcOnTabClick"] is DBNull))
this.RunProcOnTabClick = System.Convert.ToBoolean(dr["RunProcOnTabClick"]);
if (dr["TabIsPRS"] != null && !(dr["TabIsPRS"] is DBNull))
this.TabIsPRS = System.Convert.ToBoolean(dr["TabIsPRS"]);
if (dr["ItemIsPRS"] != null && !(dr["ItemIsPRS"] is DBNull))
this.ItemIsPRS = System.Convert.ToBoolean(dr["ItemIsPRS"]);
if (dr["FlagPresentAreYouSurePrompt"] != null && !(dr["FlagPresentAreYouSurePrompt"] is DBNull))
this.FlagPresentAreYouSurePrompt = System.Convert.ToBoolean(dr["FlagPresentAreYouSurePrompt"]);
if (dr["PromptAreYouSureText"] != null && !(dr["PromptAreYouSureText"] is DBNull))
this.PromptAreYouSureText = System.Convert.ToString(dr["PromptAreYouSureText"]);
if (dr["PageId"] != null && !(dr["PageId"] is DBNull))
this.PageId = System.Convert.ToInt32(dr["PageId"]);
if (dr["LinkURL"] != null && !(dr["LinkURL"] is DBNull))
this.LinkURL = System.Convert.ToString(dr["LinkURL"]);
if (dr["MenuId"] != null && !(dr["MenuId"] is DBNull))
this.MenuId = System.Convert.ToInt16(dr["MenuId"]);
} //eof constructor
#endregion Constructor
#region LinkURL
private string _LinkURL = String.Empty;
public string LinkURL
{
get { return _LinkURL; }
set { _LinkURL = value; }
}
#endregion LinkURL
#endregion Props
} //eof class
} //eof namespace