Jak dodać „aktywną” klasę do Html.ActionLink w ASP.NET MVC

128

Próbuję dodać „aktywną” klasę do mojego paska nawigacyjnego bootstrap w MVC, ale poniższe nie pokazują aktywnej klasy po napisaniu w ten sposób:

<ul class="nav navbar-nav">
  <li>@Html.ActionLink("Home", "Index", "Home", null, new {@class="active"})</li>
  <li>@Html.ActionLink("About", "About", "Home")</li>
  <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>

Powoduje to coś, co wygląda na poprawnie sformatowaną klasę, ale nie działa:

<a class="active" href="/">Home</a>

W dokumentacji Bootstrap stwierdza się, że znaczniki „a” nie powinny być używane na pasku nawigacyjnym, ale powyższe jest moim zdaniem prawidłowym sposobem dodawania klasy do Html.ActionLink. Czy jest inny (uporządkowany) sposób, w jaki mogę to zrobić?

Gillespie
źródło
3
kod, który rozwiązuje, zawiera class = active. Czy nie tego chcesz w swoim pytaniu? Sposób, w jaki masz swój kod, to sposób, w jaki dodaję zajęcia
Matt Bodily,
nie jesteś zbyt jasny !!! Nie rozumiem dokładnie, w czym problem z dodaniem go do nawigacji, chociaż mam przeczucie, że tak powinno być dobrze
JC Lizard
1
Jaki jest problem z rozwiązanym kodem? Czego się spodziewasz? Czy możesz być bardziej szczegółowy niż „nie działa”? Twoje pytanie nie jest wystarczająco jasne, aby teraz Ci pomóc.
dom
Edytowano! przepraszam, chodziło mi o to, że „aktywne” zajęcia w ogóle się nie wyświetlają
Gillespie
2
Właśnie rzuciłem okiem na dokumentację Bootstrap i myślę, że musisz dodać activeklasę do lielementu, a nie a. Zobacz pierwszy przykład tutaj: getbootstrap.com/components/#navbar
dom

Odpowiedzi:

300

W Bootstrap activeklasę należy zastosować do <li>elementu, a nie do <a>. Zobacz pierwszy przykład tutaj: http://getbootstrap.com/components/#navbar

Sposób obsługi stylu interfejsu użytkownika na podstawie tego, co jest aktywne lub nie, nie ma nic wspólnego z ActionLinkpomocnikiem ASP.NET MVC . Jest to właściwe rozwiązanie, aby śledzić sposób budowy frameworka Bootstrap.

<ul class="nav navbar-nav">
    <li class="active">@Html.ActionLink("Home", "Index", "Home")</li>
    <li>@Html.ActionLink("About", "About", "Home")</li>
    <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>

Edytować:

Ponieważ najprawdopodobniej będziesz ponownie używać menu na wielu stronach, rozsądnie byłoby mieć sposób na automatyczne zastosowanie tej wybranej klasy na podstawie bieżącej strony, zamiast wielokrotnego kopiowania menu i robienia tego ręcznie.

Najłatwiej jest po prostu użyć wartości zawartych w ViewContext.RouteData, a mianowicie wartości Actioni Controller. Moglibyśmy zbudować na tym, co obecnie masz, w następujący sposób:

<ul class="nav navbar-nav">
    <li class="@(ViewContext.RouteData.Values["Action"].ToString() == "Index" ? "active" : "")">@Html.ActionLink("Home", "Index", "Home")</li>
    <li class="@(ViewContext.RouteData.Values["Action"].ToString() == "About" ? "active" : "")">@Html.ActionLink("About", "About", "Home")</li>
    <li class="@(ViewContext.RouteData.Values["Action"].ToString() == "Contact" ? "active" : "")">@Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>

Nie jest ładna w kodzie, ale wykona zadanie i pozwoli ci wyodrębnić menu do częściowego widoku, jeśli chcesz. Istnieją sposoby, aby to zrobić w znacznie czystszy sposób, ale skoro dopiero zaczynasz, zostawię to na tym. Powodzenia w nauce ASP.NET MVC!


Późna edycja:

Wydaje się, że to pytanie generuje trochę ruchu, więc pomyślałem, że dorzuciłem bardziej eleganckie rozwiązanie, używając HtmlHelperrozszerzenia.

Edycja 24.03.2015: Musiałem przepisać tę metodę, aby umożliwić wiele akcji i kontrolerów wyzwalających wybrane zachowanie, a także obsługę wywołania metody z częściowego widoku akcji podrzędnej, pomyślałem, że udostępnię aktualizację!

public static string IsSelected(this HtmlHelper html, string controllers = "", string actions = "", string cssClass = "selected")
{
    ViewContext viewContext = html.ViewContext;
    bool isChildAction = viewContext.Controller.ControllerContext.IsChildAction;

    if (isChildAction)
        viewContext = html.ViewContext.ParentActionViewContext;

    RouteValueDictionary routeValues = viewContext.RouteData.Values;
    string currentAction = routeValues["action"].ToString();
    string currentController = routeValues["controller"].ToString();

    if (String.IsNullOrEmpty(actions))
        actions = currentAction;

    if (String.IsNullOrEmpty(controllers))
        controllers = currentController;

    string[] acceptedActions = actions.Trim().Split(',').Distinct().ToArray();
    string[] acceptedControllers = controllers.Trim().Split(',').Distinct().ToArray();

    return acceptedActions.Contains(currentAction) && acceptedControllers.Contains(currentController) ?
        cssClass : String.Empty;
}

Współpracuje z .NET Core:

public static string IsSelected(this IHtmlHelper htmlHelper, string controllers, string actions, string cssClass = "selected")
{
    string currentAction = htmlHelper.ViewContext.RouteData.Values["action"] as string;
    string currentController = htmlHelper.ViewContext.RouteData.Values["controller"] as string;

    IEnumerable<string> acceptedActions = (actions ?? currentAction).Split(',');
    IEnumerable<string> acceptedControllers = (controllers ?? currentController).Split(',');

    return acceptedActions.Contains(currentAction) && acceptedControllers.Contains(currentController) ?
        cssClass : String.Empty;
}

Przykładowe użycie:

<ul>
    <li class="@Html.IsSelected(actions: "Home", controllers: "Default")">
        <a href="@Url.Action("Home", "Default")">Home</a>
    </li>
    <li class="@Html.IsSelected(actions: "List,Detail", controllers: "Default")">
        <a href="@Url.Action("List", "Default")">List</a>
    </li>
</ul>
dom
źródło
Dzięki, jak wspomniałem, tak to miałem na początku, ale miałem nadzieję, że w całym kodzie będę mieć jedną metodę zmiany „aktywnej” klasy między wybranymi obiektami.
Gillespie,
2
@Gillespie nie martw się! Zredagowałem moją odpowiedź, aby dodać trochę informacji, które mogą ci pomóc.
dom
5
Jeśli chodzi o pierwszą edycję, to działało tylko dla strony indeksu. Wydaje się, że powodem jest to, że podczas porównywania nie powiodło się, ponieważ ViewContext.RouteData.Values ​​["Action"] nie zwraca obiektu typu String (nadal nie wiem, jak to działało na pierwszej stronie ...) . Kiedy zmieniłem wszystkie ViewContext.RouteData.Values ​​["Action"] na ViewContext.RouteData.Values ​​["Action"]. ToString (), działało to dla wszystkich stron. Może zechcesz to dodać do swojego rozwiązania na wypadek, gdyby ktoś napotkał ten sam problem.
user3281466
2
@LonelyPixel może ta odpowiedź Ci odpowiada?
dom
1
Dla użytkowników VB.NET: <li class = "@ (If (ViewContext.RouteData.Values ​​(" Action "). ToString () =" Index "," active "," "))"> @ Html.ActionLink (" Home ”,„ Index ”,„ Home ”) </li>
Andrea Antonangeli
28

Rozbudowa:

public static MvcHtmlString LiActionLink(this HtmlHelper html, string text, string action, string controller)
{
    var context = html.ViewContext;
    if (context.Controller.ControllerContext.IsChildAction)
        context = html.ViewContext.ParentActionViewContext;
    var routeValues = context.RouteData.Values;
    var currentAction = routeValues["action"].ToString();
    var currentController = routeValues["controller"].ToString();

    var str = String.Format("<li role=\"presentation\"{0}>{1}</li>",
        currentAction.Equals(action, StringComparison.InvariantCulture) &&
        currentController.Equals(controller, StringComparison.InvariantCulture) ?
        " class=\"active\"" :
        String.Empty, html.ActionLink(text, action, controller).ToHtmlString()
    );
    return new MvcHtmlString(str);
}

Stosowanie:

<ul class="nav navbar-nav">
    @Html.LiActionLink("About", "About", "Home")
    @Html.LiActionLink("Contact", "Contact", "Home")
</ul>
Prof
źródło
1
To pięknie wydajne rozszerzenie. Działa doskonale i jest dość elegancki. Czapki z głów dla ciebie, prof! Moje jedyne zastrzeżenie dotyczy opcji role="presentation", która wcale nie jest odpowiednia dla podstawowego menu nawigacyjnego ( john.foliot.ca/aria-hidden/#pr ). Lista nieuporządkowana jest w dużym stopniu odpowiednim kontenerem dla powiązanego zestawu linków, ponieważ grupuje je razem w logiczny sposób.
René Kåbis
@ René Kåbis it for bootstrap naigation getbootstrap.com/components/#nav-tabs
Prof
1
To nie działa, gdy użytkownik ręcznie wprowadza adres URL małymi lub dużymi literami, np. „ domain.com/Login ” i „ domain.com/LOGIN ”. Dla roztworu var str = String.Format("<li role=\"presentation\"{0}>{1}</li>", currentAction.toLower().Equals(action.toLower(), StringComparison.InvariantCulture) && currentController.toLower().Equals(controller.toLower(), StringComparison.InvariantCulture) ? " class=\"active\"" : String.Empty, html.ActionLink(text, action, controller).ToHtmlString() );
sky91
20

Udało mi się to zrobić, dodając parametr widoku worka w asp.net mvc. Oto co zrobiłem

Dodano ViewBag.Current = "Scheduler";parametr podobny na każdej stronie

Na stronie układu

<ul class="nav navbar-nav">
     <li class="@(ViewBag.Current == "Scheduler" ? "active" : "") "><a href="@Url.Action("Index","Scheduler")" target="_self">Scheduler</a></li>
 </ul>

To rozwiązało mój problem.

Damith
źródło
Proste i łatwe rozwiązanie.
japes Sophey
13

Może się trochę spóźnić. Ale mam nadzieję, że to pomoże.

public static class Utilities
{
    public static string IsActive(this HtmlHelper html, 
                                  string control,
                                  string action)
    {
        var routeData = html.ViewContext.RouteData;

        var routeAction = (string)routeData.Values["action"];
        var routeControl = (string)routeData.Values["controller"];

        // both must match
        var returnActive = control == routeControl &&
                           action == routeAction;

        return returnActive ? "active" : "";
    }
}

I użycie w następujący sposób:

<div class="navbar-collapse collapse">
    <ul class="nav navbar-nav">
        <li class='@Html.IsActive("Home", "Index")'>
            @Html.ActionLink("Home", "Index", "Home")
        </li>
        <li class='@Html.IsActive("Home", "About")'>
            @Html.ActionLink("About", "About", "Home")
        </li>
        <li class='@Html.IsActive("Home", "Contact")'>
            @Html.ActionLink("Contact", "Contact", "Home")
        </li>
    </ul>
</div>

Mam odniesienie z http://www.codingeverything.com/2014/05/mvcbootstrapactivenavbar.html

Virag Dilip Desai
źródło
1
Bardzo proste i eleganckie rozwiązanie. Zrobiłem trochę ulepszeń tutaj gist.github.com/jean-rakotozafy/ ... aby podświetlić menu dla wszystkich akcji kontrolera.
Zan RAKOTO
5

Wiem, że to stare pytanie, ale chciałbym tylko dodać tutaj swój głos. Uważam, że dobrym pomysłem jest pozostawienie wiedzy o tym, czy łącze jest aktywne, czy nie, kontrolerowi widoku.

Po prostu ustawiłbym unikalną wartość dla każdego widoku w akcji kontrolera. Na przykład, gdybym chciał, aby link do strony głównej był aktywny, zrobiłbym coś takiego:

public ActionResult Index()
{            
    ViewBag.Title = "Home";
    ViewBag.Home = "class = active";
    return View();
}

Wtedy moim zdaniem napiszę coś takiego:

<li @ViewBag.Home>@Html.ActionLink("Home", "Index", "Home", null, new { title = "Go home" })</li>

Kiedy przejdziesz do innej strony, powiedz Programy, ViewBag.Home nie istnieje (zamiast tego ViewBag.Programs); dlatego nic nie jest renderowane, nawet class = "". Myślę, że jest to czystsze zarówno pod względem konserwacji, jak i czystości. Zawsze staram się nie zwracać uwagi na logikę tak bardzo, jak tylko mogę.

mapussah
źródło
4

Biorąc pod uwagę to, co opublikował Damith, lubię myśleć, że możesz zakwalifikować się jako aktywny przez Viewbag.Title (najlepszą praktyką jest umieszczenie tego na stronach z treścią, co pozwoli Twojej _Layout.cshtmlstronie na przechowywanie pasków linków). Pamiętaj również, że jeśli używasz elementów podmenu, to również działa dobrze:

<li class="has-sub @(ViewBag.Title == "Dashboard 1" || ViewBag.Title == "Dashboard 2" ? "active" : "" )">
    <a href="javascript:;">
        <b class="caret"></b>
        <i class="fa fa-th-large"></i>
        <span>Dashboard</span>
    </a>
    <ul class="sub-menu">
        <li class="@(ViewBag.Title == "Dashboard 1" ? "active" : "")"><a href="index.html">Dashboard v1</a></li>
        <li class="@(ViewBag.Title == "Dashboard 2" ? "active" : "")"><a href="index_v2.html">Dashboard v2</a></li>
    </ul>
</li>
Rafe Smith
źródło
Czysto i wydajnie!
mggluscevic
3

Zmodyfikowałem odpowiedź „nie ładna” dom i uczyniłem ją brzydszą. Czasami dwa kontrolery mają sprzeczne nazwy akcji (np. Indeks), więc robię to:

<ul class="nav navbar-nav">
  <li class="@(ViewContext.RouteData.Values["Controller"].ToString() + ViewContext.RouteData.Values["Action"].ToString() == "HomeIndex" ? "active" : "")">@Html.ActionLink("Home", "Index", "Home")</li>
  <li class="@(ViewContext.RouteData.Values["Controller"].ToString() + ViewContext.RouteData.Values["Action"].ToString() == "AboutIndex" ? "active" : "")">@Html.ActionLink("About", "Index", "About")</li>
  <li class="@(ViewContext.RouteData.Values["Controller"].ToString() + ViewContext.RouteData.Values["Action"].ToString() == "ContactHome" ? "active" : "")">@Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>
DeveloperDan
źródło
3

Możesz spróbować tego: w moim przypadku ładuję menu z bazy danych w oparciu o dostęp oparty na rolach, napisz kod w każdym widoku, które menu chcesz aktywować na podstawie swojego widoku.

<script type="text/javascript">
        $(document).ready(function () {         
            $('li.active active-menu').removeClass('active active-menu');
            $('a[href="https://stackoverflow.com/MgtCustomer/Index"]').closest('li').addClass('active active-menu');
        });
</script>
atik sarker
źródło
3

To rozwiązanie jest proste w przypadku Asp.net MCV 5.

  1. Utwórz na przykład klasę statyczną Utilitarios.cs.

  2. Wewnątrz klasy utwórz metodę statyczną:

    public static string IsLinkActive(this UrlHelper url, string action, string controller)
    {
        if (url.RequestContext.RouteData.Values["controller"].ToString() == controller &&
            url.RequestContext.RouteData.Values["action"].ToString() == action)
        {
            return "active";
        }
    
        return "";
    }
  3. zadzwoń w ten sposób

    <ul class="sidebar-menu" data-widget="tree">
        <li class="header">HEADER</li>
        <li class="@Url.IsLinkActive("Index", "Home")">
            <a href="@Url.Action("Index", "Home")"><i class="fa fa-link"></i> <span>Home</span></a>
        </li>
        <li class="@Url.IsLinkActive("About", "Home")">
            <a href="@Url.Action("About", "Home")"><i class="fa fa-link"></i><span>About</span></a>
        </li>
    </ul>
Deyvi Javier Saavedra Garcia
źródło
3

ASP.NET Core i Bootstrap 4

Najbardziej aktualna odpowiedź

Mam ponownego pracował @crush „s schludny rozwiązanie dla zaktualizowanej, ASP.NET podstawowej i Bootstrap 4 kompatybilnym sposobem rozwiązania tego problemu w oparciu o IHtmlHelpermetodę rozszerzenia:

public static class LinkExtensions
{
    public static IHtmlContent ActiveActionLink(this IHtmlHelper html, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
    {
        return ActiveActionLink(html, linkText, actionName, controllerName, new RouteValueDictionary(routeValues), HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    }

    public static IHtmlContent ActiveActionLink(this IHtmlHelper html, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes)
    {
        var routeData = html.ViewContext.RouteData;
        var routeAction = (string)routeData.Values["action"];
        var routeController = (string)routeData.Values["controller"];

        var active = controllerName.Equals(routeController) && actionName.Equals(routeAction);

        using (var writer = new StringWriter())
        {
            writer.WriteLine($"<li class='nav-item {(active ? "active" : "")}'>");
            html.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes).WriteTo(writer, HtmlEncoder.Default);
            writer.WriteLine("</li>");
            return new HtmlString(writer.ToString());
        }
    }
}

Stosowanie

<nav class="navbar">
    <div class="collapse navbar-collapse">
        <ul class="navbar-nav">
            @Html.ActiveActionLink("Home", "Index", "Home", null, new { @class = "nav-link" })
            @Html.ActiveActionLink("About", "About", "Home", null, new { @class = "nav-link" })
            @Html.ActiveActionLink("Contact", "Contact", "TimeTracking", null, new { @class = "nav-link" })
        </ul>
    </div>
</nav>
WoIIe
źródło
1
Bardzo dobre rozwiązanie. W twoim przykładzie, jak mogę ustawić „Kontakt” jako domyślnie aktywny?
Dr Suman Kabir
1
Dziękuję Ci. Dobrą rzeczą jest to, że nie musisz ustawiać aktywnego elementu. Jak tylko strona "Kontakt" zostanie odwiedzona, ActiveActionLink()aktywna klasa zostanie skierowana do <li>:-)
WoIIe
gdzie sprawdzasz obszar?
Mohammad
@Mohammad Nie rozumiem. Czy mógłbyś rozwinąć swoje pytanie?
WoIIe
w klasie LinkExtensions nie otrzymałeś obszaru z routeData!
Mohammad
3

Łatwy ASP.NET Core 3.0 i TagHelpers

[HtmlTargetElement("li", Attributes = "active-when")]
public class LiTagHelper : TagHelper
{
    public string ActiveWhen { get; set; }

    [ViewContext]
    [HtmlAttributeNotBound]
    public ViewContext ViewContextData { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        if (ActiveWhen == null)
            return;

        var targetController = ActiveWhen.Split("/")[1];
        var targetAction = ActiveWhen.Split("/")[2];

        var currentController = ViewContextData.RouteData.Values["controller"].ToString();
        var currentAction = ViewContextData.RouteData.Values["action"].ToString();

        if (currentController.Equals(targetController) && currentAction.Equals(targetAction))
        {
            if (output.Attributes.ContainsName("class"))
            {
                output.Attributes.SetAttribute("class", $"{output.Attributes["class"].Value} active");
            }
            else
            {
                output.Attributes.SetAttribute("class", "active");
            }
        }
    }
}

Uwzględnij w swoim _ViewImports.cs:

@addTagHelper *, YourAssemblyName

Stosowanie:

 <li active-when="/Home/Index">
Guilherme V.
źródło
2

Dodaj „.ToString”, aby usprawnić porównywanie w ASP.NET MVC

<ul class="nav navbar-nav">
<li class="@(ViewContext.RouteData.Values["Action"].ToString() == "Index" ? "active" : "")">@Html.ActionLink("Home", "Index", "Home")</li>
<li class="@(ViewContext.RouteData.Values["Action"].ToString() == "About" ? "active" : "")">@Html.ActionLink("About", "About", "Home")</li>
<li class="@(ViewContext.RouteData.Values["Action"].ToString() == "Contact" ? "active" : "")">@Html.ActionLink("Contact", "Contact", "Home")</li>

-


źródło
To nie zadziała, jeśli istnieją takie same nazwy akcji w dwóch różnych kontrolerach. Przetestuj za pomocą kontrolerów Strona główna i Informacje z akcją Indeks.
hyde
Możesz zdefiniować serwer funkcji na maszynce jak: @functions { public bool IsActive(string action, string controller) { var actionRoute = ViewContext.RouteData.Values["Action"].ToString(); var controlRoute = ViewContext.RouteData.Values["Controller"].ToString(); return controller.Equals(controlRoute)&& actionRoute.Equals(actionRoute); } }i użycie: <li class="@(IsActive("Index", "Home")? "active": "")">
Hua Trung
2

Możemy również tworzyć UrlHelperz RequestContext, które możemy pobrać z samego MvcHandler. Dlatego wierzę, że ktoś, kto chce zachować tę logikę w szablonach Razor, byłby pomocny:

  1. W katalogu głównym projektu utwórz folder o nazwie AppCode.
  2. Utwórz tam plik o nazwie HtmlHelpers.cshtml
  3. Utwórz pomocnika w tym miejscu:

    @helper MenuItem(string action, string controller)
    {
         var mvcHandler = Context.CurrentHandler as MvcHandler;
         if (mvcHandler != null)
         {
             var url = new UrlHelper(mvcHandler.RequestContext);
             var routeData = mvcHandler.RequestContext.RouteData;
             var currentAction = routeData.Values["action"].ToString();
             var currentController = routeData.Values["controller"].ToString();
             var isCurrent = string.Equals(currentAction, action, StringComparison.InvariantCultureIgnoreCase) &&
                             string.Equals(currentController, controller, StringComparison.InvariantCultureIgnoreCase);
    
            <div class="@(isCurrent ? "active" : "")">
                <div>@url.Action(action, controller)</div>
            </div>
         }   
    }
  4. Następnie możemy użyć na naszych widokach w następujący sposób:

    @HtmlHelpers.MenuItem("Default", "Home")

Mam nadzieję, że komuś to pomoże.

Oleksii Aza
źródło
To jest naprawdę fajne. Czy są tu jakieś wady wydajności?
zmiażdżyć
@crush, nie mierzyłem tego, ale spodziewałbym się, że będzie to miało niewielki wpływ na wydajność, ponieważ myślę, że framework używa podobnej logiki do tworzenia UrlHelper, którą masz Controller.Url. Jedyną rzeczą jest to, że prawdopodobnie nie chcemy wykonywać tworzenia UrlHelperdla każdego wywołania, MenuItemwięc możemy zastosować tick zapisania go w HttpContext Items po pierwszym wywołaniu helpera, więc robimy to tylko raz na żądanie.
Oleksii Aza
2

jest możliwe dzięki funkcji lambda

@{
string controllerAction = ViewContext.RouteData.Values["Controller"].ToString() + ViewContext.RouteData.Values["Action"].ToString();
    Func<string, string> IsSelected= x => x==controllerAction ? "active" : "";
}

następnie użycie

 @Html.ActionLink("Inicio", "Index", "Home", new { area = "" }, new { @class = IsSelected("HomeIndex")})
henoc salinas
źródło
2

Stworzyłem HtmlHelperrozszerzenie, które dodaje ActiveActionLinkmetodę dla tych z Was, którzy chcą dodać „aktywną” klasę do samego łącza, a nie do <li>otaczającego go łącza.


public static class LinkExtensions
{
    public static MvcHtmlString ActiveActionLink(this HtmlHelper html, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
    {
        return ActiveActionLink(html, linkText, actionName, controllerName, new RouteValueDictionary(routeValues), HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    }

    public static MvcHtmlString ActiveActionLink(this HtmlHelper html, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes)
    {
        const string activeClassName = "active";

        var routeData = html.ViewContext.RouteData;

        var routeAction = (string)routeData.Values["action"];
        var routeController = (string)routeData.Values["controller"];

        var active = controllerName.Equals(routeController) && actionName.Equals(routeAction);

        if (active)
        {
            var @class = (string)htmlAttributes["class"];

            htmlAttributes["class"] = string.IsNullOrEmpty(@class)
                ? activeClassName
                : @class + " active";
        }

        var link = html.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes);

        return link;
    }
}

Sposób użycia jest następujący:

@Html.ActiveActionLink("Home", "Index", "Home", new { area = "" }, new { @class = "nav-item nav-link" })
zmiażdżyć
źródło
1

odpowiedź od @dombenoit działa. Chociaż wprowadza pewien kod do utrzymania. Sprawdź tę składnię:

using (var nav = Html.Bootstrap().Begin(new Nav().Style(NavType.NavBar).SetLinksActiveByControllerAndAction()))
{
    @nav.ActionLink("Link 1", "action1")
    @nav.ActionLink("Link 2", "action2")
    @nav.Link("External Link", "#")
}

Zwróć uwagę na użycie .SetLinksActiveByControllerAndAction()metody.

Jeśli zastanawiasz się, co sprawia, że ​​ta składnia jest możliwa, sprawdź TwitterBootstrapMVC

Dmitry Efimenko
źródło
Dzięki za odpowiedź Dmitry, testuję TwitterBootstrapMVC
Gillespie
1

Szukałem też rozwiązania, a jQuery bardzo mi pomogło. Najpierw musisz podać identyfikatory swoim <li>elementom.

<li id="listguides"><a href='/Guides/List'>List Guides</a></li>

Po wykonaniu tej czynności na swojej stronie układu możesz powiedzieć jQuery, który <li>element powinien być „wybrany” w Twoim widoku.

@section Scripts{
    <script type="text/javascript">
        $('#listguides').addClass('selected');
    </script>
}

Uwaga: aby dodać tę sekcję, musisz mieć @RenderSection("scripts", required: false)na swojej stronie układu, tuż przed </body>tagiem.

Doğa Gençer
źródło
1

Chciałbym zaproponować takie rozwiązanie, które jest oparte na pierwszej części odpowiedzi Doma.

Najpierw definiujemy dwie zmienne, „akcja” i „kontroler”, i używamy ich do określenia aktywnego łącza:

{ string controller = ViewContext.RouteData.Values["Controller"].ToString();
string action = ViewContext.RouteData.Values["Action"].ToString();}

I wtedy:

<ul class="nav navbar-nav">
    <li class="@((controller == "Home" && action == "Index") ? "active" : "")">@Html.ActionLink("Home", "Index", "Home")</li>
    <li class="@((controller == "Home" && action == "About") ? "active" : "")">@Html.ActionLink("About", "About", "Home")</li>
    <li class="@((controller == "Home" && action == "Contact") ? "active" : "")">@Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>

Teraz wygląda ładniej i nie ma potrzeby stosowania bardziej skomplikowanych rozwiązań.

Osama
źródło
0

jeśli w ogóle się nie wyświetla, powodem jest to, że potrzebujesz dwóch znaków @:

@@class

ALE wydaje mi się, że możesz potrzebować aktywnej klasy w tagu „li”, a nie w tagu „a”. zgodnie z dokumentacją bootstrap ( http://getbootstrap.com/components/#navbar-default ):

<ul class="nav navbar-nav">
  <li class="active"><a href="#">Home</a></li>
  <li><a href="#">Profile</a></li>
  <li><a href="#">Messages</a></li>
</ul>

dlatego twój kod będzie wyglądał następująco:

<ul class="nav navbar-nav">
  <li class="active">@Html.ActionLink("Home", "Index", "Home", null)</li>
  <li>@Html.ActionLink("About", "About", "Home")</li>
  <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>
JC Lizard
źródło
Klasa @@ zgłasza błąd, pierwotnie miałem Twój przykład kodu powyżej, który działa, ale nie jest dokładnie taki, jak „powinno” być Actionlinks?
Gillespie,
edytowane. @@ nie jest wymagane. musisz umieścić klasę na EA
JC Lizard
0

Mam nadzieję, że to pomoże. Poniżej przedstawiamy, jak może wyglądać Twoje menu:

 <ul class="nav navbar-nav">
   <li><a href="@Url.Action("Index", "Home")" class="@IsMenuSelected("Index","Home", "active")">Home</a></li>
   <li><a href="@Url.Action("About", "Home")" class="@IsMenuSelected("About", "Home", "active")">About</a></li>
   <li><a href="@Url.Action("Contact", "Home")" class="@IsMenuSelected("Contact", "Home", "active")">Contact</a></li>
 </ul>

I dodaj poniżej funkcję pomocniczą MVC poniżej tagu html.

 @helper IsMenuSelected(string actionName, string controllerName, string className)
    {
            var conname = ViewContext.RouteData.Values["controller"].ToString().ToLower();
            var actname = ViewContext.RouteData.Values["action"].ToString().ToLower();

            if (conname == controllerName.ToLower() && actname == actionName.ToLower())
        {
            @className;
        }
    }

Skierowałem odpowiedź z http://questionbox.in/add-active-class-menu-link-html-actionlink-asp-net-mvc/

Ganesh Todkar
źródło
0

Zdałem sobie sprawę, że ten problem był częstym problemem dla niektórych z nas, więc opublikowałem własne rozwiązanie przy użyciu pakietu nuget. Poniżej możesz zobaczyć, jak to działa. Mam nadzieję, że to się przyda.

Uwaga: ten pakiet NuGet jest moim pierwszym pakietem. Jeśli więc zauważysz błąd, prześlij opinię. Dziękuję Ci.

  1. Zainstaluj pakiet lub pobierz kod źródłowy i dodaj swój projekt

    -Install-Package Betalgo.MvcMenuNavigator
  2. Dodaj swoje strony do wyliczenia

    public enum HeaderTop
    {
        Dashboard,
        Product
    }
    public enum HeaderSub
    {
        Index
    }
  3. Umieść filtr na górze kontrolera lub akcji

    [MenuNavigator(HeaderTop.Product, HeaderSub.Index)]
    public class ProductsController : Controller
    {
        public async Task<ActionResult> Index()
        {
            return View();
        }
    
        [MenuNavigator(HeaderTop.Dashboard, HeaderSub.Index)]
        public async Task<ActionResult> Dashboard()
        {
            return View();
        }
    }
  4. I użyj tego w swoim układzie nagłówka w ten sposób

    @{
    var headerTop = (HeaderTop?)MenuNavigatorPageDataNavigatorPageData.HeaderTop;
    var headerSub = (HeaderSub?)MenuNavigatorPageDataNavigatorPageData.HeaderSub;
    }
    <div class="nav-collapse collapse navbar-collapse navbar-responsive-collapse">
    <ul class="nav navbar-nav">
        <li class="@(headerTop==HeaderTop.Dashboard?"active selected open":"")">
            <a href="@Url.Action("Index","Home")">Dashboard</a>
        </li>
        <li class="@(headerTop==HeaderTop.Product?"active selected open":"")">
            <a href="@Url.Action("Index", "Products")">Products</a>
        </li>
    </ul>

Więcej informacji: https://github.com/betalgo/MvcMenuNavigator

Tolga Kayhan
źródło
0

Uważam, że jest to bardziej przejrzysty i mniejszy kod, dzięki któremu wybrane menu będzie „aktywne”:

 <ul class="navbar-nav mr-auto">
                <li class="nav-item @Html.IfSelected("Index")">
                    <a class="nav-link" href="@Url.Action("Index", "Home")">Home</a>
                </li>
                <li class="nav-item @Html.IfSelected("Controls")">
                    <a class="nav-link" href="@Url.Action("Controls", "Home")">MVC Controls</a>
                </li>
                <li class="nav-item @Html.IfSelected("About")">
                    <a class="nav-link" href="@Url.Action("About", "Home")">About</a>
                </li>
</ul>

 public static string IfSelected(this HtmlHelper html, string action)
        {
            return html
                       .ViewContext
                       .RouteData
                       .Values["action"]
                       .ToString() == action
                            ? " active"
                            : "";
        }
Gabriel Marius Popescu
źródło
Ładnie, chociaż prawdopodobnie wymaga rozszerzenia dla kontrolera i obszaru w niektórych przypadkach
planetClaire
-1

Zrobiłem kombinację odpowiedzi powyżej i stworzyłem rozwiązanie.

Więc..

Najpierw w bloku maszynki do golenia utwórz jedną zmienną łańcuchową, która będzie zawierała nazwę, wartość kontrolera i akcję wywoływaną przez użytkownika.

    @{
        string controllerAction =  ViewContext.RouteData.Values["Controller"].ToString() + ViewContext.RouteData.Values["Action"].ToString(); 
    }

Następnie użyj kombinacji kodu HTML i Razor:

    <ul class="nav navbar-nav">
        <li class="@(controllerAction == "HomeIndex" ? "active" : "" )">@Html.ActionLink("Home", "Index", "Home")</li>
        <li class="@(controllerAction == "AboutIndex" ? "active" : "" )">@Html.ActionLink("About", "Index", "About")</li>
        <li class="@(controllerAction == "HomeContact" ? "active" : "" )">@Html.ActionLink("Contact", "Contact", "Home")</li>
    </ul>

Myślę, że jest to dobre, ponieważ nie trzeba za każdym razem uzyskiwać dostępu do „ViewContext.RouteData.Values”, aby uzyskać nazwę kontrolera i nazwę akcji.

Nihad Delic
źródło
-1

Moje rozwiązanie tego problemu to

<li class="@(Context.Request.Path.Value.ToLower().Contains("about") ? "active " : "" ) nav-item">
                <a class="nav-link" asp-area="" asp-controller="Home" asp-action="About">About</a>
            </li>

Lepszym sposobem może być dodanie metody rozszerzenia HTML w celu zwrócenia bieżącej ścieżki do porównania z łączem

Mudasar Rauf
źródło
-1

Ja to zrobiłem:

Stworzono pomocnika w częściowym widoku menu

 @helper RouterLink(string action, string controller)
{
    var IsActive = ViewContext.RouteData.Values["Controller"].ToString() == controller && ViewContext.RouteData.Values["Action"].ToString() == action;
    <text>href="@Url.Action(action, controller)"</text>if (IsActive){ <text>class="active"</text>}
}

Następnie użyłem go w tagu kotwicy w następujący sposób:

<li><a @RouterLink("Index","Home")>Home</a></li>

Moja aplikacja nie miała obszarów, ale można ją również uwzględnić jako inną zmienną w funkcji pomocnika. W moim widoku musiałem przekazać aktywną klasę do tagu kotwicy. Ale limożna go również skonfigurować w ten sposób.

Anup Sharma
źródło
1
Widzisz, głosy w dół tak naprawdę nie pomagają, jeśli nie podasz powodu.
Anup Sharma