Chase Florell
Chase Florell

Reputation: 47407

ASP.NET MVC Dynamically set CSS Class to list item based on route

I'm looking at the StackOverflow website and I've noticed that there is a Class="youarehere" attribute set to the buttons of active views. This results in the orange styling instead of the grey styling.

Can anybody tell me how they're doing this? What's the easiest way to dynamically set the class based on the URL?

Upvotes: 3

Views: 7952

Answers (5)

Eamon
Eamon

Reputation: 246

you could create a HtmlHelper to return the value, that way you can use it anywhere

Create a “HtmlHelpers” folder in your project Create a static class Define a static method with the first parameter “this HtmlHelper helper“

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;

namespace ThisMVCApp.WebUI.HtmlHelpers
{
  public static class HtmlHelperExtensions
  {
    public static string IfActive(this HtmlHelper helper, string controller, string action)
    {
      string classValue = "";

      string currentController = helper.ViewContext.Controller.ValueProvider.GetValue("controller").RawValue.ToString();
      string currentAction = helper.ViewContext.Controller.ValueProvider.GetValue("action").RawValue.ToString();

      if (currentController == controller && currentAction == action)
      {
        classValue = "youarehere";
      }

      return classValue;
    }
  }
}

Add this to your web.config file (the one in your Views folder!!)

<system.web.webPages.razor>
  <host ... />
  <pages pageBaseType="System.Web.Mvc.WebViewPage">
    <namespaces>
      ...
      <add namespace="ThisMVCApp.WebUI.HtmlHelpers" />
    </namespaces>
  </pages>
</system.web.webPages.razor>

now you can just apply the above method as any class or attribute on buttons or any element like so :

<nav>
  <ul id="menu">
    <li class="@Html.IfActive("Home", "Index")">@Html.ActionLink("Home", "Index", "Home")</li>
    <li class="@Html.IfActive("Home", "About")">@Html.ActionLink("About", "About", "Home")</li>
 </ul>
</nav>

Upvotes: 0

Chase Florell
Chase Florell

Reputation: 47407

Here's the answer I needed (In VB) Thanks @Darin

Imports System.Web.Mvc
Imports System.Web.Mvc.Html

Namespace Utilities.HtmlHelpers
    Public Module PagingHelpers
        Sub New()
        End Sub

        <System.Runtime.CompilerServices.Extension()> _
        Public Function CustomLink( _
                ByVal htmlHelper As HtmlHelper, _
                ByVal linkText As String, _
                ByVal actionName As String, _
                ByVal controllerName As String) As MvcHtmlString


            Dim currentAction As String = TryCast(htmlHelper.ViewContext.RouteData.Values.Item("action"), String)
            Dim currentController As String = TryCast(htmlHelper.ViewContext.RouteData.Values.Item("controller"), String)
            If ((actionName = currentAction) AndAlso _
                (controllerName = currentController)) Or _
                ((controllerName = currentController) AndAlso _
                Not controllerName = "Events") Then
                Return htmlHelper.ActionLink( _
                    linkText, _
                    actionName, _
                    controllerName, _
                    Nothing, _
                    New With { _
                        .class = "youarehere" _
                    })
            End If
            Return htmlHelper.ActionLink(linkText, actionName, controllerName)
        End Function
    End Module

End Namespace

Upvotes: 0

Syd
Syd

Reputation: 1546

I have an alternative solution. Rather than having the helper class decide which "menu" is the active menu. Decide that in your controller's action.

Your Controller

public ActionView Questions()
{
    MyViewModel model = new MyViewModel ();
    model.CurrentMenu = "Questions"; 
    ViewData.Model = model;
}

public ActionView Tags()
{
    MyViewModel model = new MyViewModel ();
    model.CurrentMenu = "Tags"; 
    ViewData.Model = model;
}

In the View (a strongly typed view)

<div id="menu">    
    <ul>
    <li class="<%= Html.IsSelectedMenu(Model.CurrentMenu,'Questions') %>"<a href="/Home/Questions">Questions</a></li> 
    <li class="<%= Html.IsSelectedMenu(Model.CurrentMenu,'Tags') %>"<a href="/Home/Tags">Tags</a></li> 
    </ul>
</div>    

In your css

li.youarehere
{
    background-color: orange;
    color: #ffffff;
}

In your helper class

public static class MyHtmlExtensions
{
    public static MvcHtmlString IsSelectedMenu(this HtmlHelper helper, string currentMenu, string menu2)
    {
        return currentMenu.Equals (menu2)? "youarehere" : "";
    }
}

Upvotes: 0

Darin Dimitrov
Darin Dimitrov

Reputation: 1039160

Writing an html helper for those buttons could be one way of doing it. Assuming the standard routing is set:

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

here's how the helper might look like:

public static MvcHtmlString MyButton(this HtmlHelper htmlHelper, string id, string text)
{
    var button = new TagBuilder("input");
    button.MergeAttribute("type", "button");
    button.MergeAttribute("value", text);
    // get the id from the current route:
    var routeId = htmlHelper.ViewContext.RouteData.Values["id"] as string;
    if (id == routeId)
    {
        button.MergeAttribute("class", "active");
    }
    return MvcHtmlString.Create(button.ToString(TagRenderMode.SelfClosing));
}

and finally add to your view:

<%= Html.MyButton("questions", "Questions") %>
<%= Html.MyButton("tags", "Tags") %>
<%= Html.MyButton("users", "Users") %>

To further improve the helper you could add additional parameters that will contain the action and the controller this button will redirect to when clicked.

Upvotes: 5

Omar
Omar

Reputation: 40202

I would assume the simplest possible way is to place the information in the ViewData from the controller/action telling the view where it is, which is what I do.

In the view, you would get that data from the ViewData, then figure out which class name to associate to which location. Be sure to use an HTML Helper.

Action under Questions controller:

public ActionResult Index()
{
      ViewData["CurrentPage"] = "Questions";
}

Html Helper:

public static string GetLocationCssClassName(this HtmlHelper html)
{
         string cssClass = string.Empty;
         if(html.ViewData["CurrentPage"] != null)
         {
             string currentPage = (string)html.ViewData["CurrentPage"];
             switch(currentPage)
             { 
                 case "Questions":
                       cssClass = "question_css_class";
                       break;
                 case "Tags":
                       cssClass = "tags_css_class";
                       break;
                 case "Users":
                       cssClass = "users_css_class";
                       break;

             }
         }
         return cssClass;
}

View Page:

<div id="main" class="<%: Html.GetLocationCssClassName() %>">

   <a href="Questions/Index" class="questions">Questions</a>
   <a href="Tags/Index" class="tags">Tags</a>
   <a href="Users/Index" class="users">Users</a>

</div>

Css:

.question_css_class a.questions
{
    background-color: Orange;
}

The code can be improved endlessly, but you get the gist. Your view has access to ViewData. Designate a specific key that will hold the current page and output the proper class based on the value inside that specific key.

Upvotes: 0

Related Questions