This is post 1 of 5 in a series titled Patterns for navigation controls
Most websites need some sort of navigation UI. If you have a site with more than a handful of pages then you're likely to need to create some components for rendering sets of links that show the structure of your site. So continuing my theme of simple patterns for Sitecore code, here is the first of a few posts on some approaches to navigation.
<asp:Repeater runat="server" ID="navRepeater"> <HeaderTemplate><ul></HeaderTemplate> <ItemTemplate> <li><asp:HyperLink runat="server" ID="navLink" /></li> </ItemTemplate> <FooterTemplate></ul></FooterTemplate> </asp:Repeater>
With some simple code to fill in the data:
protected void Page_Load(object sender, EventArgs e) { var rootItem = Sitecore.Context.Database.GetItem(Sitecore.Context.Site.ContentStartPath); navRepeater.DataSource = rootItem.Children; navRepeater.ItemDataBound += navRepeater_ItemDataBound; navRepeater.DataBind(); } void navRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e) { if(e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item) { var item = e.Item.DataItem as Item; var navLink = e.Item.FindControl("navLink") as HyperLink; navLink.Text = item.DisplayName; navLink.NavigateUrl = Sitecore.Links.LinkManager.GetItemUrl(item); } }
The root item of the current site is specified in the
<sites>
config of your site, which can be accessed via the
Sitecore.Context.Site.ContentStartPath
property. (Generally this is the right starting place for primary navigation - but if your site's IA doesn't follow that pattern you can employ alternative rules, or just use the component's Data Source here if you want to give editors the power to change this) Loading that item and binding its children to a repeater lets us display a list of links which can be formatted with some CSS.
<asp:Repeater runat="server" ID="navRepeater"> <HeaderTemplate><ul></HeaderTemplate> <ItemTemplate> <li> <asp:HyperLink runat="server" ID="navLink" /> <asp:Repeater runat="server" ID="childRepeater"> <HeaderTemplate><ul></HeaderTemplate> <ItemTemplate> <li><asp:HyperLink runat="server" ID="childLink" /></li> </ItemTemplate> <FooterTemplate></ul></FooterTemplate> </asp:Repeater> </asp:Repeater> </li> </ItemTemplate> <FooterTemplate></ul></FooterTemplate> </asp:Repeater>
And the code similarly:
protected void Page_Load(object sender, EventArgs e) { var rootItem = Sitecore.Context.Database.GetItem(Sitecore.Context.Site.ContentStartPath); navRepeater.DataSource = rootItem.Children; navRepeater.ItemDataBound += navRepeater_ItemDataBound; navRepeater.DataBind(); } void navRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e) { if(e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item) { var item = e.Item.DataItem as Item; var navLink = e.Item.FindControl("navLink") as HyperLink; var childRepeater = e.Item.FindControl("childRepeater") as Repeater; navLink.Text = item.DisplayName; navLink.NavigateUrl = Sitecore.Links.LinkManager.GetItemUrl(item); childRepeater.DataSource = item.Children; childRepeater.ItemDataBound += childRepeater_ItemDataBound; childRepeater.DataBind(); } } void childRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e) { if (e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item) { var item = e.Item.DataItem as Item; var childLink = e.Item.FindControl("childLink") as HyperLink; childLink.Text = item.DisplayName; childLink.NavigateUrl = Sitecore.Links.LinkManager.GetItemUrl(item); } }
It's fairly common to add a bit of CSS and/or jQuery behaviour to make the second level of this data appear and disappear as a hover or click effect. And that behaviour can be applied on top of this mark-up using whatever patterns and styles are appropriate to your site.
You can then add this template to the inheritance tree for your own page templates.
The code can then be adapted to filter out any items which don't have the "show in nav" field checked with a simple query instead of processing the
Children
property of the items:
private string ShowInNavigationQuery = "*[@ShowInNavigation='1']"; protected void Page_Load(object sender, EventArgs e) { var rootItem = Sitecore.Context.Database.GetItem(Sitecore.Context.Site.ContentStartPath); navRepeater.DataSource = rootItem.Axes.SelectItems(ShowInNavigationQuery); navRepeater.ItemDataBound += navRepeater_ItemDataBound; navRepeater.DataBind(); } void navRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e) { if(e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item) { var item = e.Item.DataItem as Item; var navLink = e.Item.FindControl("navLink") as HyperLink; var childRepeater = e.Item.FindControl("childRepeater") as Repeater; navLink.Text = item.DisplayName; navLink.NavigateUrl = Sitecore.Links.LinkManager.GetItemUrl(item); childRepeater.DataSource = item.Axes.SelectItems(ShowInNavigationQuery); childRepeater.ItemDataBound += childRepeater_ItemDataBound; childRepeater.DataBind(); } }
The
ShowInNavigationQuery
finds all the children of an item which have the "show" flag set to true. This is applied to the two repeater bindings in place of the previous reference to the child items collection.
More on navigation next week.
↑ Back to top