A SiteMap enables you to describe the navigational structure of a website
separate from how the URLs of your website are exposed through controllers and
controller actions. A SiteMap enables you to describe how pages in an ASP.NET
MVC application are related for purposes of navigation.
You can use a SiteMap, in combination with the SiteMap API, to generate the
navigational links for your website. For example, you can use SiteMaps to
generate menus, tabs, tree views, previous and next links, and breadcrumb
trails. In this tutorial, I demonstrate how to create a simple Menu() HTML
helper that generates menu links from a SiteMap automatically.
In an ASP.NET Web Forms application, you can use a feature of
SiteMaps called security trimming to display only those navigational links that
a user is authorized to view. For example, when security trimming is enabled,
you can hide an Admin link from any users who are not authorized to access the
Admin page. Security trimming, by default, is not supported within an ASP.NET
MVC application.
Creating a SiteMap
The easiest way to create a SiteMap is to create an XML file that describes
the navigational structure of your website. You can create a new SiteMap in
Visual Studio by selecting the menu option Project, Add New Item, and add a
SiteMap file (see Figure 1). In order for your SiteMap file to work without
additional configuration, you should name your new SiteMap file Web.SiteMap and
you should locate the SiteMap file in the root of your application.
Figure 1 – Adding a new XML SiteMap file

An XML SiteMap file contains a root <siteMap> element that contains one
or more <siteMapNode> elements. You can nest one <siteMapNode>
element within another <siteMapNode> element. You use these
<siteMapNode> elements to describe the relationship between the pages in
your ASP.NET MVC application.
Each <siteMapNode> element can have the following attributes (this is
not a complete list):
- url – The URL of the page. Used when generating the URL for a navigation
link.
- title – The title of the page. Used when generating the title for a
navigation link.
- description – The description of the page. Used when generating a
description associated with a navigation link.
- resourceKey – A resource key that you can use to localize the siteMapNode
to multiple languages
- siteMapFile – The path to another SiteMap file. Used when dividing a
SiteMap into multiple SiteMap files.
XML documents are case-sensitive. So, there is a difference
between <siteMapNode> and <SiteMapNode>.
The SiteMap file in Listing 1 contains four <siteMapNode> elements. It
describes a website that contains a Home page and four top-level pages entitled
Products, Services, and About.
Listing 1 – Web.sitemap
<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode
url="~/"
title="Home"
description="The Home Page">
<siteMapNode
url="~/Product/Index"
title="Products"
description="Our Products" />
<siteMapNode
url="~/Service/Index"
title="Services"
description="Our Services" />
<siteMapNode
url="~/Home/About"
title="About"
description="About Us" />
</siteMapNode>
</siteMap>
Understanding the SiteMap API
You interact with a SiteMap through the SiteMap API. The SiteMap API is
represented by the static SiteMap class. Because the SiteMap class is static,
you can access the class from anywhere within an ASP.NET MVC application without
doing anything special (you can access the class directly within controllers,
views, helpers, model classes, and so on).
The static SiteMap class has two important properties:
- CurrentNode – Returns the SiteMapNode that corresponds to the user’s
current location in the website.
- RootNode – Returns the root SiteMapNode.
You use the SiteMap class to determine where you are within a SiteMap. You
use the properties and methods of the SiteMapNode class to generate navigational
links. Here are some of the more interesting properties of the SiteMapNode
class:
- ChildNodes – Returns all of the child SiteMapNodes.
- Description – Returns the description of a SiteMapNode.
- NextSibling – Returns the next sibling SiteMapNode.
- ParentNode – Returns the parent SiteMapNode.
- PreviousSibling – Returns the previous sibling SiteMapNode.
- Title – Returns the title of a SiteMapNode.
- Url – Returns the URL of a SiteMapNode.
Creating a Menu HTML Helper
You can take advantage of SiteMaps, and the SiteMap API, to create HTML
Helpers that generate navigational links automatically. For example, the HTML
Helper in Listing 2 generates a menu from a SiteMap.
Listing 2 – Helpers\MenuHelper.cs
using System.Text;
using System.Web;
using System.Web.Mvc;
namespace MvcApplication1.Helpers
{
public static class MenuHelper
{
public static string Menu(this HtmlHelper helper)
{
var sb = new StringBuilder();
// Create opening unordered list tag
sb.Append("<ul class='menu'>");
// Render each top level node
var topLevelNodes = SiteMap.RootNode.ChildNodes;
foreach (SiteMapNode node in topLevelNodes)
{
if (SiteMap.CurrentNode == node)
sb.AppendLine("<li class='selectedMenuItem'>");
else
sb.AppendLine("<li>");
sb.AppendFormat("<a href='{0}'>{1}</a>", node.Url, helper.Encode(node.Title));
sb.AppendLine("</li>");
}
// Close unordered list tag
sb.Append("</ul>");
return sb.ToString();
}
}
}
Listing 2 contains an extension method named Menu() that extends the
HtmlHelper class. This method grabs all of the child nodes of the root node in
the SiteMap and renders a list of links. The links are rendered in an HTML
unordered list <ul> tag.
The SiteMap.CurrentNode property is used to determine whether a link being
rendered corresponds to the current location of the user. The current node is
marked with the Cascading Style Sheet selectedMenuItem class.
You can use the Menu() HTML helper in a particular view. However, it makes
more sense to call the Menu() helper method within a View Master Page. That way,
the menu will appear in all of the views in your application.
The View Master Page in Listing 3 illustrates how you can use the Menu()
helper method.
Listing 3 – Site.master
<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.Master.cs" Inherits="MvcApplication1.Views.Shared.Site" %>
<%@ Import Namespace="MvcApplication1.Helpers" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
<style type="text/css">
.menu
{
list-style:none;
padding:0px;
margin:0px;
}
.menu li
{
float:left;
}
.menu a
{
display:block;
background-color:#eeeeee;
color:Black;
font-weight:bold;
padding:4px;
border:solid 1px black;
text-decoration:none;
margin:2px;
}
.selectedMenuItem a
{
background-color: White;
}
</style>
</head>
<body>
<div>
<%= Html.Menu() %>
<br style="clear:both" />
<asp:ContentPlaceHolder
ID="ContentPlaceHolder1"
runat="server" />
</div>
</body>
</html>
Notice that the namespace MvcApplication1.Helpers is imported at the
top of the View Master Page. You must import the namespace of the extension
method in order for the extension method to appear as a method of the Html
property.
Notice that the View Master Page contains a Cascading Style Sheet that is
used to style the menu links rendered by the Menu() helper. This style sheet is
used to format the unordered list and links to look like a tab strip (see Figure
2).
When you click a menu link, the selected menu link is highlighted with the
style defined by the selectedMenuItem CSS class. In the case of Listing 3, the
selected menu item is displayed with a white background color and unselected
menu items are displayed with a gray background color.
Figure 2 – The rendered Menu helper

The Importance of Canonical URLs
In an ASP.NET MVC application, multiple URLs can be mapped to the same
controller action. For example, by default, you can invoke the Index() action on
the Home controller by requesting any of the following URLs:
http://www.MySite.com/
http://www.MySite.com /Home
http://www.MySite.com /Home/Index
The default routes set up in the Global.asax file map all three of these URLs
to the same controller action. Furthermore, you can invoke the Index action
exposed by the Product controller by requesting either of the following
URLs:
http://www.MySite.com/Product/
http://www.MySite.com/Product/Index
This feature of the ASP.NET MVC framework causes problems when you use
SiteMaps. When you create a standard XML SiteMap file, you can associate each
SiteMapNode with only one URL. For example, you can associate only one URL with
the Index action of the Home controller. Alternative URLs for the same action
won’t match the correct SiteMapNode. So what do you do?
One option is to modify the routes defined in the Global.asax file so that
multiple URLs cannot be mapped to the same controller action. For
example, instead of defining the Default route like this:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
You can replace the Default route definition with the following two route
definitions like this:
routes.MapRoute(
"Home",
"",
new { controller = "Home", action = "Index", id = "" }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { id = "" }
);
The first route definition maps the URL http://www.MySite.com/ to the Index
action of the Home controller. The second route definition maps a URL such as
http://www.MySite.com/Product/Index to the Index action of the Product
controller.
These modified routes won’t match a URL like http://www.MySite.com/Home/Index
or http://www.MySite.com/Product. The modified routes map one and only one URL
to a controller action. In other words, you are forced to use canonical URLs for
all of your controller actions.
Another solution to the problem of canonical URLs is to take advantage of the
Internet Information Services 7.0 URL Rewrite Module. Learn more at
http://learn.iis.net/page.aspx/460/using-url-rewrite-module/
Summary
In this tutorial, you learned how to use SiteMaps to describe the
navigational structure of your ASP.NET MVC websites. You learned how to create
new XML SiteMap files and interact with the SiteMap API. Finally, you learned
how to create a custom Menu() HTML helper that generates website menu links from
a SiteMap automatically.