54 votes

Meilleur moyen d'obtenir un lien vers une page active dans MVC 3 Razor

Lorsque je veux qu'un lien de menu spécifique soit actif sur une page donnée, j'utilise cette approche dans Razor :

Sur la maquette principale, j'ai ces contrôles :

var active = ViewBag.Active;
const string ACTIVE_CLASS = "current";

if (active == "home")
{
    ViewBag.ActiveHome = ACTIVE_CLASS;
}
if (active == "products")
{
    ViewBag.ActiveProducts = ACTIVE_CLASS;
}

etc.

Le menu html sur la maquette principale :

<ul>
<li class="@ViewBag.ActiveHome"><a href="stackoverflow.com/">Home</a></li>
<li class="@ViewBag.ActiveProducts"><a href="@Url.Action("index", "products")">Products</a></li>
</ul>

Lorsque vous spécifiez la page de mise en page à utiliser sur une autre vue :

@{
    ViewBag.Active = "home";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

Existe-t-il une meilleure approche pour séparer les liens actifs que celle que j'utilise actuellement ?

126voto

Darin Dimitrov Points 528142

Une meilleure approche consiste à utiliser une aide HTML :

using System.Web.Mvc; 
using System.Web.Mvc.Html;

public static class MenuExtensions
{
    public static MvcHtmlString MenuItem(
        this HtmlHelper htmlHelper, 
        string text,
        string action, 
        string controller
    )
    {
        var li = new TagBuilder("li");
        var routeData = htmlHelper.ViewContext.RouteData;
        var currentAction = routeData.GetRequiredString("action");
        var currentController = routeData.GetRequiredString("controller");
        if (string.Equals(currentAction, action, StringComparison.OrdinalIgnoreCase) &&
            string.Equals(currentController, controller, StringComparison.OrdinalIgnoreCase))
        {
            li.AddCssClass("active");
        }
        li.InnerHtml = htmlHelper.ActionLink(text, action, controller).ToHtmlString();
        return MvcHtmlString.Create(li.ToString());
    }
}

et ensuite :

<ul>
    @Html.MenuItem("Home", "Home", "Home")
    @Html.MenuItem("Products", "Index", "Products")
</ul>

Pour que ce qui précède fonctionne, il faut que vos vues reconnaissent votre extension : Dans Web.config dans le dossier Views, ajoutez <add namespace="yourNamespacehere.Helpers" /> à l'intérieur de la balise namespaces. Ensuite, construisez votre projet, fermez et rouvrez la vue à laquelle vous ajoutez cet élément.

puis, en fonction de l'action et du contrôleur en cours, l'assistant ajoutera ou non l'attribut active lors de la génération de l'ancre.

6voto

dperish Points 53

En développant l'exemple de Darin, voici la classe complète qui ajoute des paramètres optionnels supplémentaires pour RouteValues et HtmlAttributes sur l'aide. En fait, elle se comporte exactement comme l'ActionLink de base.

using System;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace MYNAMESPACE.Helpers {
    public static class MenuExtensions {
        public static MvcHtmlString MenuItem(this HtmlHelper htmlHelper,
                                             string text, string action,
                                             string controller,
                                             object routeValues = null,
                                             object htmlAttributes = null) {
            var li = new TagBuilder("li");
            var routeData = htmlHelper.ViewContext.RouteData;
            var currentAction = routeData.GetRequiredString("action");
            var currentController = routeData.GetRequiredString("controller");
            if (string.Equals(currentAction,
                              action,
                              StringComparison.OrdinalIgnoreCase) &&
                string.Equals(currentController,
                              controller,
                              StringComparison.OrdinalIgnoreCase)) {
                li.AddCssClass("active");
            }
            if (routeValues != null) {
                li.InnerHtml = (htmlAttributes != null)
                    ? htmlHelper.ActionLink(text,
                                            action,
                                            controller,
                                            routeValues,
                                            htmlAttributes).ToHtmlString()
                    : htmlHelper.ActionLink(text, 
                                            action, 
                                            controller, 
                                            routeValues).ToHtmlString();
            }
            else {
                li.InnerHtml = htmlHelper.ActionLink(text, 
                                                     action, 
                                                     controller).ToHtmlString();
            }
            return MvcHtmlString.Create(li.ToString());
        }
    }
}

Et dans le web.config du dossier View :

<system.web.webPages.razor>
  <host ... />
  <pages ... >
    <namespaces>
      ...

      ...
      <add namespace="MYNAMESPACE.Helpers" />
    </namespaces>
  </pages>
</system.web.webPages.razor>

2voto

edocetirwi Points 305

Utilisez cet InnerHtml si vous souhaitez inclure un formatage HTML dans votre texte ;

li.InnerHtml = "<a href=\"" + new UrlHelper(htmlHelper.ViewContext.RequestContext).Action(action, controller).ToString() + "\">" + text + "</a>";

Le texte pourrait être "<b>Bold</b> Normal" ;

2voto

Party Ark Points 577

Mis à jour pour RC2 - Pour ceux qui se demandent comment faire cela en MVC6 / Asp.Net 5 - similaire mais subtilement différent. Il n'y a plus de MvcHtmlString et le RouteData fonctionne de manière complètement différente. En outre, l'objet de contexte devrait maintenant être IHtmlContent plutôt que HtmlHelper .

using System;
using Microsoft.AspNet.Mvc.Rendering;

public static class MenuExtensions
{
    public static IHtmlContent MenuItem(
        this IHtmlHelper htmlHelper,
        string text,
        string action,
        string controller
    )
    {

        var li = new TagBuilder("li") { TagRenderMode = TagRenderMode.Normal };
        var routeData = htmlHelper.ViewContext.RouteData;
        var currentAction = routeData.Values["action"].ToString();
        var currentController = routeData.Values["controller"].ToString();

        if (string.Equals(currentAction, action, StringComparison.OrdinalIgnoreCase) &&
            string.Equals(currentController, controller, StringComparison.OrdinalIgnoreCase))
        {
            li.AddCssClass("active");
        }

        li.InnerHtml.AppendHtml(htmlHelper.ActionLink(text, action, controller));

        return li;

    }
}

0voto

ggoodro Points 1

Ce code a très bien fonctionné pour moi, même sur un nouveau projet Visual Studio 2013 MVC5/Bootstrap. Notez également que vous pouvez modifier la ligne li.AddCssClass("active") ; pour qu'elle pointe vers une classe personnalisée si vous souhaitez ne pas utiliser la classe "active" de Bootstrap. J'en ai ajouté une appelée "activemenu" dans le fichier Site.css du projet et j'y ai apporté toutes les modifications spécifiques au style de la barre de navigation que je souhaitais.

La ligne dans le code ci-dessus a juste été changée en ceci pour que tout fonctionne :

li.AddCssClass("activemenu");

Dans Site.css, j'ai ajouté une classe simple à mon intention :

.activemenu {
    text-decoration: underline;
}

Vous pouvez également modifier la couleur de l'arrière-plan et/ou la bordure, etc...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X