My objective is to render the full web.sitemap as a nested unordered list. By full, I mean to produce the entire sitemap from root to all descendants. This list must contain the title and url of each node.
This process is useful when your website is a Windows Authenticated network and you need to create a navigation list with secuirty trimming by Active Directory role.
Context -- This is an asp.net 4.0 webpage using Visual Studio 2010. I use strict = true and explicit = true.
Background & What I've done so far
This page https://msdn.microsoft.com/en-us/library/system.web.sitemap(v=vs.110).aspx shows an example of how to generate a sitemap based on "current node," but I want to generate a full sitemap from the root to all children.
The URL gave me the inspiration to set up a recursive function, but my problem is while I can get the node titles I am unable to get the node URL to accompany it.
Update (5/7/2015): I learned that I was missing a critical piece of code necessary to get the URL of the "current" node. The ordered list emphasizes the new additions, and I'm posting my refreshed code.
I have discovered that while I can get two levels deep from the root node, I am not going any further, which leads me to believe that the recursion script is not defined correctly.
*This goes in the LoadMe sub *
Dim node As SiteMapNode-- this variable is defined before the while loop and represents the "current" node in the while loop.node = CType(rootNodesChildrenEnumerator.Current, SiteMapNode)-- the node value is updated each pass through the while loopnode.Url-- this is how to read the URL of the "current" node
*This goes in the List_Childnodes function *
Dim node As SiteMapNodenode = CType(childNodesEnumerator.Current, SiteMapNode)sb.Append(childNodesEnumerator.Current.ToString())
Here is the full (updated) code.
Thank you for any ideas you can provide in improving the recursion and ideas to make the code syntax better.
<%@ Page Title="Sitemap Test" Language="VB" MasterPageFile="~/MasterPage.master" Strict="true" Explicit="true" %>
<script runat="server">
Private Sub LoadMe(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim sb As New StringBuilder
' Examine the RootNode, and navigate the SiteMap relative to it.
sb.Append("<ul>")
sb.Append("<li><a href=""" & Page.ResolveClientUrl(SiteMap.RootNode.Url) & """>" & SiteMap.RootNode.Title & "</a></li>")
' What nodes are children of the RootNode?
If (SiteMap.RootNode.HasChildNodes) Then
Dim rootNodesChildrenEnumerator As IEnumerator = SiteMap.RootNode.ChildNodes.GetEnumerator()
Dim node As SiteMapNode
While (rootNodesChildrenEnumerator.MoveNext())
node = CType(rootNodesChildrenEnumerator.Current, SiteMapNode)
sb.Append("<li><a href=""" & node.Url & """>" & rootNodesChildrenEnumerator.Current.ToString() & "</a></li>")
sb.Append(List_Childnodes(CType(rootNodesChildrenEnumerator.Current, SiteMapNode)))
End While
End If
sb.Append("</ul>")
lblSitemap.Text = sb.ToString
End Sub
Function List_Childnodes(Current_Node As SiteMapNode) As String
Dim sb As New StringBuilder
' What nodes are children of the function parameter?
If (Current_Node.HasChildNodes) Then
Dim childNodesEnumerator As IEnumerator = Current_Node.ChildNodes.GetEnumerator()
sb.Append("<ul>")
Dim node As SiteMapNode
While (childNodesEnumerator.MoveNext())
' Prints the Title of each node.
node = CType(childNodesEnumerator.Current, SiteMapNode)
sb.Append("<li>")
sb.Append("<a href=""" & node.Url & """>")
sb.Append(childNodesEnumerator.Current.ToString())
sb.Append("</a>")
sb.Append("</li>")
' Because I didn't get all children, I tried calling the same function here
' to see if I could get all child descendents.
' this didn't work
List_Childnodes(node)
End While
sb.Append("</ul>")
End If
Return sb.ToString
End Function
</script>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
<h1>Sitemap Test</h1>
<p>The <var>LoadMe</var> sub runs on Me.Load and recursively calls all children. The root node is manually moved into the first level for user convenience.</p>
<h2>Sitemap tree</h2>
<asp:Label ID="lblSitemap" runat="server" Text="Label"></asp:Label>
It turned out that all I needed was to write one line differently.
List_Childnodes(node)should have beensb.Append(List_Childnodes(node))I verified that security trimming of roles (as defined in web.sitemap) works very well.
Edit: I also realized that I needed to call the
List_ChildNodes(node)function before I closed the</li>tag. Now the nested UL is properly formed.Second edit: I also wrapped
Page.ResolveClientUrl()aroundnode.urlto make absolutely sure the links will work properly when viewed from both localhost and the server.