I am working on a website that contains a main menu on top. In addition, I am making use of the SiteMapPath to show a breadcrumb that helps the user while browsing the website. I might have the following heirarchy in the website:
Default.aspx
Articles.aspx
--> BrowseArticles.aspx
--> ShowArticle.aspx
I prepared first of all an xml file of the major pages something like this:
<?xml version="1.0" encoding="utf-8" ?>
<MainMenu>
<MenuItem>
<Title>Home</Title>
<Href>Default.aspx</Href>
</MenuItem>
<MenuItem>
<Title>Articles</Title>
<Href>ShowCategories.aspx</Href>
</MenuItem>
</MainMenu>
So, my aim is to show a main menu of the major pages in the website. I prepare some code using XLinq to browse the XML file and based on the URL of the currently accessed page, I know which menu item I can style it differently since it is the one accessed currently.
However, when I go through the heirarchy, I loose the ability to catch the major page to style it differently. This is explained since those sub pages are not included in my menu xml file. What I need to do is whereever I am in the heirarchy of a major item for instance, going deep through the Articles section, I always want the Articles item to be selected. So I made use of the web.sitemap to know the sub-root of the currently visited page.
Sub-Root means all pages that are one level below the root. Because I want to show in my main menu the major pages, so I am interested only in the sub-root nodes!
So, here is the code I used to construct the main menu:
/// <summary>
/// This method is required to build the main menu.
/// It first starts by loading the XML menu file,
/// then select all MenuItem elements and add them to
/// a collection of MenuItem. The final step is a call
/// to GenerateHTML which will generate the actual code
/// for the main menu with the right item selected!
/// </summary>
/// <returns></returns>
public static string BuildMainMenu()
{
// Query the menu.xml file into an XElement
XElement menuItems= null;
// Load Data from Cache
if (Utility.Cache["MainMenu"] != null)
menuItems= (XElement)Utility.Cache["MainMenu"];
// Cache empty load from the xml file
if (menuItems == null)
{
try
{
menuItems = XElement.Load(HttpContext.Current.Server.MapPath("~/menu.xml"));
// Store the XML in a Cache - For better peformance
// Caching of the menu for 90 days with cache dependency on the menu.xml
// file
System.Web.Caching.CacheDependency depend=
new System.Web.Caching.CacheDependency(HttpContext.Current.Server.MapPath("menu.xml"));
Utility.Cache.Insert("MainMenu", menuItems, depend, DateTime.Now.AddDays(90), TimeSpan.Zero);
}
catch (System.Xml.XmlException ex)
{
throw ex;
}
}
// Get the MenuItems collection
List<MenuItem> menuItemsCol= new List<MenuItem>();
IEnumerable<XElement> iElements=
from e in menuItems.Elements("MenuItem")
select e;
foreach (XElement x in iElements)
{
CodeSuiteWeb.Menu.MenuItem item =
new CodeSuiteWeb.Menu.MenuItem {Title=(string)x.Element("Title"), Href=(string)x.Element("Href")};
if (item != null)
menuItemsCol.Add(item);
}
// Generate HTML for the menu
if ((menuItemsCol != null) && (menuItemsCol.Count > 0))
return GenerateMenuHTML(menuItemsCol);
return string.Empty;
}
The code above is well documented through XML documentation. There are left few major methods that will helps in building the menu:
/// <summary>
/// Generates the HTML code that constitutes
/// the main menu of the website
/// </summary>
/// <param name="menuItemsCol">
/// A collection of <seealso cref="MyWeb.Menu.MenuItem" />
/// representing the items in the menu xml file/>
/// </param>
/// <returns></returns>
private static string GenerateMenuHTML(List<MenuItem> menuItemsCol)
{
// Container to hold the HTML generated
StringBuilder sb= new StringBuilder();
// Get the visited page
string currentPage= GetCurrentPage();
// Generate the Menu HTML
foreach (MyWeb.Menu.MenuItem item in menuItemsCol)
{
if (item.Href.StartsWith(GetSubRootNode(), StringComparison.CurrentCultureIgnoreCase))
sb.AppendFormat("<li><a href=\"{0}\" title=\"{1}\" class=\"hover\">{1}</a></li>", item.Href, item.Title);
else
sb.AppendFormat("<li><a href=\"{0}\" title=\"{1}\">{1}</a></li>", Utility.BasePath + item.Href, item.Title);
}
return sb.ToString();
}
/// <summary>
/// Gets the Sub-Parent node's URL of the current node.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents the URL of the sub-parent node
/// </returns>
private static string GetSubRootNode()
{
// Get the current code
SiteMapNode current = SiteMap.CurrentNode;
// Get the root node
SiteMapNode root = SiteMap.RootNode;
// Get a copy of the root node
SiteMapNode node = root;
string url = "";
// If we are processing the parent node,
// then return its url.
if (current != root)
{
// We check if the current's parent node
// is different from the root, go up
// one level in the web.sitemap. Kepp up looping
// until the current node's parent is the root
while (current.ParentNode != root)
{
current = current.ParentNode;
}
node = current;
}
// If localhost, then remove /CodeSuite/CodeSuiteWeb/
url = node.Url;
if (HttpContext.Current.Request.IsLocal)
url = url.Substring(24, url.Length - 24);
return url;
}
private static string GetCurrentPage()
{
string rawUrl= HttpContext.Current.Request.FilePath;
// If localhost, then remove /CodeSuite/CodeSuiteWeb/
if (HttpContext.Current.Request.IsLocal)
rawUrl = rawUrl.Substring(24, rawUrl.Length - 24);
return rawUrl;
//// Remove all the path and extension and reture pure page name
//rawUrl= rawUrl.Substring(rawUrl.LastIndexOf('/')+1);
//return rawUrl;
}
As you can see, no matter how deep you go through the heirarchy of pages under one sub-root node, always the sub-root node (which is one of the major nodes in the main menu) will be selected!
Hope this helps!
Regards
Tags: ASP.NET 2.0 - General