The importance of good navigation on a site cannot be underestimated. It doesn't matter how great your site looks or how well it was developedif it's hard to navigate, users won't like using it. It's easy to see how seriously navigation is taken just by looking at the number of menu controls that have been written since ASP.NET 1.0 was releasedthere are now controls that use tree views, vertical expansion, horizontal layouts, flashy graphics, and so on. Providing a good menu isn't the end of site navigation because it's important to ensure visitors know where they are within the site hierarchy. Too often we see sites with pages three or four levels deep within the menu structure, but when we navigate to those pages there's no indication of where we are. We are left wondering how to navigate back up the structure; at worst, we have to go back to the home page to navigate down again. Site MapsThere are plenty of ways to implement navigation on a site, but none that are an intrinsic part of ASP.NET 1.x. With ASP.NET 2.0, there are controls and configuration files for providing a set way to define site structure and techniques for displaying the navigation information and extracting the current navigation path . Like the rest of ASP.NET, the architecture for navigation has been broken down into logical parts , allowing customization. First, there is a configurable provider supplying the site map information, and then a set of controls that can take advantage of the data supplied by the provider. The provider not only exposes the site structure to other controls but also keeps track of the current navigation, allowing pages to identify where in the hierarchy they are. The entire structure and the current details can be exposed to users by binding controls to the provider. This pluggable architecture means that data defining the structure of a site can come from any data sourcethe site map provider is the link between the data and the navigation within a site. Site Map ProvidersA site map provider is a data provider that exposes the site structure by way of a set interface. Site maps are pluggable within the application configuration file, within the system.web section. The syntax for this section is shown in Listing 5.9. Listing 5.9 Site Map Configuration Syntax<siteMap defaultProvider=" string " enabled="[truefalse]"> <providers> <add name=" string " description=" string " provider-specific-configuration /> <remove name=" string " /> <clear> </providers> </siteMap> The attributes for the siteMap element are shown in Table 5.1. The attributes for the providers element are shown in Table 5.2. Table 5.1. siteMap Configuration
Table 5.2. siteMap providers Configuration
Table 5.3. XmlSiteMapProvider-Specific Attribute
With the Technology Preview of ASP.NET 2.0, the only provider is the XmlSiteMapProvider (in System.Web ), allowing site navigation structure to be stored in an XML file. For a full description of the type attribute, see the machine.config file. The XmlSiteMapProvider has one provider-specific attribute, as shown in Table 5.3. The pluggable architecture makes it extremely easy to add support for additional methods of site map storage. For example, you could write a FrontPage site map provider to read the site structure from the format used by Microsoft FrontPage, or perhaps one to build the structure from the file system, directly reading the names of the files and directories. To write your own site map provider you need to implement the ISiteMapProvider interface. A discussion of this is outside the scope of the book, but details of the interface can be found in the documentation. Site Map Configuration FilesThe XmlSiteMapProvider defines a set schema for the app.SiteMap file, as shown in Listing 5.10. Listing 5.10 XmlSiteMapProvider Schema<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <xs:element name="siteMap"> <xs:complexType> <xs:sequence> <xs:element ref="siteMapNode" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="siteMapNode"> <xs:complexType> <xs:sequence> <xs:element ref="siteMapNode" minOccurs="0" MaxOccurs="unbounded"/> </xs:sequence> <xs:attribute name="url" type="xs:string"/> <xs:attribute name="title" type="xs:string"/> <xs:attribute name="description" type="xs:string"/> <xs:attribute name="keywords" type="xs:string"/> <xs:attribute name="roles" type="xs:string"/> <xs:attribute name="SiteMapFile" type="xs:string"/> <xs:attribute name="Provider" type="xs:string"/> </xs:complexType> </xs:element> </xs:schema> This defines a structure consisting of a root siteMap element, with the site structure being contained by siteMapNode elements. There has to be one top-level siteMapNode element, and within that can be any number of siteMapNode elements of any depth. The attributes for the siteMapNode element are shown in Table 5.4. The use of SiteMapFile allows the site map information to be split among different sources. This is especially useful when different divisions supply sections of a corporate siteeach part of the site map can be authored independently and even stored in different providers. Listing 5.11 shows a sample site map file. To create one within Visual Studio .NET "Whidbey," you simply create a new XML file and call it app.SiteMap there isn't a template for this. Table 5.4. siteMapNode Attributes
Listing 5.11 Sample app.SiteMap File<siteMap> <siteMapNode title="Home" description="Home" url="SiteMaps.aspx?id=1"> <siteMapNode title="Sales" description="The Sales Site" url="SiteMaps.aspx?id=2"> <siteMapNode title="Customers" url="SiteMaps.aspx?id=3"/> <siteMapNode title="Products" url="SiteMaps.aspx?id=4/> <siteMapNode title="Region" url="SiteMaps.aspx?id=5"/> <siteMapNode title="Futures" url="SiteMaps.aspx?id=6"/> </siteMapNode> <siteMapNode title="Research" description="The Research Site" url="SiteMaps.aspx?id=7"> <siteMapNode title="Widgets" url="SiteMaps.aspx?id=8"/> <siteMapNode title="Doodads" url="SiteMaps.aspx?id=9"/> <siteMapNode title="Thingies" url="SiteMaps.aspx?id=10" /> </siteMapNode> </siteMapNode> </siteMap> This provides the following structure for the site: Home Sales Customers Products Region Futures Research Widgets Doodads Thingies Using a Site Map FileOnce the structure of your site is defined in the site map file, you then need a way to make use of it. For this you use a SiteMapDataSource control, which provides data access to the site map data, and then a control to display that data. From within Visual Studio .NET "Whidbey," you can just drag a SiteMapDataSource control onto the design surfacethere's no need to set any properties because it defaults to using app.SiteMap as its data source. You can then drag a TreeView control onto the page and set its DataSourceId property to the id of the SiteMapDataSource control. Figure 5.11, for example, shows how our Big Corp site could be constructed using a single menu. Figure 5.11. A TreeView bound to a SiteMapDataSource
Other controls can be bound to site map data, but in the Technology Preview release, the TreeView provides the best option because of its hierarchical display. It's possible that a dedicated menu control will appear in future versions. Site Maps in DepthAt its simplest, the use of site maps needs nothing more than has been discussed above, but there's actually more to them. Adding a SiteMapData Source control to a page provides all that's needed for site map handling, but there are properties that allow for more control over how the data is supplied from the SiteMapDataSource to controls. For example, the syntax of the SiteMapDataSource control is shown in Listing 5.12. Listing 5.12 SiteMapDataSource Syntax<asp:SiteMapDataSource id=" String " runat="server" FlatDepth=" Integer " SiteMapProvider=" String " SiteMapViewType="[FlatPathTree]" StartingDepth=" Integer " StartingNodeType="[CurrentParentRoot]" StartingNodeUrl=" String " /> The attributes are shown in Table 5.5. Table 5.5. SiteMapDataSource Attributes
The effects of some of these properties are not immediately apparent and depend on which control you bind to the data source and where you are in the navigation hierarchy. Probably the most useful control is the TreeView , which naturally displays hierarchical data, but the ListBox is also good for displaying site map data in a flat view. A good way to see the effects of these properties is to build a grid with three SiteMapDataSource controls, each set to a different SiteMapViewType . Then you can bind a TreeView and a ListBox to each type view of the site map, as shown in Listing 5.13. Listing 5.13 Sample Site Map Displays<asp:SiteMapDataSource id="SiteDataFlat" runat="server" SiteMapViewType="Flat" /> <asp:SiteMapDataSource id="SiteDataPath" runat="server" SiteMapViewType="Path" /> <asp:SiteMapDataSource id="SiteDataTree" runat="server" SiteMapViewType="Tree" /> <table border="1" width="50%"> <tr> <td>Flat</td> <td>Path</td> <td>Tree</td> </tr> <tr> <td> <asp:TreeView runat="server" DataSourceId="SiteDataFlat" /> </td> <td> <asp:TreeView runat="server" DataSourceId="SiteDataPath" /> </td> <td> <asp:TreeView runat="server" DataSourceId="SiteDataTree" /> </td> </tr> <tr> <td> <asp:ListBox runat="server" DataSourceId="SiteDataFlat" /> </td> <td> <asp:ListBox runat="server" DataSourceId="SiteDataPath" /> </td> <td> <asp:ListBox runat="server" DataSourceId="SiteDataTree" /> </td> </tr> </table> The initial display is shown in Figure 5.12. By default the TreeView binds the title attribute of the site map to the Text property and the url attribute to the NavigateUrl property, giving you an instant menu control. For the ListBox the title attribute is bound to both the DataTextField and DataValueField properties. Figure 5.12. Initial site map display
You can see from this that when in Tree mode, the TreeView displays as you expect it to. However, the Flat view shows how all nodes (at whatever level) are shown. Nodes with children are expandable in the normal TreeView style. For the Path mode, nothing is shown because we haven't yet performed any navigation. For the ListBox control, the Tree mode shows only the first node because it is a naturally flat control and can deal only with a single level of the hierarchy. However, in Flat mode you see all nodes because they have been flattened and therefore appear at the top level. The results of navigating to the Sales Region page are shown in Figure 5.13. Figure 5.13. Navigating to a page
Here you can see that the Tree and Flat views are essentially the same as their initial settings, and the Path view has now been filled. In the Path view column, note that the TreeView contains the same data as the Tree mode, but the ListBox shows only those nodes in the path between the root node and the selected node. Flattening NodesSetting the FlatDepth property limits the depth of the nodes that are flattened. For example, on the left in Figure 5.14 you see a FlatDepth of 1 , so only one node is flattened. On the right a FlatDepth of 2 causes three nodes to be flattenedthe top node, plus its two child nodes. Figure 5.14. Results of setting different FlatDepth properties
Setting the Starting DepthThe StartingDepth property indicates at which node level the data is displayed from, and it affects all three modes ( Flat , Path , and Tree ). For example, setting the StartingDepth to 1 (where no FlatDepth is set) is shown in Figure 5.15. Figure 5.15. Results of setting the StartingDepth property to 1
Here you can see that only nodes from level 1 down are shown and only those from our navigation pointremember, the SiteMapData Source keeps track of where we are in the navigational structure. Setting the Starting Node TypeThe StartingNodeType property identifies what type of node to start displaying data from. For example, setting this property to Parent would give the results in Figure 5.16. We've navigated to the Region node, a node that is underneath Sales . In the ListBox , for the Flat view we see only the Parent of the current node, plus its children; for the Path view, we see only the current node and its parent; and for the Tree view, we see only the parent. Figure 5.16. Results of setting the StartingNodeType property to Parent
Setting the StartingNodeType to Current means that only the current node is displayed, as shown in Figure 5.17. Setting the CurrentNodeType to Root means that the current node becomes the root node as far as displaying the node hierarchy is concerned . Figure 5.17. Results of setting the StartingNodeType property to Current
Setting the Start Node URLThe StartingNodeUrl property allows us to set the starting point, given the URL of a page. Since URLs in the site map file must be unique, this allows us to navigate to a given node knowing only the URL, rather than its location in the hierarchy. Showing a Navigation PathWhen a site map provides the navigational architecture for a site, it's easy to add features that take advantage of this. With a hierarchy three deep or more, it has always been hard for users to remember where they are within that structure, so the idea of breadcrumbs came about, laying a trail of the path back to the root of the site. With ASP.NET 2.0 this is simple: We have the SiteMapPath control, which automatically hooks into the site map architecture, so all you have to do is drop it on a page, as shown in Figure 5.18. Figure 5.18. The SiteMapPath control
This figure shows the default implementation, just from adding the following line of code to our page: <asp:SiteMapPath runat="server" /> To use the SiteMapPath control you don't need a SiteMapDataSource control because it works directly with the site map provider. The current node is shown as simple text, and parent nodes are shown as hyperlinks , allowing quick navigation up the tree. The text for the tooltip is set to the description attribute from the site map file. There are plenty of ways to customize this control to fit it to your site. The syntax is shown in Listing 5.14. Listing 5.14 SiteMapPath Syntax<SiteMapPath id=" String " runat="server" CurrentNodeStyle=" Style " CurrentNodeTemplate=" Template " HoverNodeStyle=" Style " NodeStyle=" Style " NodeTemplate=" Template " ParentLevelsDisplayed=" Integer " PathDirection="[CurrentToRootRootToCurrent]" PathSeparator=" String " PathSeparatorStyle=" Style " PathSeparatorTemplate=" Template " RenderCurrentNodeAsLink=" Boolean " RootNodeStyle=" Style " RootNodeTemplate=" Template " ShowToolTips=" Boolean " SiteMapProvider=" String " /> These are just the unique properties for this control, described in Table 5.6. All other properties are inherited and are described in the documentation. Table 5.6. SiteMapPath Properties
These properties give a great deal of flexibility in how the navigation path is shown. For example, consider the code shown in Listing 5.15. Listing 5.15 Setting the SiteMapPath Properties<asp:SiteMapPath ID="SiteMapPath1" runat="server" NodeStyle-Font-Name="Franklin Gothic Medium" NodeStyle-Font-Underline="true" NodeStyle-Font-Bold="true" RootNodeStyle-Font-Name="Symbol" RootNodeStyle-Font-Bold="false" CurrentNodeStyle-Font-Name="Verdana" CurrentNodeStyle-Font-Size="10pt" CurrentNodeStyle-Font-Bold="true" CurrentNodeStyle-ForeColor="red" CurrentNodeStyle-Font-Underline="false"> <PathSeparatorTemplate> <asp:Image runat="server" ImageUrl="arrow.gif"/> </PathSeparatorTemplate> </asp:SiteMapPath> This defines styles for the nodes and a separator that uses a custom image. The results are shown in Figure 5.19. Figure 5.19. A customized SiteMapPath control
Notice that the root node is underlined even though it wasn't specified as part of the RootNodeStyle the underlining was inherited from the NodeStyle . SiteMapPath EventsThe SiteMapPath is built dynamically from the data held by the underlying site map provider. As the tree of nodes is traversed, each item in the path, from the root node to the current node, is added to the Controls collection of the SiteMapPath control. Like other collection controls (such as the DataList or DataGrid ), two events are fired when items are either created ( ItemCreated ) or bound ( ItemDataBound ) to the SiteMapPath . The signature for these events is the same: Sub eventName (Sender As Object, E As SiteMapNodeItemEventArgs) SiteMapNodeItemEventArgs has one property, Item , which returns an object of type SiteMapNodeItem , which in turn has three properties, as described in Table 5.7. Table 5.7. SiteMapNodeItem Properties
Intercepting the ItemCreated and ItemDataBound events gives you a chance to change the default behavior as the items are created. For example, Listing 5.16 shows how you could build up an HTML meta tag consisting of the Keywords from the site map details. If the SiteMapPath control were embedded into the master page, this meta tag would be automatically constructed for each page. Listing 5.16 SiteMapPath ItemCreated Event<%@ Page %> <head runat="server" id="PageHead" /> <script runat="server"> Sub ItemCreated(Sender As Object, E As SiteMapNodeItemEventArgs) If E.Item.ItemType = _ SiteMapNodeItemType.Current Then Dim sb As New StringBuilder() Dim s As String For Each s In E.Item.SiteMapNode.Keywords sb.Append(s) sb.Append(" ") Next Dim ctl As New HtmlGenericControl("meta") ctl.Attributes.Add("name", "keywords") ctl.Attributes.Add("content", sb.ToString()) PageHead.Controls.Add(ctl) End If End Sub </script> <form runat="server"> <asp:SiteMapPath runat="server" onItemCreated="ItemCreated"/> </form> The SiteMapNode ObjectWhen the site map is constructed from the data provider, each of the items is built into a SiteMapNode object. These in turn are added to a SiteMapNodeCollection , which therefore represents all pages within a Web site. The SiteMapNode object provides links to nodes up, down, and next to it in the hierarchy and thus can be used to build a treelike structure. As shown in Listing 5.16, the ItemCreated event of the SiteMapPath object allows access to the SiteMapNode , which has the properties detailed in Table 5.8. Table 5.8. SiteMapNode Properties
There are three methods for the SiteMapNode object, as described in Table 5.9. Table 5.9. SiteMapNode Methods
Accessing the Site Map at RuntimeSo far we've seen the site map be used by controls, but it can also be accessed directly because it is exposed through a static page property called SiteMap . For example, to access the current node within the site map, you can use the following code: Dim currNode As SiteMapNode currNode = SiteMap.CurrentNode This means that even if you aren't using a SiteMapPath control, you can easily build links pointing back to the hierarchy, as shown in Listing 5.17. Listing 5.17 Using the SiteMap Property of the Page<script runat="server"> Sub Page_Load(Sender As Object, E As EventArgs) ParentLink.NavigateUrl = SiteMap.CurrentNode.ParentNode.Url End Sub </script> <form runat="server"> <asp:HyperLink id="ParentLink" Text="Go Back" /> </form> Table 5.10 details the properties of the SiteMap class. Table 5.10. SiteMap Class Properties
These properties give you access to the site map details and allow you to interface into it at the programmatic level, in case more flexibility is required than the standard server controls provide. |