I just finished writing an epic (in length) answer on StackOverflow and thought that it is nearly blog worthy… so in keeping with my mediocre posts, I’m going to blog it! ;-)
The question was this:
My ultimate goal is to have a menu that adds a class to the list item that associates with the current page I am on.
So I have it set up such that each controller will be associated with an item in my menu. I need to add a class to that list item (changing the color, background, whatever).
Is there a simple way to do this? Pass a value to the View, then what?
It seems like a good idea to pass a value to the view, but if your menu items are coupled to your controllers and actions then it would make sense to simply use the values you can find in the ViewContext.RouteData.Values collection.
So using the following simple HtmlHelper extension as a base we can cater for any number of scenarios.
public static string OnClass(this HtmlHelper html, bool isOn)
{
if (isOn)
return " class=\"on\"";
return string.Empty;
}
Just test the current action:
public static string OnClass(this HtmlHelper html, string action)
{
string currentAction = html.ViewContext.RouteData.Values["action"].ToString();
return html.OnClass(currentAction.ToLower() == action.ToLower());
}
Test for any number of actions:
public static string OnClass(this HtmlHelper html, string[] actions)
{
string currentAction = html.ViewContext.RouteData.Values["action"].ToString();
foreach (string action in actions)
{
if (currentAction.ToLower() == action.ToLower())
return html.OnClass(true);
}
return string.Empty;
}
Test for action and controller:
public static string OnClass(this HtmlHelper html, string action, string controller)
{
string currentController = html.ViewContext.RouteData.Values["controller"].ToString();
if (currentController.ToLower() == controller.ToLower())
return html.OnClass(action);
return string.Empty;
}
Test for any number of actions on a controller:
public static string OnClass(this HtmlHelper html, string[] actions, string controller)
{
string currentController = html.ViewContext.RouteData.Values["controller"].ToString();
if (currentController.ToLower() == controller.ToLower())
return html.OnClass(actions);
return string.Empty;
}
Etc etc.
Then you simply call any of them from your view like so:
<ul id="left-menu">
<!-- simple boolean -->
<li <%= Html.OnClass(something == somethingElse) %>>Blah</li>
<!-- action -->
<li <%= Html.OnClass("Index") %>>Blah>/li>
<!-- any number of actions -->
<li <%= Html.OnClass(new string[] { "Index", "Details", "View" }) %>>Blah</li>
<!-- action and controller -->
<li <%= Html.OnClass("Index", "Home") %>>Blah</li>
<!-- any number of actions on a controller -->
<li <%= Html.OnClass(new string[] { "Index", "Details", "View" }, "Home") %>>Blah</li>
</ul>
So yeah, that was the approach I used in my last ASP.NET MVC application. No doubt next time I’ll do something completely different… maybe create my own versions of HtmlHelper.ActionLink that create the <li> & <a> & set the on class... mmm yeah, I like that idea.