15

I'm wondering how is it possible to add a CSS Class to the current page in your navigation when using ASP.NET MVC 3? Here is my navigation in my _Layout.cshtml file:

<p>@Html.ActionLink("Product Search", "Index", new { controller = "Home" }, new { @class = "current" })
                | @Html.ActionLink("Orders", "Index", new { controller = "Orders" }) 
                | @Html.ActionLink("My Account", "MyAccount", new { controller = "Account" })
                | @Html.ActionLink("Logout", "LogOff", new { controller = "Account" })</p>

As you can see I have 4 links in my navigation with the first one having the CSS class "current" applied to it, I'd like to be able to add/remove this class to the different links in my navigation depending on which page the user is at. Is this possible?

Cheers

CallumVass
  • 11,288
  • 26
  • 84
  • 154

6 Answers6

25

You can do this

@{ 
   var currentController = ViewContext.RouteData.Values["controller"] as string ?? "Home";
   var currentAction = ViewContext.RouteData.Values["action"] as string ?? "Index";
   var currentPage = (currentController + "-" + currentAction ).ToLower();
}

@Html.ActionLink("Product Search", "Index", "Home", null,
                 new { @class = currentPage == "home-index" ? "current" : "" })
@Html.ActionLink("MyAccount", "MyAccount", "Account", null,
                  new { @class = currentPage == "account-myaccount" ? "current" : "" })
Jeremy Thompson
  • 61,933
  • 36
  • 195
  • 321
dknaack
  • 60,192
  • 27
  • 155
  • 202
15

I would recommend using an extension method for this. Something like:

public static HtmlString NavigationLink(
    this HtmlHelper html,
    string linkText,
    string actionName,
    string controllerName)
{
    string contextAction = (string)html.ViewContext.RouteData.Values["action"];
    string contextController = (string)html.ViewContext.RouteData.Values["controller"];

    bool isCurrent =
        string.Equals(contextAction, actionName, StringComparison.CurrentCultureIgnoreCase) &&
        string.Equals(contextController, controllerName, StringComparison.CurrentCultureIgnoreCase);

    return html.ActionLink(
        linkText,
        actionName,
        controllerName,
        routeValues: null,
        htmlAttributes: isCurrent ? new { @class = "current" } : null);
}

Then you can use it in your View by including the namespace of your extension and just calling your method:

@using MyExtensionNamespace;

...

  @Html.NavigationLink("Product Search", "Index", "Home")
| @Html.NavigationLink("Orders", "Index", "Orders") 
| @Html.NavigationLink("My Account", "MyAccount", "Account")
| @Html.NavigationLink("Logout", "LogOff", "Account")

This has the benefit of keeping your razor a little cleaner and is easily reusable in other views.

bingles
  • 11,582
  • 10
  • 82
  • 93
  • Thanks, marked as answer as I feel this is the best way to do it, cleaner razor views and code re-usability – CallumVass Jul 28 '14 at 07:32
  • For the extension `HtmlHelper.ActionLink()`, add the namespace for [LinkExtensions](https://msdn.microsoft.com/en-us/library/system.web.mvc.html.linkextensions.aspx) like this: `using System.Web.Mvc.Html;` – Mike Sep 28 '15 at 17:02
8
@{
   var controller = ViewContext.RouteData.Values["controller"].ToString();
   var action = ViewContext.RouteData.Values["action"].ToString();
   var isActiveController = new Func<string, string, string, string, string>((ctrl, act, activeStyle, inactiveStyle) => controller == ctrl && action == act ? activeStyle : inactiveStyle);
 }

Then in your class attribute in your HTML you can do:

class="@isActiveController("controlername","action","activecssstyleclass","inactiveccsstyle")"

Just an other way of @dknaack his answer.. bit more generic and less functionality to repeat in your code.

KyorCode
  • 1,477
  • 1
  • 22
  • 32
5

In my case,assume I have a Home page and a menu.

Add a ViewBag.Active as a placeholder in Home page like this:

@{
   ViewBag.Title = "Home";
   ViewBag.Active = "Home";
}

Then place it to your li class as a condition to active it or not:

 <li class="@(ViewBag.Active=="Home"? "active" : "")">
      <a href="@Url.Action("Index", "Home")"><span>@ViewBag.Title</span></a>
 </li>
fbarikzehy
  • 4,885
  • 2
  • 33
  • 39
2

I used this tutorial to get this done, it's a lot simpler to understand and takes 2 minutes Hightlight Active menu item

CSharper
  • 5,420
  • 6
  • 28
  • 54
  • 1
    the downside to this approach is that you have to do ViewBag.CurrentPage in each of your controller actions.. – CallumVass Sep 20 '13 at 13:40
1

You can also override the AnchorTagHelper (the default <a> tag helper) to create your own tag helper. The advantage is that it already has all required information by providing the asp-controller and even allows for autocomplete etc. by your IDE.

This is my TagHelper:

public class NavAnchorTagHelper : AnchorTagHelper
{
    public NavAnchorTagHelper(IHtmlGenerator generator) : base(generator)
    {
    }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        base.Process(context, output);
        
        var contextController = (string)ViewContext.RouteData.Values["controller"];
        if (contextController?.Equals(this.Controller) == true)
        {
            output.AddClass("text-dark", HtmlEncoder.Default);
        }
        output.TagName = "a";
    }
}

And I use it like that:

<nav-anchor class="nav-link" asp-controller="MyController" asp-action="Index">Test</nav-anchor>
Tim
  • 43
  • 6