Monday, June 15, 2009

ASP.NET MVC Strongly-Typed ActionLink with Images

Today I had the desire to change some of the ActionLinks we have been using from boring text to exciting images (which meant using my image collection as I have no talent whatsoever in drawing anything). Unfortunately I was unable to locate a respective method on the existing HtmlHelper.

I am a big fan of the strongly-typed variety of these methods, which are, as of today, only available in the MVC Futures (available at CodePlex).

In my hunt for an efficient (lazy) copy-and-paste solution I came across a thread on Stack Overflow with a comment by eu-ge-ne. His code does a nice job of getting an image produced, but did not get me the desired strongly-typed implementation I was seeking.

His version was this (with a minor fix and my personal dislike of var replaced):

public static string ActionLinkWithImage(this HtmlHelper html, string imgSrc, string actionName)
{
UrlHelper urlHelper = new UrlHelper(html.ViewContext.RequestContext);
string imgUrl = urlHelper.Content(imgSrc);
TagBuilder imgTagBuilder = new TagBuilder("img");
imgTagBuilder.MergeAttribute("src", imgUrl);
string img = imgTagBuilder.ToString(TagRenderMode.Normal);
string url = urlHelper.Action(actionName);

TagBuilder tagBuilder = new TagBuilder("a")
{
InnerHtml = img
};

tagBuilder.MergeAttribute("href", url);
return tagBuilder.ToString(TagRenderMode.Normal);
}


The following does almost the same, but strongly-typed.
public static class HtmlHelperExtensions
{
public static string ActionLinkWithImage<TController>(this HtmlHelper html, Expression<Action<TController>> action, string imgSrc) where TController : Controller
{
UrlHelper urlHelper = new UrlHelper(html.ViewContext.RequestContext);
string imgUrl = urlHelper.Content(imgSrc);
TagBuilder imgTagBuilder = new TagBuilder("img");
imgTagBuilder.MergeAttribute("src", imgUrl);
string img = imgTagBuilder.ToString(TagRenderMode.Normal);
TagBuilder tagBuilder = new TagBuilder("a")
{
InnerHtml = img
};
tagBuilder.MergeAttribute("href", LinkBuilder.BuildUrlFromExpression(html.ViewContext.RequestContext, html.RouteCollection, action));
return tagBuilder.ToString(TagRenderMode.Normal);
}

public static string ActionLinkWithImage<TController>(this HtmlHelper html, Expression<Action<TController>> action, string imgSrc, object imageAttributes, object linkAttributes) where TController : Controller
{
UrlHelper urlHelper = new UrlHelper(html.ViewContext.RequestContext);
string imgUrl = urlHelper.Content(imgSrc);
TagBuilder imgTagBuilder = new TagBuilder("img");
imgTagBuilder.MergeAttribute("src", imgUrl);

imgTagBuilder.MergeAttributes(new RouteValueDictionary(imageAttributes));

string img = imgTagBuilder.ToString(TagRenderMode.Normal);

TagBuilder tagBuilder = new TagBuilder("a")
{
InnerHtml = img
};

tagBuilder.MergeAttributes(new RouteValueDictionary(linkAttributes));
tagBuilder.MergeAttribute("href", LinkBuilder.BuildUrlFromExpression(html.ViewContext.RequestContext, html.RouteCollection, action));
return tagBuilder.ToString(TagRenderMode.Normal);
}
}


An example of consumption on your page would then be:
<%= Html.ActionLinkWithImage<FooController>(fc => fc.Edit(deal.ID), "~/Content/Images/edit_16.png", new { style = "border-style: none;", alt = "Edit Foo", title = "Edit Deal" }, null) %>


Hope this helps someone with the same predicament (Someone feel free to incorporate into the Futures assembly :))

No comments:

Post a Comment