Question

I'm using the MVC SiteMap Provider to create a menu for my website, and highlight the current menu item.

In my controller I have a Index, Details, Add, Edit and Delete method. Only the Index and Add methods are available as a menu item.

I would still like to highlight the current main menu item (ie, Products) when I'm in the Details action. How do I do that?

I tried:

<mvcSiteMapNode title="Details" action="Details" visibility="IfSelected,!*" />

But that doesn't highlight the menu, and Details is in the menu even though I don't want it.

Then I tried

<mvcSiteMapNode title="Details" action="Details" visibility="IfSelected,!*" preservedRouteParameters="id" />

Which highlights the Details sub menu, but I'd rather have the Details submenu hidden in the menu and only have the main menu item ('Projects') highlighted.

Was it helpful?

Solution

There are really 2 separate things you are apparently attempting to achieve, but you have combined them into a single question.

First of all, to do CRUD operations, have a look at the article How to Make MvcSiteMapProvder Remember a User's Position and the accompanying downloadable demos. Specifically, look at the demo titled MvcSiteMapProvider-Forcing-A-Match because it demonstrates exactly how to nest the nodes, force them to match the current request, setup visibility, and dynamically change the title on the nodes according to the record you have selected.

The second part of your question is really about changing the output HTML to highlight a menu item, which you cannot do by using any of the built-in features. For that, you need to edit the /Views/Shared/DisplayTemplates/SiteMapNodeModel.cshtml file directly. This file is added to your MVC project by the NuGet package and you should be careful not to overwrite your changes to it when you upgrade. Do note that this file is shared by the Menu, SiteMap, and SiteMapPath HTML helpers, so you should either make a copy for the menu or take care to always use branching logic for the specific HTML helper(s) you want to edit.

To determine the closest matching node to the current page, you need this bit of razor code.

@{var isClosestVisibleMenuItem = false;}
@if ((Model.IsCurrentNode || (Model.IsInCurrentPath && !Model.IsRootNode && !Model.Descendants.Any())) 
    && Model.SourceMetadata["HtmlHelper"].ToString() == "MvcSiteMapProvider.Web.Html.MenuHelper")
{
    isClosestVisibleMenuItem = true;
}

Note that the SourceMetadata value is checked to ensure we are referring to the Menu - all other HTML helpers will always set the isClosestVisibleMenuItem variable to false.

Then it is just a matter of using the variable to tell the template what HTML to output. Here is a sample that puts a <b></b> around the closest visible node in the menu only. In a real-world example, you would probably want to add a specific CSS class to the anchor tag in order to highlight it.

@model MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel
@using System.Web.Mvc.Html
@using MvcSiteMapProvider.Web.Html.Models

@{var isClosestVisibleMenuItem = false;}
@if ((Model.IsCurrentNode || (Model.IsInCurrentPath && !Model.IsRootNode && !Model.Descendants.Any())) && Model.SourceMetadata["HtmlHelper"].ToString() == "MvcSiteMapProvider.Web.Html.MenuHelper")
{
    isClosestVisibleMenuItem = true;
}

@if (Model.IsCurrentNode && Model.SourceMetadata["HtmlHelper"].ToString() != "MvcSiteMapProvider.Web.Html.MenuHelper")  { 
    <text>@Model.Title</text>
} else if (Model.IsClickable) {

    if (isClosestVisibleMenuItem)
    {
        <b>
        @if (string.IsNullOrEmpty(Model.Description))
        {
            <a href="@Model.Url">@Model.Title</a>
        }
        else
        {
            <a href="@Model.Url" title="@Model.Description">@Model.Title</a>
        }
        </b>
    }
    else
    {
        if (string.IsNullOrEmpty(Model.Description))
        {
            <a href="@Model.Url">@Model.Title</a>
        }
        else
        {
            <a href="@Model.Url" title="@Model.Description">@Model.Title</a>
        }
    }
} else {
    if (isClosestVisibleMenuItem)
    {
        <b>@Model.Title</b>
    }
    else
    {
        <text>@Model.Title</text>   
    }
}

IfSelected is for a specific use case where you have a deep hierarchy and want to show the current path (no matter how deep) while keeping the rest of the menu at a high level. Typically, this would only apply to the Menu, none of the other HTML helpers. But nothing about your question implies that is what you are trying to achieve.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top