1

In a project, i've implemented MvcSiteMapProvider that work great. This is a side menu generated with @Html.MvcSiteMap().Menu()

Here is a node of the menu (Mvc.sitemap file).

<mvcSiteMapNode title="Home" controller="Home" action="Index">
<mvcSiteMapNode title="About" controller="Home" action="About"/>
<mvcSiteMapNode title="Project" controller="Home" action="DummyAction">
  <mvcSiteMapNode title="List" controller="Home" action="Project"/>
  <mvcSiteMapNode title="Edit" controller="Home" action="Edit" preservedRouteParameters="id" visibility="SiteMapPathHelper,!*"/>
</mvcSiteMapNode>

When I load the "List" from "Project", it displays a page with all projects and I can select one of them to load the Edit action with the related ID of the project.

The problem is, when I'm in the Edit action page, the side menu is all collapsed, but I'm expecting to have the "Project" node opened. If I add the node "Edit" in the menu, it work (matching node action), but I don’t want this node because its useless for the user.

Also, I've tried the DefaultSiteMapNodeVisibiltyProvider, I can hide the "Edit" node if added, but when I'm in the "Edit" action page, the Project node is also closed.

I'll face the same problem for the "New/Add" operation that I don’t want to see in the side menu, but will be accessible from a link in the project list. However, for these operations, I want to let the user know that it is inside the "Project" section, with the "Project" node opened.

See this project on GitHub: Project on Github

Best regards,

  • Since MvcSiteMapProvider doesn't deal with "opening" or "collapsing" nodes on the UI, this sounds like a problem with your Bootstrap code. So, you might get more helpful answers if you add the bootstrap tag to your question. There are blog examples of how to setup the MvcSiteMapProvider templates for Bootstrap (see [this comment](http://stackoverflow.com/questions/36135121#comment-59925036)). – NightOwl888 Mar 23 '16 at 00:09
  • In addition, you should edit your answer to add the complete source of how to setup your scenario. See [How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve). There isn't enough configuration/code here to reproduce the problem, so it is difficult to determine exactly what the source of the problem is. You might consider creating a demo of the problem on GitHub and linking to it from here. – NightOwl888 Mar 23 '16 at 00:13
  • Thank for your comment. Effectively, it has something with the bootstrap because it must add the class "active open" to the current node, that is not in the menu because the node is hidden. In the IEnumerable of the MenuHelperModel.cshtml, the hidden node is not, so I can't catch the visibility state to make the parent node active. Like you said, I'll prepare a setup scenario to reproduce the problem. Best regards, – Jean-Philippe Chenel Mar 23 '16 at 13:42
  • @NightOwl888 Project was created on GitHub with the problem. Thanks. – Jean-Philippe Chenel Mar 24 '16 at 15:39

1 Answers1

0

Your issue appears to be that you haven't accounted for the recursive nature of the Menu HTML helper. There are 3 templates that the Menu interacts with:

  1. MenuHelperModel.cshtml
  2. SiteMapNodeModelList.cshtml
  3. SiteMapNodeModel.cshtml

The problem is that you haven't added the appropriate logic to the SiteMapNodeModelList.cshtml. In addition, the logic in the MenuHelperModel.cshtml is overly complex.

A good approach for dealing with both a Menu and SiteMapPath HTML helper on the same page with customizations is to use named templates rather than editing the default templates.

BootstrapMenuHelperModel.cshtml

@model MvcSiteMapProvider.Web.Html.Models.MenuHelperModel
@using System.Web.Mvc.Html
@using MvcSiteMapProvider.Web.Html.Models
<ul id="menu" class="nav sidebar-menu">
    @foreach (var node in Model.Nodes)
    {
        string nodeclass = "";
        if (node.IsInCurrentPath && !node.IsRootNode)
        {
            nodeclass = "active";
            if (node.Children.Any())
            {
                nodeclass += " open";
            }
        }

        <li class="@nodeclass">
            @Html.DisplayFor(m => node)
            @if (node.Children.Any())
            {
                // Here we refer to a named template BootstrapMenuNodeModelList.cshtml
                @Html.DisplayFor(m => node.Children, "BootstrapMenuNodeModelList")
            }
        </li>
    }
</ul>

BootstrapMenuNodeModelList.cshtml

@model MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModelList
@using System.Web.Mvc.Html
@using MvcSiteMapProvider.Web.Html.Models
<ul>
    @foreach (var node in Model)
    {
        string nodeclass = "";
        if (node.IsInCurrentPath && !node.IsRootNode)
        {
            nodeclass = "active";
            if (node.Children.Any())
            {
                nodeclass += " open";
            }
        }

        <li class="@nodeclass">
            @Html.DisplayFor(m => node)
            @if (node.Children.Any())
            {
                // Here we refer to a named template BootstrapMenuNodeModelList.cshtml,
                // which happens to be a recursive call to this template.
                @Html.DisplayFor(m => node.Children, "BootstrapMenuNodeModelList")
            }
        </li>
    }
</ul>

Usage

Here, we tell the Menu HTML helper to use our custom template named BootstrapMenuHelperModel.cshtml.

@Html.MvcSiteMap().Menu("BootstrapMenuHelperModel")

NOTE: You could also create a custom template for the SiteMapNodeModel using the same approach. You would just need to call the overload in the other templates to use your template name.

Example:

Change @Html.DisplayFor(m => node) to @Html.DisplayFor(m => node, "MyTemplate") and create a corresponding file named MyTemplate.cshtml in the /Views/Shared/DisplayTemplates/ folder.

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • Thank you very much for this answer. It has worked very well on the demo project and I've implemented it easily on my project. I've just added the class "submenu" to the ul item in BootstrapMenuNodeModelList.cshtml. Best regards. – Jean-Philippe Chenel Apr 04 '16 at 15:15