Aug 23 2007

Website Menu + web.sitemap: A smart combination

Category: ASP.NET 2.0 - GeneralBil@l @ 18:30

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:

         --> 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" ?>

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)
                    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)
            // 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);
                    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!


Comments are closed