Handy ASP.NET MVC 2 Extension Methods – Where am I?
- by Bobby Diaz
Have you ever needed to detect what part of the application is currently being viewed? This might be a bigger issue if you write a lot of shared/partial views or custom display or editor templates. Another scenario, which is the one I encountered when I first started down this path, is when you have some type of menu and you’d like to be able to determine which item represents the current page so you can highlight it in some way. A simple example is the menu that is created as part of the default ASP.NET MVC 2 Application template. <div id="menucontainer"> <ul id="menu"> <li><%= Html.ActionLink("Home", "Index", "Home") %></li> <li><%= Html.ActionLink("About", "About", "Home") %></li> </ul> </div> The part that got me at first, however, was the following entry in the default style sheet (Site.css): ul#menu li.selected a { background-color: #fff; color: #000; } I assumed that the .selected class would automatically get applied to the active menu item. After trying a few different things, including the MvcContrib MenuBuilder, I decided to write my own extension methods so I would have more control over the output. First, I needed a way to determine what view the user has navigated to based on the requested URL and route configuration. Now, I am sure there are many ways to do this, but this is what I came up with: public static class RequestExtensions { public static bool IsCurrentRoute(this RequestContext context, String areaName, String controllerName, params String[] actionNames) { var routeData = context.RouteData; var routeArea = routeData.DataTokens["area"] as String; var current = false; if ( ((String.IsNullOrEmpty(routeArea) && String.IsNullOrEmpty(areaName)) || (routeArea == areaName)) && ((String.IsNullOrEmpty(controllerName)) || (routeData.GetRequiredString("controller") == controllerName)) && ((actionNames == null) || actionNames.Contains(routeData.GetRequiredString("action"))) ) { current = true; } return current; } // additional overloads omitted... } With that in place, I was able to write several UrlHelper methods that check if the supplied values map to the current view. public static class UrlExtensions { public static bool IsCurrent(this UrlHelper urlHelper, String areaName, String controllerName, params String[] actionNames) { return urlHelper.RequestContext.IsCurrentRoute(areaName, controllerName, actionNames); } public static string Selected(this UrlHelper urlHelper, String areaName, String controllerName, params String[] actionNames) { return urlHelper.IsCurrent(areaName, controllerName, actionNames) ? "selected" : String.Empty; } // additional overloads omitted... } Now I can re-work the original menu to utilize these new methods. Note: be sure to import the proper namespace so the extension methods become available inside your views! <div id="menucontainer"> <ul id="menu"> <li class="<%= Url.Selected(null, "Home", "Index") %>"> <%= Html.ActionLink("Home", "Index", "Home")%></li> <li class="<%= Url.Selected(null, "Home", "About") %>"> <%= Html.ActionLink("About", "About", "Home")%></li> </ul> </div> If we take it one step further, we can clean up the markup even more. Check out the Html.ActionMenuItem() extension method and the refined menu: public static class HtmlExtensions { public static MvcHtmlString ActionMenuItem(this HtmlHelper htmlHelper, String linkText, String actionName, String controllerName) { var html = new StringBuilder("<li"); if ( htmlHelper.ViewContext.RequestContext .IsCurrentRoute(null, controllerName, actionName) ) { html.Append(" class=\"selected\""); } html.Append(">") .Append(htmlHelper.ActionLink(linkText, actionName, controllerName)) .Append("</li>"); return MvcHtmlString.Create(html.ToString()); } // additional overloads omitted... } <div id="menucontainer"> <ul id="menu"> <%= Html.ActionMenuItem("Home", "Index", "Home") %> <%= Html.ActionMenuItem("About", "About", "Home") %> </ul> </div> Which generates the following HTML: <div id="menucontainer"> <ul id="menu"> <li class="selected"><a href="/">Home</a></li> <li><a href="/Home/About">About</a></li> </ul> </div> I have created a codepaste of these extension methods if you are interested in using them in your own projects. Enjoy!