SteGriff

Blog

Next & Previous

Umbraco 4 navigation menu with hidden items

Look, I don’t choose to work with Umbraco 4, but sometimes you’re maintaining a legacy system for a client and needs must.

A lot of older Umbraco systems just use XSLT for macros like this, but this project is heavy on .Net user controls and we use the old Umbraco NodeFactory API to get data.

I found it hard to make a menu which gave me umbracoNaviHide capabilities, so here are my finished snippets for your benefit:

First , a singleton for accessing the CurrentSitemapNode

private SiteMapNode _currentSitemapNode;
public SiteMapNode CurrentSitemapNode
{
    get
    {
        if (_currentSitemapNode == null)
        {
            _currentSitemapNode = SiteMap.Providers["UmbracoSiteMapProvider"].CurrentNode;
        }
        return _currentSitemapNode;
    }
}

A helper function for getting the value of a property

This fixes some oddities around umbraco yes/no fields

public T PropertyValue<T>(Node umbracoNode, string propertyName)
{
	//Get the umbraco property value as an object
	object originalValue = umbracoNode.GetProperty(propertyName).Value;

	//Use a special conversion if it is a bool
	if (typeof(T) == typeof(bool))
	{
		//Have to use .Equals here (not ==) because "1" is string and originalValue is object
		originalValue = (originalValue.Equals("1"));
	}

	//Otherwise use generic conversion to cast it to T
	return (T)Convert.ChangeType(originalValue, typeof(T));
}

The function which gets the actual navigation items

private SiteMapNodeCollection GetNavigationItems()
{
	//For the root of the navigation, you can either use the current parentNode:
	var parentNode = CurrentSitemapNode.ParentNode;
	
	//...Or get a particlar node by id:
	/*
	const string particularNodeId = "2001";
	var parentNode = SiteMap.Providers["UmbracoSiteMapProvider"].FindSiteMapNodeFromKey(particularNodeId);
	*/
	
	//If you don't care about umbracoNaviHide:
	/*
	return parentNode.ChildNodes;
	*/
	
	//If you do:
	SiteMapNodeCollection children = new SiteMapNodeCollection();
	foreach (SiteMapNode node in parentNode.ChildNodes)
	{
		//Get the umbraco node using the ID (key) of the SiteMapNode
		int nodeId = Int32.Parse(node.Key);
		var umbracoNode = new Node(nodeId);
		
		//Use the helper function to get boolean property value
		bool hidden = PropertyValue<bool>(umbracoNode, "umbracoNaviHide");
		if (!hidden)
		{
			children.Add(node);
		}
	}

	return children;
}

Now that you have a source of NavigationItems, you need to provide them to the front end. I use another singleton for this so that it doesn’t get recalculated in program code unnecessarily:

private SiteMapNodeCollection _navigationItems;
public SiteMapNodeCollection NavigationItems
{
	get
	{
		if (_navigationItems == null)
		{
			_navigationItems = GetNavigationItems();
		}
		return _navigationItems;
	}
}

How do you render the menu items?

I pass them into an asp:ListView in the frontend which uses the aforementioned singleton for a data source:

<ul class="account-sidenav">
	<asp:ListView runat="server" ID="NavigationList" ItemPlaceholderID="navItem" DataSource="<%# NavigationItems %>">
		<LayoutTemplate>
			<asp:placeholder runat="server" ID="navItem" />
		</LayoutTemplate>
		<ItemTemplate>
			<li>
				<a href="<%# ((SiteMapNode)Container.DataItem).Url %>">
					<%# ((SiteMapNode)Container.DataItem).Title %>
				</a>
			</li>
		</ItemTemplate>
	</asp:ListView>
</ul>

That’s enough to make it work. If you want to highlight the current page in the menu, you can tweak the inner link to this:

<a href="<%# ((SiteMapNode)Container.DataItem).Url %>" class="<%# AddActiveClass((SiteMapNode)Container.DataItem) %>">
	<%# ((SiteMapNode)Container.DataItem).Title %>
</a>

And add this function in the code behind:

public string AddActiveClass(SiteMapNode node)
{
    return CurrentSitemapNode.Title == node.Title
        ? "active"
        : "";
}

Hope this helps you!