MvcSiteMapProvider - Несколько страниц необходимо связать с одним узлом меню - PullRequest
7 голосов
/ 30 марта 2012

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

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

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

В моем файле 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. Я хочу указать, что любая данная страница является частью узла родительского меню «Member Center», так что дочерние узлы меню «Member Center» будут отображаться независимо от того, определена ли данная страница как определенный узел в структуре меню.

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

Есть предложения?

Ответы [ 3 ]

6 голосов
/ 11 апреля 2012

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

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

Edit:

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

IList<string> classes = new List<string> ();
if (Model.IsCurrentNode || Model.IsInCurrentPath && !Model.Children.Any ())
{
    classes.Add ("menu-current");
}
1 голос
/ 18 мая 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");
}
1 голос
/ 12 апреля 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>
}
...