This is my first Sitecore CMS post, so I thought I will start with something very simple, what could be more simpler than ‘Top Navigation’
Sitecore gives you the flexibility to render items by XSLT or by .NET UserControls.
I prefer using .NET Controls over XSLT for various reasons
1. XSLT are easy to implement and fast but difficult to maintain when you have to introduce complex logic for navigation (like if user is logged in or not)
2. XSLT are difficult to debug (imagine going through 3 to 4 foreach loops!)
3. XSLT are so old, I feel like working in 90s (but people still use it)
Anyways, I will implement a very simple top navigation with a repeater in a .NET UserControl which will take top childrens of ‘Home’ Item/Node and render them
as top navigation
Here is the code for repeater (pretty standard)
<asp:Repeater ID="RepeaterTopNavigation" runat="server" OnItemDataBound="RepeaterTopNavigation_ItemDataBound"> <HeaderTemplate> <ul> <li><a href="/home.aspx">Home</a></li> </HeaderTemplate> <ItemTemplate> <li> <asp:HyperLink ID="HyperLinkTopNavigation" runat="server"></asp:HyperLink> </li> </ItemTemplate> <FooterTemplate> </ul> </FooterTemplate> </asp:Repeater>
And here is the code behind for the repeater
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using Sitecore.Data.Items; using Sitecore.Links; namespace SampleSite.Controls { public partial class TopNavigation : System.Web.UI.UserControl { protected void Page_Load(object sender, EventArgs e) { //get the home item, this is hardcoded but you can define it in web.config Item home = Sitecore.Context.Database.GetItem("/sitecore/content/home"); //get all children from home using Sitecore API Item[] children = home.Children.ToArray(); //Bind the children to repeater RepeaterTopNavigation.DataSource = children; RepeaterTopNavigation.DataBind(); } public void RepeaterTopNavigation_ItemDataBound(object sender, RepeaterItemEventArgs e) { //gets the current data item from RepeaterItemEventsArgs and cast it as a Sitecore Item Item currentItem = (Item)e.Item.DataItem; //check if it is not null, safety first if (currentItem != null) { //check if it is coming from Item or Alternating Item template if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) { //Find the HyperLink control that has been defined in repeater HyperLink topNavigation = (HyperLink)e.Item.FindControl("HyperLinkTopNavigation"); //Use Sitecore API to get the link to the Item and upadte the href property of link topNavigation.NavigateUrl = LinkManager.GetItemUrl(currentItem); //Assign name to the link topNavigation.Text = currentItem.Name; } } } } }
Explanation
1. Get the home Item
2. Get the children of home Item as array
3. Bind them to repeater
4. For OnItemDataBound, cast the DataItem as Sitecore Item and update the HyperLink property for each
Drop the control on your main layout and check for any build errors.
Thats it!
I agree with your choice of favoring UserControls over XSLT’s. In fact, the only component that’s easier to develop using XSLT’s are the infamous hierahical left menu. And even so, I perfer to develop it using usercontrols as it gives me greater extendability.
http://briancaos.wordpress.com/2011/01/25/using-usercontrols-instead-of-xslt-in-sitecore-projects/
http://briancaos.wordpress.com/2011/08/03/creating-a-tree-like-left-menu-in-sitecore-using-usercontrols-and-c/
Hi Naveed,
Thanks for this solution. I’m trying to integrate it into my sitecore website but I’m having some problems. I get a System.NullReferenceException: Object reference not set to an instance of an object. error when I publish the site.
The source error is:
Line 45: HyperLink topNavigation = (HyperLink)e.Item.FindControl(“HyperLinkTopNavigation”);
Line 46: //Use Sitecore API to get the link to the Item and upadte the href property of link
Line 47: topNavigation.NavigateUrl = LinkManager.GetItemUrl(currentItem);
Line 48: //Assign name to the link
Line 49: topNavigation.Text = currentItem.Name;
———————-
The stack trace is:
[NullReferenceException: Object reference not set to an instance of an object.]
Layouts.Topnavigation.TopnavigationSublayout.RepeaterTopNavigation_ItemDataBound(Object sender, RepeaterItemEventArgs e) in c:\inetpub\wwwroot\Infotech\Website\layouts\TopNavigation.ascx.cs:47
System.Web.UI.WebControls.Repeater.CreateControlHierarchy(Boolean useDataSource) +692
System.Web.UI.WebControls.Repeater.OnDataBinding(EventArgs e) +67
Layouts.Topnavigation.TopnavigationSublayout.Page_Load(Object sender, EventArgs e) in c:\inetpub\wwwroot\Infotech\Website\layouts\TopNavigation.ascx.cs:26
System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +24
System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +41
System.Web.UI.Control.OnLoad(EventArgs e) +131
System.Web.UI.Control.LoadRecursive() +65
System.Web.UI.Control.LoadRecursive() +190
System.Web.UI.Control.LoadRecursive() +190
System.Web.UI.Control.LoadRecursive() +190
System.Web.UI.Control.LoadRecursive() +190
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2427
—————————-
My code is:
Home
—————————–
My code behind is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Sitecore.Data.Items;
using Sitecore.Links;
namespace Layouts.Topnavigation {
///
/// Summary description for TopnavigationSublayout
///
public partial class TopnavigationSublayout : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
//get the home item, this is hardcoded but you can define it in web.config
Item home = Sitecore.Context.Database.GetItem(“/sitecore/content/home”);
//get all children from home using Sitecore API
Item[] children = home.Children.ToArray();
//Bind the children to repeater
RepeaterTopNavigation.DataSource = children;
RepeaterTopNavigation.DataBind();
}
public void RepeaterTopNavigation_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
//gets the current data item from RepeaterItemEventsArgs and cast it as a Sitecore Item
Item currentItem = (Item)e.Item.DataItem;
//check if it is not null, safety first
if (currentItem != null)
{
//check if it is coming from Item or Alternating Item template
if (e.Item.ItemType == ListItemType.Item ||
e.Item.ItemType == ListItemType.AlternatingItem)
{
//Find the HyperLink control that has been defined in repeater
HyperLink topNavigation = (HyperLink)e.Item.FindControl(“HyperLinkTopNavigation”);
//Use Sitecore API to get the link to the Item and upadte the href property of link
topNavigation.NavigateUrl = LinkManager.GetItemUrl(currentItem);
//Assign name to the link
topNavigation.Text = currentItem.Name;
}
}
}
}
}
————
I would appreciate any help I could get.
thank you,
Perplexed
Sorry, for the late reply, I hope this has been resolved, most probably a typo in .ascx file for the control “HyperLinkTopNavigation”
So, according to my understanding, this method requires one DB call per one child item. Is it?
yes, but when Sitecore will cache the items and won’t call DB again and again.