Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]
.pdf
568 C H A P T E R 1 6 ■ W E B S I T E N AV I G AT I O N
Defining a Site Map
The starting point in site map–based navigation is the site map provider. ASP.NET ships with a single site map provider, named XmlSiteMapProvider, which is able to retrieve site map information from an XML file. If you want to retrieve a site map from another location or in a custom format, you’ll need to create your own site map provider—a topic covered in the section “Creating a Custom SiteMapProvider.”
The XmlSiteMapProvider looks for a file named Web.sitemap in the root of the virtual directory. Like all site map providers, its task is to extract the site map data and create the corresponding SiteMap object. This SiteMap object is then made available to other controls through the SiteMapDataSource.
To try this, you need to begin by creating a Web.sitemap file and defining the website structure using the <siteMap> and <siteMapNode> elements.
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0"> <siteMapNode>
<siteMapNode>...</siteMapNode> <siteMapNode>...</siteMapNode>
...
</siteMapNode>
</siteMap>
To be valid, your site map must begin with the root <siteMap> node, followed by a single <siteMapNode> element, representing the default home page. You can nest other <siteMapNode> elements in the root <siteMapNode> as many layers deep as you want. Each site map node should have a title, description, and URL, as shown here:
<siteMapNode title="Home" description="Home" url="~/default.aspx">
In this example, the URL uses the new ~/ syntax, which indicates the root of the web application. This style isn’t necessary, but it is strongly recommended, as it ensures that your site map links are interpreted correctly regardless of the current folder.
You can now use the <siteMapNode> to create a site map. The only other restriction is that you can’t create two site map nodes with the same URL.
■Note The restriction to avoid duplicate URLs is not baked into the navigation system. It’s simply required by the XmlSiteMapProvider, because the XmlSiteMapProvider uses the URL as a unique key. If you create your own site map provider or use a third-party provider, you may allow different URLs and require separate key information. However, you can’t get around the rule that every site must begin with one root node, because that’s implemented in the base SiteMapProvider class. (As you’ll see shortly, you still have options for tailoring the display of the site map tree, but you must start with a single home node.)
Here’s a sample site map:
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0"> <siteMapNode title="Home" description="Home" url="~/default.aspx">
<siteMapNode title="Products" description="Our products" url="~/Products.aspx">
<siteMapNode title="Hardware" description="Hardware choices" url="~/Hardware.aspx" />
<siteMapNode title="Software" description="Software choices" url="~/Software.aspx" />
</siteMapNode>
<siteMapNode title="Services" description="Services we offer"
C H A P T E R 1 6 ■ W E B S I T E N AV I G AT I O N |
569 |
url="~/Services.aspx">
<siteMapNode title="Training" description="Training classes" url="~/Training.aspx" />
<siteMapNode title="Consulting" description="Consulting services" url="~/Consulting.aspx" />
<siteMapNode title="Support" description="Support plans" url="~/Support.aspx" />
</siteMapNode>
</siteMapNode>
</siteMap>
■Tip In this example, the Products and Services nodes have URLs, which means they are clickable (and take the user to specific pages). However, if you simply want to use these nodes as categories to arrange other links, just omit the url attribute. You’ll still see the node in your bound controls; it just won’t be rendered as a link.
Binding to a Site Map
Once you’ve defined the Web.sitemap file, you’re ready to use it in a page. This is a great place to use master pages so that you can define the navigation controls as part of a template and reuse them with every page. Here’s how you might define a basic structure in your master page that puts navigation controls on the left:
<form id="form1" runat="server"> <table>
<tr>
<td style="width: 226px;vertical-align: top;"> <!-- Navigation controls go here. -->
</td>
<td style="vertical-align: top;">
<asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server" /> </td>
</tr>
</table>
<asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" /> </form>
Then, create a child with some simple static content:
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
<br /> <br />
Default.aspx page (home). </asp:Content>
The only remaining task is to choose the controls you want to use to display the site map data. One all-purpose solution is the TreeView control. You can add the TreeView and bind it to the SiteMapDataSource in the master page using the DataSourceID, as shown here:
<asp:TreeView ID="treeNav" runat="server" DataSourceID="SiteMapDataSource1" />
Alternatively, you could use the fly-out Menu control just as easily:
<asp:Menu ID="Menu1" runat="server" DataSourceID="SiteMapDataSource1" />
570 C H A P T E R 1 6 ■ W E B S I T E N AV I G AT I O N
Figure 16-9 shows both options.
Figure 16-9. TreeView and Menu navigation
You can do a lot more to customize the appearance of your navigation controls and the processing of your site map. You’ll consider these more advanced topics in the following sections.
Breadcrumbs
ASP.NET actually defines three navigation controls: the TreeView, Menu, and SiteMapPath. The SiteMapPath provides breadcrumb navigation, which means it shows the user’s current location and allows the user to navigate back up the hierarchy to a higher level using links. Figure 16-10 shows an example with a SiteMapPath control when the user is on the Software.aspx page. Using the SiteMapPath control, the user can return to the Products.aspx page or the Home.aspx page.
C H A P T E R 1 6 ■ W E B S I T E N AV I G AT I O N |
571 |
Figure 16-10. Breadcrumb navigation with SiteMapPath
Here’s how you define the SiteMapPath control:
<asp:SiteMapPath ID="SiteMapPath1" runat="server" />
The SiteMapPath control is useful both for an at-a-glance view that provides the current position and for a way to move up the hierarchy. However, you always need to combine it with other navigation controls that let the user move down the site map hierarchy.
The SiteMapPath control is also thoroughly customizable. Table 16-6 lists some of its most commonly configured properties.
Table 16-6. SiteMapPath Appearance-Related Properties
Property |
Description |
ShowToolTips |
Set this to false if you don’t want the description text to appear |
|
when the user hovers over a part of the site map path. |
ParentLevelsDisplayed |
Sets the maximum number of parent levels that will be shown at |
|
once. By default, this setting is -1, which means all levels will be |
|
shown. |
RenderCurrentNodeAsLink |
If true, the portion of the page that indicates the current page is |
|
turned into a clickable link. By default, this is false because the |
|
user is already at the current page. |
PathDirection |
You have two choices: RootToCurrent (the default) and |
|
CurrentToRoot (which reverses the order of levels in the path). |
PathSeparator |
Indicates the characters that will be placed between each level in |
|
the path. The default is the greater-than (>) symbol. Another |
|
common path separator is the colon (:). |
|
|
For even more control, you can configure the SiteMapPath control with styles or even redefine the controls and HTML with templates (see Table 16-7).
572 C H A P T E R 1 6 ■ W E B S I T E N AV I G AT I O N
Table 16-7. SiteMapPath Styles and Templates
Style |
Template |
Applies To |
NodeStyle |
NodeTemplate |
All parts of the path except the root and |
|
|
current node. |
CurrentNodeStyle |
CurrentNodeTemplate |
The node representing the current page. |
RootNodeStyle |
RootNodeTemplate |
The node representing the root. If the root |
|
|
node is the same as the current node, the |
|
|
current node template or styles are used. |
PathSeparatorStyle |
PathSeparatorTemplate |
The separator between each node. |
|
|
|
For example, the following SiteMapPath uses an arrow image as a separator and a fixed string of bold text for the root node. The final part of the path, which represents the current page, is italicized.
<asp:SiteMapPath ID="SiteMapPath1" runat="server"> <PathSeparatorTemplate>
<asp:Image ID="Image1" ImageUrl="~/images/arrow.jpg" runat="server" GenerateEmptyAlternateText="True" />
</PathSeparatorTemplate>
<RootNodeTemplate>
<b>Root</b>
</RootNodeTemplate>
<CurrentNodeTemplate>
<i><asp:Label ID="Label1" runat="server" Text='<%# Eval("title") %>'> </asp:Label></i>
</CurrentNodeTemplate>
</asp:SiteMapPath>
Notice how the CurrentNodeTemplate uses a data binding expression to bind to the title property of the current node. You can also get the url and description attributes that you declared in the site map file in the same way.
Binding Portions of a SiteMap
In the examples so far, the page controls replicate the structure of the site map file exactly. However, this isn’t always what you want. For example, you might not like the way the Home node sticks out because of the XmlSiteMapProvider rule that every site map must begin with a single root. To clean this up, you can set the SiteMapDataSource.ShowStartingNode property to false. Then, modify the site map file so it defines the Home entry in the first group of pages. (The root node won’t be shown, so you can use a dummy node with any URL for it.)
Here’s the revised site map:
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0"> <siteMapNode title="Root" description="Root" url="~/">
<siteMapNode title="Home" description="Home" url="~/default.aspx"/> <siteMapNode title="Products" description="Our products"
url="~/Products.aspx">
...
</siteMapNode>
</siteMap>
Figure 16-11 shows the nicer result.
C H A P T E R 1 6 ■ W E B S I T E N AV I G AT I O N |
573 |
Figure 16-11. A site map without the root node
This example shows how you can skip the root node. Another option you have is to show just a portion of the complete site map, starting from the current node. For example, you might use a control such as the TreeView to show everything in the hierarchy starting from the current node. If the user wants to move up a level, they could use another control (such as a SiteMapPath).
To implement this design, simply set the SiteMapDataSource.StartFromCurrentNode property to true. The SiteMapPath will still show the complete hierarchy, allowing the user to move up to a higher-level page. However, other bound controls will show only pages beneath the current page, allowing the user to move down the hierarchy. You still have the choice of whether to use ShowStartingNode, but now it determines whether you show the current node (because that’s the starting point for the navigation tree).
Figure 16-12 shows an example where both StartFromCurrentNode and ShowStartingNode are true. The current page is Software.aspx. The SiteMapPath shows higher-level pages, and the TreeView shows the nodes underneath the Software.aspx node.
Figure 16-12. Binding to child nodes only
For this technique to work, ASP.NET must be able to find a page in the Web.sitemap file that matches the current URL. Otherwise, it won’t know where the current position is, and it won’t provide any navigation information to the bound controls.
576 C H A P T E R 1 6 ■ W E B S I T E N AV I G AT I O N
Table 16-8. SiteMapNode Navigational Properties
Property |
Description |
ParentNode |
Returns the node one level up in the navigation hierarchy, which contains the |
|
current node. On the root node, this returns a null reference. |
ChildNodes |
Provides a collection of all the child nodes. Check the HasChildNodes property |
|
to determine if there are child nodes. |
PreviousSibling |
Returns the previous node that’s at the same level (or a null reference if no |
|
such node exists). |
NextSibling |
Returns the next node that’s at the same level (or a null reference if no such |
|
node exists). |
|
|
To see this in action, consider the following code, which configures two labels on a page to show the heading and description information retrieved from the current node:
protected void Page_Load(object sender, EventArgs e)
{
lblHead.Text = SiteMap.CurrentNode.Title; lblDescription.Text = SiteMap.CurrentNode.Description;
}
The next example is a little more ambitious. It implements the previous/next links, which allow the user to traverse an entire set of subnodes. The code checks for the existence of sibling nodes, and if there aren’t any in the required position, it simply hides the links.
protected void Page_Load(object sender, EventArgs e)
{
if (SiteMap.CurrentNode.NextSibling != null)
{
lnkNext.NavigateUrl = SiteMap.CurrentNode.NextSibling.Url; lnkNext.Visible = true;
}
else
{
lnkNext.Visible = false;
}
}
Binding Other Controls
The TreeView and MenuView are two navigation controls that show hierarchical navigation information. (Both the TreeView and MenuView are described in more detail later in this chapter.) However, you aren’t limited to these two controls—you can also use any ASP.NET control that supports data binding, from the ListBox to the GridView.
For example, you can bind the navigation information to a template in a rich data control and use data binding expression to extract the title, description, and URL information. Here’s an example with a GridView:
<asp:GridView ID="listNavLinks" runat="server" DataSourceID="SiteMapDataSource1" AutoGenerateColumns="false" ShowHeader="False" BackColor="Linen" CellPadding="5"> <Columns>
<asp:TemplateField>
<ItemTemplate>
<a href='<%# Eval("Url") %>'><%# Eval("Title") %></a> <br />
C H A P T E R 1 6 ■ W E B S I T E N AV I G AT I O N |
577 |
<%# Eval("Description") %> </ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Figure 16-13 shows the result.
Figure 16-13. Showing navigation links in a GridView template
The only limitation in this example is that it shows links nested underneath the current page. It doesn’t provide links to travel back up. You would need to add other controls to provide this functionality. You can use the SiteMapPath control along with the GridView, or you can use the SiteMap API. For example, you can use a LinkButton that, when clicked, runs this code to go up one level in the hierarchy:
protected void cmdUp_Click(object sender, EventArgs e)
{
Response.Redirect(SiteMap.CurrentNode.ParentNode.Url);
}
Unfortunately, you have no way to bind to nodes further down the hierarchy, as the SiteMapDataSource control doesn’t support the XPath syntax demonstrated in Chapter 12. However, you can embed a nested control and bind it programmatically, using the same technique that’s described in Chapter 10 in the “A Parent/Child View in a Single Table” section.
Adding Custom Site Map Information
In the site maps you’ve seen so far, the only information that’s provided for a node is the title, description, and URL. This is the bare minimum of information that you’ll want to use. However, the schema for the XML site map is open, which means you’re free to insert custom attributes with your own data.
You might want to insert additional node data for a number of reasons. This additional information might be descriptive information that you intend to display or contextual information that describes how the link should work. For example, you could add attributes that specify a target frame or indicate that a link should be opened in a pop-up window. The only catch is that it’s up to you to act on the information later. In other words, you need to configure your user interface so it uses this extra information.
