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:

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:

Comments are closed