hierarchical menu (question & solution)

Topics: Controls
Oct 17, 2007 at 8:00 AM
Sorry if this has been brought up before, but I haven't been able to find a discussion about it.

Supposedly there is a way in 1.2 to have a hierarchical menu by having parentpages to pages. This should update the sitemap (which I haven't been able to find).
Has anyone done this for real? Anyone have a control to share?

What I would like is the format being like this:

<ul>
<li>Home</li>
<li>About Us
<ul>
<li>History</li>
<li>Employees</li>
</ul>
</li>
</ul>

Wouldn't this be good if it was a standard control to use?

Cheers!
- jonah
Oct 17, 2007 at 12:44 PM
Edited Oct 17, 2007 at 2:30 PM
I created my own version since nobody came up with a good one.. =)

Here's my code. It creates an hierarchical menu and adds a "current" class to whatever top-node page is currently active.
There probably is a lot better way to do this, so feel free to comment and improve.

Cheers!
- jonah

************************************************************************************************************************************************************************************************************
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Web;
using System.Web.UI;

public partial class themestestcontrols_HierarchicalMenu : UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
}

protected override void Render(HtmlTextWriter writer)
{
// Stringbuilder to hold menu
StringBuilder sb = new StringBuilder(100);
sb.AppendLine("<ul id=\"navmenu\">");

// Add all root nodes
List<Guid> pages = GetPages(true);

foreach (Guid page in pages)
{
sb.AppendFormat("<li><a href=\"{0}\"{1}>{2}</a>", BlogEngine.Core.Page.GetPage(page).RelativeLink, MarkCurrent(page, BlogEngine.Core.Page.GetPage(page).RelativeLink.ToString()), BlogEngine.Core.Page.GetPage(page).Title);
this.CreateMenu(page, sb);
}

sb.AppendLine("</ul>");

writer.Write(sb);
base.Render(writer);
}

/// <summary>
/// Creates the menu.
/// Does a recursive call whenever a child of a child is found
/// </summary>
/// <param name="parentId">The parent id.</param>
/// <param name="sb">The stringbuilder.</param>
public void CreateMenu(Guid parentId, StringBuilder sb)
{
// Retrieve list of children to page
List<Guid> Children = GetChildren(parentId);

if (Children.Count > 0)
{
sb.Append("<ul>");
foreach (Guid child in Children)
{
Guid CurrentPage = child;
// Does this childpage have any children of its own?
if (HasChildren(child))
{
sb.AppendFormat("<li><a href=\"{0}\">{1}</a>\n", BlogEngine.Core.Page.GetPage(child).RelativeLink, BlogEngine.Core.Page.GetPage(child).Title);
CreateMenu(CurrentPage, sb);
sb.AppendLine("");
sb.AppendLine("</li>");
}
else
{
sb.AppendFormat("<li><a href=\"{0}\">{1}</a></li>\n", BlogEngine.Core.Page.GetPage(child).RelativeLink, BlogEngine.Core.Page.GetPage(child).Title);
}
}
sb.Append("</ul>");
}
else
{
sb.Append("</li>");
}


}

/// <summary>
/// Determines whether the specified gid has children.
/// </summary>
/// <param name="gid">The gid.</param>
/// <returns>
/// <c>true</c> if the specified gid has children; otherwise, <c>false</c>.
/// </returns>
private bool HasChildren(Guid gid)
{
foreach (BlogEngine.Core.Page page in BlogEngine.Core.Page.Pages)
{
if (page.Parent == gid)
{
return true;
}
}
return false;
}

/// <summary>
/// Gets the pages.
/// </summary>
/// <param name="OnlyWithoutChildren">if set to <c>true</c> only without children.</param>
/// <returns></returns>
private List<Guid> GetPages(bool OnlyWithoutChildren)
{
List<Guid> pages = new List<Guid>();
foreach (BlogEngine.Core.Page page in BlogEngine.Core.Page.Pages)
{
if (page.ShowInList && page.IsPublished)
{
if (OnlyWithoutChildren && (page.Parent == new Guid()))
{
pages.Add(page.Id);
}
else if (!OnlyWithoutChildren)
{
pages.Add(page.Id);
}
}
}
return pages;
}

/// <summary>
/// Returns a list of all childpages to a given page
/// </summary>
/// <param name="gid">The GUID of the page.</param>
/// <returns></returns>
private List<Guid> GetChildren(Guid gid)
{
List<Guid> pageChildren = new List<Guid>();
foreach (BlogEngine.Core.Page page in BlogEngine.Core.Page.Pages)
{
if (page.Parent == gid)
{
pageChildren.Add(page.Id);
}
}
return pageChildren;
}

/// <summary>
/// Parses url and gets name of page.
/// </summary>
/// <param name="requestPath">The request path.</param>
/// <returns>Name of page in lower case</returns>
public static string GetPageName(string requestPath)
{
if (requestPath.IndexOf('?') != -1)
requestPath = requestPath.Substring(0, requestPath.IndexOf('?'));
return requestPath.Remove(0, requestPath.LastIndexOf("/") + 1).ToLower();
}

/// <summary>
/// Checks the link, and if it's the current page it marks it with a "current" class.
/// </summary>
/// <param name="page">The page.</param>
/// <param name="url">The URL.</param>
/// <returns></returns>
private string MarkCurrent(Guid page, string url)
{
string sUrl = HttpUtility.UrlEncode(GetPageName(HttpContext.Current.Request.RawUrl.ToLower()));
string pageUrl = GetPageName(url);
if ((BlogEngine.Core.Page.GetPage(page).IsFrontPage && (sUrl == pageUrl)) || (BlogEngine.Core.Page.GetPage(page).IsFrontPage && (sUrl == "default.aspx")))
{
return " class=\"current\"";
}
else
{
if (string.Compare(sUrl, pageUrl) == 0)
{
return " class=\"current\"";
}
}
return string.Empty;
}
}
*********************************************************************************************************************************************************************************************
Coordinator
Oct 17, 2007 at 4:43 PM
Appreciate you sharing this, I’m working on a new theme and is looking for something similar.
Sep 19, 2008 at 12:02 PM
Edited Sep 19, 2008 at 12:14 PM
$0Hello, I have created a HierarchicalPageMenu widget that displays the page hierachy on the right side of the blog.It is inspired by and designed to replace the classic PageList widget.My version allows to allow the display of folder icons as well as tracking the current page as well if so desired.$0$0It can be downloaded at:$0http://d4.myfreefilehosting.com/d2/BlogEngine.NET.Widget.HierarchicalPageMenu.zip$0Happy Blogging.$0Maxime.$0