Submenus

May 25, 2009 at 12:10 PM

Hi,

Love the project, I have found it very useful. I have created an extension helper based on your code that you might want to include in your menu helpers that renders a second level submenu based on the top level node:

public static class MenuHelper

    {

        public static string SubMenu(this HtmlHelper helper, string providerName,  bool showStartingNode, string subMenuCssClass,string selectedMenuItemCssClass)

        {

            // Check for provider

            if (SiteMap.Providers[providerName] == null)

            {

                throw new UnknownSiteMapProviderException(string.Format("Unknown SiteMap provider: {0}", providerName));

            }

 

            // Helpers

            HttpContextBase httpContext = new HttpContextWrapper(HttpContext.Current);

            UrlHelper urlHelper = new UrlHelper(new RequestContext(httpContext, RouteTable.Routes.GetRouteData(httpContext)));

 

            // Determine root node

            SiteMapNode relativeRootNode = SiteMap.Providers[providerName].CurrentNode;

            

            // Fetch nodes to render

            SiteMapNodeCollection nodesToRender = new SiteMapNodeCollection();

            if (showStartingNode)

            {

                nodesToRender.Add(relativeRootNode);

            }

            if (relativeRootNode.HasChildNodes)

            {

                nodesToRender.AddRange(relativeRootNode.ChildNodes);

            }

            else if (relativeRootNode.ParentNode != SiteMap.RootNode)

            {

                nodesToRender.AddRange(relativeRootNode.ParentNode.ChildNodes);

            }

 

            // String builder

            var sb = new StringBuilder();

 

            if (nodesToRender.Count > 0)

            {                

 

                // Create unordered list tag

                sb.Append("<div");

                if (!string.IsNullOrEmpty(subMenuCssClass))

                {

                    sb.Append(string.Format(" class=\"{0}\" ><ul>", subMenuCssClass));

                }

 

                // Render each node

                foreach (SiteMapNode node in nodesToRender)

                {

                    MvcSiteMapNode mvcNode = node as MvcSiteMapNode;

                    bool nodeVisible = true;

 

                    // Node authorized?

                    if (!node.IsAccessibleToUser(HttpContext.Current))

                        nodeVisible = false;

 

                    // Node visible?

                    if (mvcNode != null && mvcNode.Visibility != MvcSiteMapNodeVisibility.Full)

                        nodeVisible = false;

 

                    // Render node

                    if (nodeVisible)

                    {

                        if (SiteMap.CurrentNode == node || node.IsInCurrentNodePath())

                        {

                            sb.Append("<li");

                            if (!string.IsNullOrEmpty(selectedMenuItemCssClass))

                            {

                                sb.Append(string.Format(" class=\"{0}\"", selectedMenuItemCssClass));

                            }

                            sb.Append(">");

                        }

                        else

                        {

                            sb.Append("<li>");

                        }

 

                        string url = node.Url;

                        if (url.StartsWith("~"))

                        {

                            url = urlHelper.Content(node.Url);

                        }

 

                        sb.AppendFormat("<a href=\"{0}\">{1}</a>", url, helper.Encode(node.Title));

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

                    }

                }

 

                // Close unordered list tag

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

            }

 

            return sb.ToString();

        }

    }

 

I then used it in my master page directly below where I used your Menu helper:

<div>

       <% if (SiteMap.CurrentNode != SiteMap.RootNode ) // Don't render a sub menu for the root node (Home)

            {%> 

                <%= Html.SubMenu(SiteMap.Provider.Name, false, "submenu", "submenu-current")%>

            <%} %>

   </div> 

 

Thnaks again.

 

AB

Mar 26, 2010 at 6:31 PM

I got the following error when I added your method into MenuHelper.cs file and included the following in my master page:

<div>

       <% if (SiteMap.CurrentNode != SiteMap.RootNode ) // Don't render a sub menu for the root node (Home)

            {%> 

                <%= Html.SubMenu(SiteMap.Provider.Name, false, "submenu", "submenu-current")%>

            <%} %>

   </div>

CS1061: 'System.Web.Mvc.HtmlHelper<object>' does not contain a definition for 'SubMenu' and no extension method 'SubMenu' accepting a first argument of type 'System.Web.Mvc.HtmlHelper<object>' could be found (are you missing a using directive or an assembly reference?)


I added and rebuild the whole project and referenced the dll again but still doesn't work. Do  I need to make any more changes?

Mar 27, 2010 at 3:05 AM

Look at Html.MenuPinned, which does the same thing and is already included?

 

Apr 6, 2010 at 12:31 AM

Re: Look at Html.MenuPinned, which does the same thing and is already included?

It doesn't look like Html.MenuPinned() support setting of the setCssSelected class - only main menu gets highlighted, not my sub-menu. I'm going to try Haybarr's code shortly as it seems to address this...

Apr 6, 2010 at 1:02 AM

Would be best to take a look at the included behavior and simply modify its output to include a class on the selected node.

Then you can submit that as an enhancement request.

Apr 6, 2010 at 9:09 AM

Yep, already did this modification: http://mvcsitemap.codeplex.com/Thread/View.aspx?ThreadId=208337

Apr 15, 2010 at 12:27 PM

I cannot get MenuPinned to produce the list that the Submenu code example above gives. MenuPinned, in all my tests, produces a "tree" of nodes, when what we really need is just the child nodes of the current "main menu" node. The Submenu code above does this. Am I using MenuPinned incorrectly? How do you use it to produce just the child nodes?

Apr 15, 2010 at 2:25 PM

MenuPinned creates a tree of nodes starting at a certain level.

So, if you have something like 'tabs at top of page' and when you click on a tab you have a menu on the left.   That menu on the left might use supersubs or something, so menupinned produces a tree starting at that node.

Apr 15, 2010 at 3:00 PM

So MenuPinned cannot produce the "Left Menu" concept?

Apr 15, 2010 at 3:10 PM

I would say we need an overload of menupinned to have a 'max levels' parameter.  Then you could pass 1 in for that and it would work.

It should be a really easy modification, if you want to submit that as a patch, would be great - he will accept it.

Apr 15, 2010 at 5:54 PM
Edited Apr 16, 2010 at 5:59 PM

I would like to contribute to making the following enhancements to the menu helper:

1) make it (or MenuPinned) work like a submenu by giving it a maximum level to render.
2) allow an additional inner tag, such as a "".
3) create a new visible state of "WhenActive" which would only make a node visibile in the menu if it was active.
4) create a new menu parameter, users, that would behave like the users attribute in the allow/deny config. So users="?" would be visible to only unauthenticated users, users="+" would be for only authenticated users, and otherwise you could put individual user names.

Apr 15, 2010 at 11:11 PM

Sorry I have not been responsive but I have been very busy at work. Are there still any problems using the example I posted? If so let me know. Otherwise jjorczak your experience is exactly the problem I had when I posted the code above. Unfortunately I did not have time to make a contribution to the project but if you would like to work on it together let me know as  I have a few days to spare next week.

 

 

Apr 16, 2010 at 11:46 AM

Your example works well, except for one item that was bombing on a null reference. I removed it quick in my copy to get it to pass, but did not spend the time analyzing it yet. I would love to collaborate on the fixes and making these changes permanent. Please let me know how to proceed.