【问题标题】:Adding "active" tag to navigation list in an asp.net mvc master page在 asp.net mvc 母版页的导航列表中添加“活动”标签
【发布时间】:2010-09-17 21:26:21
【问题描述】:

在默认的asp.net mvc项目中,在Site.Master文件中,有一个菜单导航列表:

<div id="menucontainer">
    <ul id="menu">              
        <li><%= Html.ActionLink("Home", "Index", "Home")%></li>
        <li><%= Html.ActionLink("About Us", "About", "Home")%></li>
    </ul>
</div>

这会在浏览器中呈现为:

<div id="menucontainer"> 
    <ul id="menu">              
        <li><a href="/">Home</a></li> 
        <li><a href="/Home/About">About Us</a></li> 
    </ul> 
</div> 

我希望能够根据被调用的视图动态设置活动列表项。也就是说,当用户查看主页时,我希望创建以下 HTML:

<div id="menucontainer"> 
    <ul id="menu">              
        <li class="active"><a href="/">Home</a></li> 
        <li><a href="/Home/About">About Us</a></li> 
    </ul> 
</div> 

我希望这样做的方法是:

<div id="menucontainer">
    <ul id="menu">              
        <li <% if(actionName == "Index"){%> class="active"<%}%>><%= Html.ActionLink("Home", "Index", "Home")%></li>
        <li <% if(actionName == "About"){%> class="active"<%}%>><%= Html.ActionLink("About Us", "About", "Home")%></li>
    </ul>
</div>

这里的关键是&lt;% if(actionName == "Index"){%&gt; class="active"&lt;%}%&gt; 行。我不知道如何确定当前的 actionName 是什么。

关于如何做到这一点的任何建议?或者,如果我完全走错了路,有没有更好的方法来做到这一点?

【问题讨论】:

    标签: asp.net-mvc


    【解决方案1】:

    您的视图必须了解控制器的操作这一事实违反了 MVC 模式。也许您的控制器可以将一些“控制”信息传递给视图,最终让它完成同样的事情,唯一的区别是谁负责。

    就像在控制器的操作中一样,您可以:

    public ActionResult Index(){
         ViewData["currentAction"] = "Index";
         //... other code
        return View();
    }
    

    那么在你看来,你可以:

    <% if( ((string)ViewData["currentAction"]) == "Index" {%> <!- some links --><% } %>
    <% if( ((string)ViewData["currentAction"]) == "SomethingElse" {%> <!- some links --><% } %>
    

    但是,我想得越多,我就越质疑为什么您对多个操作使用相同的视图。 那个的观点是否相似?

    如果用例证明它是合理的,那么就按照我上面的建议去做。但除此之外,也许您可​​以将事情分解为多个视图(每个控制器操作一个视图),问题就会自行解决。

    【讨论】:

      【解决方案2】:

      试试

      应该可以正常工作!!!

      编辑:在 Beta1 中删除

      从 ViewContext 类中删除了 ViewName 属性。

      【讨论】:

        【解决方案3】:

        在视图中,您可以通过以下方式获取当前操作名称:

        ViewContext.RouteData.Values["action"].ToString()
        

        【讨论】:

          【解决方案4】:

          根据之前的答案,这是我目前针对同一问题的解决方案:

          在母版页中,我给每个 li 一个对应于控制器和操作的 id,因为这应该从 ActionLink 中知道。我以前使用页面标题执行此操作,但这有助于组织。

          Site.Master:

          <ul id="menu">
              <li id="menuHomeIndex" runat="server"><%= Html.ActionLink("Home", "Index", "Home") %></li>
              <li id="menuHomeAbout" runat="server"><%= Html.ActionLink("About Us", "About", "Home") %></li>
          </ul>
          

          Site.Master.cs:

          // This is called in Page_Load
          private void SetActiveLink()
          {
              string action = "" + ViewContext.RouteData.Values["controller"] + ViewContext.RouteData.Values["action"];
              var activeMenu = (HtmlGenericControl)Page.Master.FindControl("menu" + action);
          
              if (activeMenu != null)
              {
                  activeMenu.Attributes.Add("class", "selected");
              }
          }
          

          它比内联代码工作量更大,但我认为它更简洁,还可以让您在不同的控制器中使用相同名称的操作。因此,如果您使用不同的控制器添加更多菜单项,则并非所有名为 Index 的操作都会在菜单中突出显示。

          如果有人发现这种方法存在问题,请告诉我。

          【讨论】:

            【解决方案5】:

            你也可以尝试从其控制器名和视图名中检测当前选中的选项卡,然后添加类属性。

            public static string MenuActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName)
            {
                var htmlAttributes = new RouteValueDictionary();
            
                if (helper.ViewContext.Controller.GetType().Name.Equals(controllerName + "Controller", StringComparison.OrdinalIgnoreCase))
                {
                    htmlAttributes.Add("class", "current");
                }
            
                return helper.ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(), htmlAttributes);
            }
            

            【讨论】:

            • 如果您在返回 helper.ActionLink 时遇到错误,请将其添加到您的使用参考中:using System.Web.Mvc.Html;
            • 它应该返回一个 MvcHtmlString 而不是一个字符串
            【解决方案6】:

            这应该在客户端使用 jQuery,使用 Google 提供最新的 jQuery 库:

            <script src="http://www.google.com/jsapi" type="text/javascript" language="javascript"></script>
            <script type="text/javascript" language="javascript">google.load("jquery", "1");</script>  
            
            <script language="javascript" type="text/javascript">
                  $(document).ready(function(){
                      var str=location.href.toLowerCase(); 
                    $('#menucontainer ul#menu li a').each(function() {
                            if (str.indexOf(this.href.toLowerCase()) > -1) {
                                    $(this).attr("class","current"); //hightlight parent tab
                                 }  
                            });
                  });  
                </script>
            

            【讨论】:

            • @Brady 为什么不呢?看起来是一个可靠的解决方案。
            【解决方案7】:

            我给自己做了一个辅助方法来处理这类事情。在我的母版页后面的代码中(可以推送到扩展方法......可能是更好的方法),我输入了以下代码。

            protected string ActiveActionLinkHelper(string linkText, string actionName, string controlName, string activeClassName)
            {
                if (ViewContext.RouteData.Values["action"].ToString() == actionName && 
                        ViewContext.RouteData.Values["controller"].ToString() == controlName)
                    return Html.ActionLink(linkText, actionName, controlName, new { Class = activeClassName });
            
                return Html.ActionLink(linkText, actionName, controlName);
            }
            

            然后,我只是在我的页面中这样称呼它:

            <%= ActiveActionLinkHelper("Home", "Index", "Home", "selected")%>
            

            【讨论】:

            • 新的 { Class = "selected" } 应该是新的 { Class = activeClassName } ;P
            • 太棒了。我自己也在找这个。
            【解决方案8】:

            使用带有 Razor 视图的 MVC3,您可以这样实现:

            <ul id="menu">
             @if (ViewContext.RouteData.Values["action"].ToString() == "Index")
             {
             <li class="active">@Html.ActionLink("Home", "Index", "Home")</li>
             }
             else
             {
             <li>@Html.ActionLink("Home", "Index", "Home")</li>
             }
             @if (ViewContext.RouteData.Values["action"].ToString() == "About")
             {
             <li class="active">@Html.ActionLink("About", "About", "Home")</li>
             }
             else
             {
             <li>@Html.ActionLink("About", "About", "Home")</li>
             }
            </ul>
            

            然后应用您的“.active”类风格,例如:

            ul#menu li.active 
            {
             text-decoration:underline;
            }
            

            【讨论】:

              【解决方案9】:

              在 MVC 3 Razor View Engine 中,您可以这样做:

              @{string ctrName = ViewContext.RouteData.Values["controller"].ToString();}
              
              <div id="menucontainer">
                <ul id="menu"> 
                  <li @if(ctrName == "Home"){<text> class="active"</text>}>@ Html.ActionLink("Home",  "Index", "Home")</li>
                  <li @if(ctrName == "About"){<text> class="active"</text>}>@ Html.ActionLink("About Us", "About", "Home")</li>
                </ul>
              </div>
              

              当我有两个页面时,我的示例工作:Home/About 并且它的控制器具有相同的名称索引,所以我得到控制器名称来区分而不是行动。如果您想采取行动,只需替换为以下内容:

              @{string ctrName = ViewContext.RouteData.Values["action"].ToString();}
              

              【讨论】:

              • 也可能值得做类似的事情:@if(ctrName.ToLower() == "home") 来解释手动输入网址的任何人。否则,您会发现有人可以在 /home(而不是 /Home)访问您的页面,但看不到样式为活动的菜单。请注意,如果您确实使用“ToLower()”,请确保您的变量“ctrName”永远不能为空,否则您可能会引发异常。例如。定义“ctrName”,如:string ctrName = “”; if (ViewContext.RouteData.Values["action"] != null) { ctrName = ViewContext.RouteData.Values["action"].ToString(); }
              【解决方案10】:

              这是与当前 MVC4 版本兼容的版本。
              我已经将 Adam Carr 的代码重写为扩展方法。

              using System;
              using System.Web.Mvc;
              using System.Web.Mvc.Html;
              using System.Web.Routing;
              
              namespace MyApp.Web {
                  public static class HtmlHelpers {
                      /// <summary>
                      /// Returns an anchor element (a element) that contains the virtual path of the
                      /// specified action. If the controller name matches the active controller, the
                      /// css class 'current' will be applied.
                      /// </summary>
                      public static MvcHtmlString MenuActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName) {
                          var htmlAttributes = new RouteValueDictionary();
                          string name = helper.ViewContext.Controller.GetType().Name;
              
                          if (name.Equals(controllerName + "Controller", StringComparison.OrdinalIgnoreCase))
                              htmlAttributes.Add("class", "current");
              
                          return helper.ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(), htmlAttributes);
                      }
                  }
              }
              

              【讨论】:

              • helper.ActionLink 无法编译
              • @AndyBrudtkuhl 使用 System.Web.Mvc.Html;让它编译
              【解决方案11】:

              为了贡献我自己的答案(在 MVC4 中测试),我从其他答案中提取了一些最好的部分,修复了一些问题,并添加了一个帮助程序来处理不一定通过 Controller 和 Action 解决的 url(例如. 如果你有一个嵌入式 CMS 来处理一些页面链接等)

              代码也可以在 github 上分叉:https://gist.github.com/2851684

              /// 
              /// adds the active class if the link's action & controller matches current request
              /// 
              public static MvcHtmlString MenuActionLink(this HtmlHelper htmlHelper,
                  string linkText, string actionName, string controllerName,
                  object routeValues = null, object htmlAttributes = null,
                  string activeClassName = "active")
              {
                  IDictionary htmlAttributesDictionary =
                      HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
              
                  if (((string)htmlHelper.ViewContext.RouteData.Values["controller"])
                          .Equals(controllerName, StringComparison.OrdinalIgnoreCase) &&
                      ((string)htmlHelper.ViewContext.RouteData.Values["action"])
                          .Equals(actionName, StringComparison.OrdinalIgnoreCase))
                  {
                      // careful in case class already exists
                      htmlAttributesDictionary["class"] += " " + activeClassName;
                  }
              
                  return htmlHelper.ActionLink(linkText, actionName, controllerName,
                                                  new RouteValueDictionary(routeValues),
                                                  htmlAttributesDictionary);
              }
              
              /// 
              /// adds the active class if the link's path matches current request
              /// 
              public static MvcHtmlString MenuActionLink(this HtmlHelper htmlHelper,
                  string linkText, string path, object htmlAttributes = null,
                  string activeClassName = "active")
              {
                  IDictionary htmlAttributesDictionary =
                      HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
                  if (HttpContext.Current.Request.Path
                      .Equals(path, StringComparison.OrdinalIgnoreCase))
                  {
                      // careful in case class already exists
                      htmlAttributesDictionary["class"] += " " + activeClassName;
                  }
                  var tagBuilder = new TagBuilder("a")
                                          {
                                              InnerHtml = !string.IsNullOrEmpty(linkText)
                                                              ? HttpUtility.HtmlEncode(linkText)
                                                              : string.Empty
                                          };
                  tagBuilder.MergeAttributes(htmlAttributesDictionary);
                  tagBuilder.MergeAttribute("href", path);
                  return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal));
              }

              【讨论】:

                【解决方案12】:

                我想对我的布局有更多的控制,这就是我所做的。

                创建其他模型继承的 LayoutModel:

                public abstract class LayoutModel
                {
                    public CurrentPage CurrentPage { get; set; }
                }
                

                创建一个继承自 ActionFilterAttribute 的 LayoutAttribute,如下所示:

                public class LayoutAttribute : ActionFilterAttribute
                {
                    private CurrentPage _currentPage { get; set; }
                
                    public LayoutAttribute(
                        CurrentPage CurrentPage
                    ){
                        _currentPage = CurrentPage;
                    }
                
                    public override void OnActionExecuted(ActionExecutedContext filterContext)
                    {
                        var result = filterContext.Result as ViewResultBase;
                        if (result == null || result.Model == null || !(result.Model is LayoutModel)) return;
                
                        ((LayoutModel)result.Model).CurrentPage = _currentPage;
                    }
                }
                

                现在在 Action 或 Controller 级别上,我可以像这样设置当前页面(如果需要,还可以设置其他内容):

                [Layout(CurrentPage.Account)]
                public class MyController : Controller
                {
                
                }
                

                在我的布局视图中,我现在可以访问当前页面,以及我添加到 LayoutModel 的任何其他内容。

                【讨论】:

                  【解决方案13】:

                  将 MVC3 与 Razor 视图结合使用提供了另一种选择:

                  _Layout.cshtml:

                  <li class="@ViewBag.NavClassHome">@Html.ActionLink("Home", "Index", "Home")</li>
                  <li class="@ViewBag.NavClassAbout">@Html.ActionLink("Disclaimer", "About", "Home")</li>
                  

                  主控制器:

                  public ActionResult Index() {
                      ViewBag.NavClassHome = "active";
                      return View();
                  } 
                  
                  public ActionResult About() {
                      ViewBag.NavClassAbout = "active";
                      return View();
                  }
                  

                  如果您还想为回发保留它,您还必须在此处分配 ViewBag 值:

                  [HttpPost]
                  public ActionResult Index() {
                      ViewBag.NavClassHome = "active";
                      return View();
                  }
                  
                  [HttpPost]
                  public ActionResult About() {
                      ViewBag.NavClassAbout = "active";
                      return View();
                  }
                  

                  对我来说已经过测试并且工作正常,但是您的服务器端代码中会有一个 css 类名称。

                  【讨论】:

                  • 我在google搜索之前考虑过这个解决方案,代码有点多,但是对于我的小网站来说,这似乎是我最好的解决方案。我唯一的补充是创建 ViewBag.NavClass 并继续使用一个变量而不是 Home、About 等。
                  【解决方案14】:

                  一个老问题,但希望有人会觉得这很有帮助。

                  1. ViewBag中放一些你可以用来识别你的页面的东西,我用的是ViewgBag.PageName

                  例如,在 index.cshtml 中,放入类似

                  @{
                      ViewBag.PageName = "Index";
                  }
                  
                  1. 使用条件语句为每个链接项添加一个类,如果正在访问的页面具有所需的值,则返回 active,否则返回空字符串。详情请查看下方:

                  <li class="@((ViewBag.PageName == "Index") ? "active" : "")"><a href="@Url.Action("Index","Home")">Home</a></li>
                  <li class="@((ViewBag.PageName == "About") ? "active" : "")"><a href="@Url.Action("About","Home")">About</a></li>
                  <li class="@((ViewBag.PageName == "Contact") ? "active" : "")"><a href="@Url.Action("Contact","Home")">Contact</a></li>

                  我不只是测试它,我在我的项目中使用这种方法

                  【讨论】:

                  • 这是我发现唯一有效的方法。谢谢你。我正在使用 MVC4。
                  【解决方案15】:

                  希望这会有所帮助。

                  <ul>
                      <li class="@(ViewContext.RouteData.Values["Controller"].ToString() == "Home" ? "active" : "")">
                          <a asp-area="" asp-controller="Home" asp-action="Index"><i class="icon fa fa-home"></i><span>Home</span>
                          </a>
                      </li>
                  </ul>
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多