I've written an extension method to HtmlHelper (derived from active menu item - asp.net mvc3 master page). This allows me to output the cssclass "active" for the current page.
However, I've now refactored to use Areas so the method no longer works because I have controllers called Home and actions called Index in several areas. So I've been trying to sort this out by checking the current area with the Area passed as part of the routevalues anonymous types.
So my extension method now looks like this:
public static MvcHtmlString NavigationLink<T>(this HtmlHelper<T> htmlHelper, string linkText, string actionName, string controllerName, dynamic routeValues)
{
string currentController = htmlHelper.ViewContext.RouteData.GetRequiredString("controller");
string currentArea = htmlHelper.ViewContext.RouteData.DataTokens["Area"] as string;
if (controllerName == currentController && IsInCurrentArea(routeValues,currentArea))
{
return htmlHelper.ActionLink(
linkText,
actionName,
controllerName,
(object)routeValues,
new
{
@class = "active"
});
}
return htmlHelper.ActionLink(linkText, actionName, controllerName, (object)routeValues, null);
}
private static bool IsInCurrentArea(dynamic routeValues, string currentArea)
{
string area = routeValues.Area; //This line throws a RuntimeBinderException
return string.IsNullOrEmpty(currentArea) && (routeValues == null || area == currentArea);
}
I changed the type of routeValues to dynamic so that I could compile the following line:
string area = routeValues.Area;
I can see the Area property on the routeValues object in the debugger but as soon as I access it I get a RuntimeBinderException.
Is there a better way to access the properties on an anonymous type?
I've figured out that I can use the constructor on the RouteValueDictionary which allows me to look up the Area
property easily.
I also noticed I was complicating the issue by trying to use the controller value as well so my code now looks like the following:
public static MvcHtmlString NavigationLink<T>(this HtmlHelper<T> htmlHelper, string linkText, string actionName, string controllerName, object routeValues)
{
string currentArea = htmlHelper.ViewContext.RouteData.DataTokens["Area"] as string;
if (IsInCurrentArea(routeValues, currentArea))
{
return htmlHelper.ActionLink(
linkText,
actionName,
controllerName,
routeValues,
new
{
@class = "active"
});
}
return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, null);
}
private static bool IsInCurrentArea(object routeValues, string currentArea)
{
if (routeValues == null)
return true;
var rvd = new RouteValueDictionary(routeValues);
string area = rvd["Area"] as string ?? rvd["area"] as string;
return area == currentArea;
}