MvcSiteMapProvider — несколько страниц должны ссылаться на один узел меню

В моем проекте MVC3 я установил MvcSiteMapProvider v.3.2.1 от Maartenba и создал очень простое статическое двухуровневое меню. Ниже представлена ​​структура концептуальной карты.

- Home
- Member Center
    - Member Listing [SELECTED]
    - Event Calendar
    - Documents
- Administration

Теперь в списке участников есть много подстраниц (например, «Подробности», «Редактировать» и т. д.), но я не хочу отображать их как пункты меню 3-го уровня (главным образом потому, что они привязаны к определенному ID пользователя). Однако я хочу, чтобы все эти страницы третьего уровня были «привязаны» к узлу меню «Список участников», чтобы он отображался как выбранный на этих страницах.

У меня есть следующий код в моем файле Mvc.SiteMap:

<mvcSiteMapNode title="Home" controller="Home" action="Index">
  <mvcSiteMapNode title="Member Center" area="Members" controller="Home" action="Index" roles="Approved Member" >
    <mvcSiteMapNode title="Member Listing" area="Members" controller="Member" action="List" />
    <mvcSiteMapNode title="Event Calendar" area="Members" controller="Event" action="List" />
    <mvcSiteMapNode title="Documents" area="Members" controller="Document" action="List" />
  </mvcSiteMapNode>
  <mvcSiteMapNode title="Administration" area="Admin" controller="Home" action="Index" roles="Site Administrator" >
  </mvcSiteMapNode>
</mvcSiteMapNode> 

Чтобы отобразить меню, я использую следующий код в файле _Layout.cshtml:

@Html.MvcSiteMap().Menu(1, true, true, 1, true, true)

Наконец, я изменил файл SiteMapNodeModel.cshtml, чтобы он добавлял класс «selectedMenuItem» к узлу, который соответствует странице, которую просматривает пользователь. Вот фрагмент, отображающий узел меню.

@model SiteMapNodeModel
  <a href="@Model.Url" class="@(Model.IsCurrentNode ? "selectedMenuItem" : "")">@Model.Title</a>

Отображение и навигация по карте работают нормально, пока я не перейду дальше в зону для участников. Например, если я пройду мимо Members/Member/List (что отображает меню правильно) и перейду на страницу, подобную Members/Member/Detail/1, дочерние узлы в Центре участников ("Список участников", "Календарь событий" и т. д.) исчезнут . Поэтому вот две мои проблемы с моим текущим кодом:

  1. Я хочу указать, что любая данная страница является частью узла родительского меню «Центр участников», чтобы отображались узлы дочернего меню «Центра участников», независимо от того, определена ли данная страница как конкретный узел в меню. структура.

  2. Я хочу указать (возможно, в представлении или действии контроллера), что конкретная страница должна быть привязана к определенному узлу меню. Например, когда пользователь находится в Members/Member/Detail/1, я просто хочу, чтобы дочерний узел «Список участников» был указан как IsCurrentNode, чтобы файл SiteMapNodeModel.cshtml правильно украсил его классом «selectedMenuItem».

Какие-либо предложения?


person bigmac    schedule 30.03.2012    source источник


Ответы (3)


Вы можете добавить узлы 3-го уровня в XML карты сайта и указать видимость, чтобы скрыть их из меню. Вот объявление узла, чтобы отображать его только в хлебных крошках:

<mvcSiteMapNode area="Members"
                controller="Member"
                action="Detail"
                visibility="SiteMapPathHelper,!*"
                title="Member details" />

Редактировать:

Насколько я знаю, вы не можете установить IsCurrentNode. Но вы можете проверить, выбран ли в данный момент узел меню, с помощью следующего кода (я использую его в шаблоне отображения SiteMapNodeModel):

IList<string> classes = new List<string> ();
if (Model.IsCurrentNode || Model.IsInCurrentPath && !Model.Children.Any ())
{
    classes.Add ("menu-current");
}
person Max Kiselev    schedule 11.04.2012
comment
Макс, большое спасибо! Это решает основную проблему исчезновения узлов 2-го уровня. Однако знаете ли вы, что узел второго уровня должен быть установлен в IsCurrentNode, когда это произойдет? Например, я хотел бы, чтобы узел «Список участников» отображался как выбранный на странице «Сведения об участниках». Если нет, то ничего страшного... просто эстетический запрос. - person bigmac; 12.04.2012

Добавляя к ответу Макса, я бы также создал метод расширения для SiteMapNodeModel. Что вы могли бы использовать для реализации всей пользовательской логики, необходимой для этого:

public static class SiteMapNodeModelExtender
{
  public static bool IsRealCurrentNode(this SiteMapNodeModel node)
  {
    // Logic to determine the "real" current node...
    // A naive implementation could be:
    var currentPath = HttpContext.Current.Request.Url.AbsolutePath;
    return currentPath.StartsWith("Members/Member/") && node.Title.Equals("Member Center")
  }
}

и соответствующим образом измените шаблон отображения:

/* Also check IsRealCurrentNode, depending on the use case maybe only
IsRealCurrentNode */
@if ((Model.IsCurrentNode || Model.IsRealCurrentNode()) && Model.SourceMetadata["HtmlHelper"].ToString() != "MvcSiteMapProvider.Web.Html.MenuHelper")  { 
  <text>@Model.Title</text>
} else if (Model.IsClickable) { 
  <a href="@Model.Url ">@Model.Title</a>
} else { 
  <text>@Model.Title</text>
}
person Xharze    schedule 12.04.2012

В дополнение к ответу Макса Киселева, если вы хотите использовать эту технику, но иметь возможность использовать атрибуты в действиях вашего контроллера, я сделал следующее:

Определите настраиваемого поставщика видимости:

public class AlwaysInvisibleVisibilityProvider : ISiteMapNodeVisibilityProvider
{
    public bool IsVisible(SiteMapNode node, HttpContext context, IDictionary<string, object> sourceMetadata)
    {
        return false;
    }
}

Затем подкласс MvcSiteMapNodeAttribute:

public class InvisibleMvcSiteMapNodeAttribute : MvcSiteMapNodeAttribute
{
    public InvisibleMvcSiteMapNodeAttribute(string key, string parentKey)
    {
        Key = key;
        ParentKey = parentKey;
        VisibilityProvider = typeof (AlwaysInvisibleVisibilityProvider).AssemblyQualifiedName;
    }
}

И затем вы можете использовать его в действиях вашего контроллера:

[HttpGet]
[InvisibleMvcSiteMapNodeAttribute("ThisNodeKey", "ParentNodeKey")] 
public ViewResult OrderTimeout()
{
    return View("Timeout");
}
person Peter McEvoy    schedule 18.05.2012