我只是潜入 MVC SiteMapProvider 并摆弄它,所以这个问题更多的是关于设计而不是无法实现我想要的。我设置了一个使用 MvcSiteMapProvider 3.3.4 的简单 MVC4 Web 应用程序。
我想用它实现的是有一个 2 级网站导航;一种用于水平显示的顶层,另一种用于垂直显示的低于该水平的级别。子菜单必须只显示在当前选定节点下分层的节点。此外,我希望在两个级别中突出显示活动节点,因此实际选择的节点以及如果它是子节点,我还想突出显示父节点。
我实际上能够进行设置,所以结果看起来像我想要的那样,但我真的不喜欢它在代码中的样子,所以我在这里要问的是这是否是一种合法的方式,或者是否有人可以启发我了解我目前缺少的最佳实践。
我面临的障碍是:
1)我只想使用一个站点地图文件并在对 MvcSiteMap.Menu 函数的不同调用中显示它的不同级别(如上所述,级别 1 是水平的,级别 2 是垂直的,级别 0 是站点地图的根节点[我不想看到])
2)我的第二个问题是我将 Home-Controller 的 Index-Action 作为我的根节点,但也作为根节点的子节点,因为我希望额外的层次结构将其他 Home-Controller 的操作放入。当然,这需要一些额外的配置来稍后显示适当的面包屑,因为事实证明,如果选择它,这也会使突出显示“Home”节点变得更加困难。
考虑到这一点,我设置了以下站点地图:
(web.config 部分)
<siteMap defaultProvider="MvcSiteMapProvider" enabled="true">
<providers>
<clear />
<add name="MvcSiteMapProvider" type="MvcSiteMapProvider.DefaultSiteMapProvider, MvcSiteMapProvider" siteMapFile="~/Mvc.Sitemap" securityTrimmingEnabled="true" cacheDuration="5" enableLocalization="true" scanAssembliesForSiteMapNodes="true" includeAssembliesForScan="" excludeAssembliesForScan="" attributesToIgnore="visibility" nodeKeyGenerator="MvcSiteMapProvider.DefaultNodeKeyGenerator, MvcSiteMapProvider" controllerTypeResolver="MvcSiteMapProvider.DefaultControllerTypeResolver, MvcSiteMapProvider" actionMethodParameterResolver="MvcSiteMapProvider.DefaultActionMethodParameterResolver, MvcSiteMapProvider" aclModule="MvcSiteMapProvider.DefaultAclModule, MvcSiteMapProvider" siteMapNodeUrlResolver="MvcSiteMapProvider.DefaultSiteMapNodeUrlResolver, MvcSiteMapProvider" siteMapNodeVisibilityProvider="MvcSiteMapProvider.FilteredSiteMapNodeVisibilityProvider, MvcSiteMapProvider" siteMapProviderEventHandler="MvcSiteMapProvider.DefaultSiteMapProviderEventHandler, MvcSiteMapProvider" />
</providers>
</siteMap>
(Mvc.Sitemap)
<?xml version="1.0" encoding="utf-8" ?>
<mvcSiteMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-3.0"
xsi:schemaLocation="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-3.0 MvcSiteMapSchema.xsd"
enableLocalization="true">
<mvcSiteMapNode title="Home" controller="Home" action="Index">
<mvcSiteMapNode title="Home" controller="Home" action="Index" visibility="!SiteMapPathHelper,*">
<mvcSiteMapNode title="HomeSub" controller="Home" action="Sub">
</mvcSiteMapNode>
</mvcSiteMapNode>
<mvcSiteMapNode title="Menu1" controller="Menu1" action="Index">
<mvcSiteMapNode title="Menu1Sub" controller="Menu1" action="Sub">
</mvcSiteMapNode>
</mvcSiteMapNode>
<mvcSiteMapNode title="Menu2" controller="Menu2" action="Index">
<mvcSiteMapNode title="Menu2Sub" controller="Menu2" action="Sub">
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMap>
(Views\Shared\DisplayTemplates\MenuHelperModel.cshtml)
@model MvcSiteMapProvider.Web.Html.Models.MenuHelperModel
@using System.Web.Mvc.Html
@using MvcSiteMapProvider.Web.Html.Models
<ul id="menu">
@{
string firstUrl = null;
}
@foreach (var node in Model.Nodes) {
string classes = node.IsCurrentNode | node.IsInCurrentPath | ( ((MvcSiteMapProvider.MvcSiteMapNode)SiteMap.CurrentNode).Action == node.Action && ((MvcSiteMapProvider.MvcSiteMapNode)SiteMap.CurrentNode).Controller == node.Controller )
? "current" : "";
if (firstUrl == null)
{
firstUrl = node.Url;
}
else if (node.Url.Contains(firstUrl))
{
classes += " child";
}
<li class="@classes">@Html.DisplayFor(m => node)
@if (node.Children.Any()) {
@Html.DisplayFor(m => node.Children)
}
</li>
}
</ul>
我用这个渲染主站点菜单
@Html.MvcSiteMap("MvcSiteMapProvider").Menu(0, true, false, 1)
我读这个就像“让我从根节点开始菜单到低于它的级别;将根节点放入与其子节点相同的级别,但不向我显示根节点”。我没有接到这个电话,因为如果我将startingNodeInChildLevel 设置为false 并将showStartingNode 设置为false,我希望返回相同的结果,但不是吗?这怎么写才能更容易理解?
我的子菜单是这样的
@Html.MvcSiteMap("MvcSiteMapProvider").Menu(2, 1, true)
老实说,我也不太理解这个调用,当我尝试 MvcSiteMap.Menu 的不同重载时,我偶然发现了它,它似乎完全符合我的需要(将显示的节点减少到仅在所选父节点下的节点节点)。有人对此进行了澄清,它到底做了什么?
添加了一个面包屑
@Html.MvcSiteMap("MvcSiteMapProvider").SiteMapPath()
现在既然这似乎对我有用,我为什么还要费心发布这个?
好吧,对于第一次(或者可能是前十次 :-))阅读此内容的任何人来说,对 MvcSiteMap.Menu 的调用肯定会让人感到困惑。
我不喜欢当前解决方案的第二件事是 Home-Controller 的 Index-Action-Node 中的可见性设置;我把它放在那里,如果我选择了“HomeSub”-Link,Home-Node 就不会在面包屑中出现两次。
第三件事:是否有更好的方法来确定是否选择了当前节点(在非常具体的情况下,选择了“Home”,因为它也是“Home”的子节点?我在那里添加了一个检查控制器和操作相等,因为 IsCurrentNode 似乎比较了节点拥有的键和不幸的是不同的键。
我知道这听起来像是一个模糊的问题,但我不想在这里开始讨论我想知道这是否是一种好的方式(或者什么是更好/更容易阅读的方式)以及是否有人可以向我解释那些菜单调用以及为什么我必须按照我所做的那样设置可见性来完成这项工作。
如果你想摆弄这个,你可以在这里下载这个“完整的”示例项目(Visual Studio 2012)。