47

In ASP.NET MVC is there an equivalent of the Html.ActionLink helper for Img tags?

I have a controller action that outputs a dynamically generated JPEG and I wanted to use the same Lambda expressions to link to it as I do HREFs using ActionLink.

Alternatively, a helper that just gives the URL to a route (again specified using Lambdas) would also be acceptable.

EDIT: I had originally specified that I was using Preview 5, however I see that a Beta has been released. So all-in-all the version number was an unneeded piece of info as I may be upgrading soon :-)

Jason Whitehorn
  • 13,585
  • 9
  • 54
  • 68

14 Answers14

47

You can use the URL.Action method

<a href="<%= Url.Action("Create")  %>"><img src="../../Content/Images/add_48.png" /></a>
Robert MacLean
  • 38,975
  • 25
  • 98
  • 152
39

This question is older, and I just started recently with ASP.NET MVC when the RC was already out, but for those who find this question later like me this might be interesting:

At least in the RC you can use Url.Action() also with anonymous types, the result looks much nicer than the suggestions above, I guess:

<a href="<%= Url.RouteUrl("MyRoute", new { param1 = "bla", param2 = 5 }) %>">
   put in <span>whatever</span> you want, also <img src="a.gif" alt="images" />.
</a>

There are many other overloads for RouteUrl as well, of course.

realMarkusSchmidt
  • 4,303
  • 1
  • 29
  • 33
20

Url.Action() will get you the bare URL for most overloads of Html.ActionLink, but I think that the URL-from-lambda functionality is only available through Html.ActionLink so far. Hopefully they'll add a similar overload to Url.Action at some point.

stevemegson
  • 11,843
  • 2
  • 38
  • 43
  • The parse-the-lambda functionality is in a nice convenient method in MvcToolkit, it's just internal. You could add your own extension method that calls it through reflection (or recompile MvcToolkit with it made public), assuming that an official one will replace it shortly. – stevemegson Oct 17 '08 at 08:38
  • If you make a nice extensionmethod out of this (something like: Html.ActionImage()), it shouldn't clutter your views :) – Erik van Brakel Feb 19 '09 at 16:31
8

I used a workaround to place a marker instead of text for ActionLink and then replace it with my image code. Something like this:

<%= Html.ActionLink("__IMAGE_PLACEHOLDER__", "Products").Replace("__IMAGE_PLACEHOLDER__", "<img src=\"" + myImgUrl + "\" />")%>

Not the most elegant solution but it works.

Alan Mendelevich
  • 3,591
  • 4
  • 32
  • 50
  • This is what I'm doing, except I have an extension method for building the img tag. It'd be great to wrap this up in an extension method itself, and I just might do that. – Chris Charabaruk Feb 26 '09 at 06:20
5

In MVC3, your link would look like this:

<a href="@Url.Action("Create")"><img src="../../Content/Images/add_48.png" /></a>
WEFX
  • 8,298
  • 8
  • 66
  • 102
4

In ASP.NET MVC Beta, you can use the Html.BuildUrlFromExpression method in the Futures assembly (which is not included in the default ASP.NET MVC install, but is available from CodePlex) to create a link around an image--or any HTML--using the lambda-style ActionLink syntax, like this:

<a href="<%=Html.BuildUrlFromExpression<MyController>(c => c.MyAction())%>">
     <%=Html.Image("~/Content/MyImage.gif")%>
</a>

To keep your image links borderless, you'll need to add a CSS rule like this:

img
{
     border: none;
}
adamjcooper
  • 1,131
  • 12
  • 24
3

You can use this control.It behaves like ActionLink.

http://agilefutures.com/index.php/2009/06/actionimage-aspnet-mvc

2

It's pretty simple to achieve in MVC 2. I have created my own very simple extension method to support Lambda expressions for the Url.Action helper. It requires that you reference MVC 2 Futures.
Here's the code:

using System;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Routing;

using ExpressionHelperInternal=Microsoft.Web.Mvc.Internal.ExpressionHelper;

namespace Bnv.Bssi.Web.Infrastructure.Helpers
{
    public static class UrlExtensions
    {
        public static string Action<TController>(this UrlHelper helper, Expression<Action<TController>> action) where TController : Controller
        {
            RouteValueDictionary routeValuesFromExpression = ExpressionHelperInternal.GetRouteValuesFromExpression<TController>(action);

            return helper.Action(routeValuesFromExpression["action"].ToString(), routeValuesFromExpression);
        }
    }
}

This is how you use it:

<img src="<%= Url.Action<YourController>(c => c.YourActionMethod(param1, param2)); %>" />
Maksymilian Majer
  • 2,956
  • 2
  • 29
  • 42
2

I know that my post is too late but i wanna share :)

I added new extension method something like this :

public static class ImageExtensions
{
    public static MvcHtmlString ImageLink(this HtmlHelper htmlHelper, string imgSrc, string additionalText = null, string actionName = null, string controllerName = null, object routeValues = null, object linkHtmlAttributes = null, object imgHtmlAttributes = null)
    {
        var urlHelper = ((Controller)htmlHelper.ViewContext.Controller).Url;
        var url = "#";
        if (!string.IsNullOrEmpty(actionName))
            url = urlHelper.Action(actionName, controllerName, routeValues);

        var imglink = new TagBuilder("a");
        imglink.MergeAttribute("href", url);
        imglink.InnerHtml = htmlHelper.Image(imgSrc, imgHtmlAttributes) + " " + additionalText;
        linkHtmlAttributes = new RouteValueDictionary(linkHtmlAttributes);
        imglink.MergeAttributes((IDictionary<string, object>)linkHtmlAttributes, true);

        return MvcHtmlString.Create(imglink.ToString());
    }

    public static MvcHtmlString Image(this HtmlHelper htmlHelper, string imgSrc, object imgHtmlAttributes = null)
    {
        var imgTag = new TagBuilder("img");
        imgTag.MergeAttribute("src", imgSrc);
        if (imgHtmlAttributes != null)
        {
            imgHtmlAttributes = new RouteValueDictionary(imgHtmlAttributes);
            imgTag.MergeAttributes((IDictionary<string, object>)imgHtmlAttributes, true);
        }
        return MvcHtmlString.Create(imgTag.ToString());
    }
}

Hope this helped.

Wahid Bitar
  • 13,776
  • 13
  • 78
  • 106
1

Is Url.Content() what you're looking for?

Give it something like Url.Content("~/path/to/something.jpg") it will turn it into the appropriate path based on the application root.

-Josh

1

I took the above answers and made a bit of a wrapper extension:

    public static MvcHtmlString ActionImageLink(this HtmlHelper helper, string src, string altText, UrlHelper url, string actionName, string controllerName)
        {
            return ActionImageLink(helper, src, altText, url, actionName, controllerName, null, null);
        }

        public static MvcHtmlString ActionImageLink(this HtmlHelper helper, string src, string altText, UrlHelper url, string actionName, string controllerName, Dictionary<string, string> linkAttributes, Dictionary<string, string> imageAttributes)
        {
            return ActionImageLink(helper, src, altText, url, actionName, controllerName, null, linkAttributes, imageAttributes);
        }

        public static MvcHtmlString ActionImageLink(this HtmlHelper helper, string src, string altText, UrlHelper url, string actionName, string controllerName, dynamic routeValues, Dictionary<string, string> linkAttributes, Dictionary<string, string> imageAttributes)
        {
            var linkBuilder = new TagBuilder("a");
            linkBuilder.MergeAttribute("href", routeValues == null ? url.Action(actionName, controllerName) : url.Action(actionName, controllerName, routeValues));

            var imageBuilder = new TagBuilder("img");
            imageBuilder.MergeAttribute("src", url.Content(src));
            imageBuilder.MergeAttribute("alt", altText);

            if (linkAttributes != null)
            {
                foreach (KeyValuePair<string, string> attribute in linkAttributes)
                {
                    if (!string.IsNullOrWhiteSpace(attribute.Key) && !string.IsNullOrWhiteSpace(attribute.Value))
                    {
                        linkBuilder.MergeAttribute(attribute.Key, attribute.Value);
                    }
                }
            }

            if (imageAttributes != null)
            {
                foreach (KeyValuePair<string, string> attribute in imageAttributes)
                {
                    if (!string.IsNullOrWhiteSpace(attribute.Key) && !string.IsNullOrWhiteSpace(attribute.Value))
                    {
                        imageBuilder.MergeAttribute(attribute.Key, attribute.Value);
                    }
                }
            }

            linkBuilder.InnerHtml = MvcHtmlString.Create(imageBuilder.ToString(TagRenderMode.SelfClosing)).ToString();
            return MvcHtmlString.Create(linkBuilder.ToString());
        }

has made it easier for me anyway, hope it helps someone else.

Jon
  • 15,110
  • 28
  • 92
  • 132
  • I like this extension, however, the `this HtmlHelper helper` is extraneous. I changed it to `this UrlHelper url` and call it using `Url.ActionImageLink(...)` – Jim Gilmartin Jul 12 '12 at 04:15
  • Or you can do something like `var _url = new UrlHelper(htmlHelper.ViewContext.RequestContext).Action(_actionName, _mergedRouteValues);` to get an UrlHelper. I shamelessly stole this from the link @Tobi provided. – Jim Gilmartin Jul 12 '12 at 04:50
  • @JimGilmartin I see, good idea. The only reason I put on the htmlhelper was so I could use it in the same way as any other razor helper, saves me trying to remember where it is stored / accessed from. – Jon Jul 12 '12 at 07:58
0

Adding to the other posts: in my case (asp.net mvc 3) I wanted an image link to act as a language selector so I ended up with:

  public static MvcHtmlString ImageLink(this HtmlHelper htmlHelper, string imgSrc, string cultureName, object htmlAttributes, object imgHtmlAttributes, string languageRouteName = "lang", bool strictSelected = false)
        {
           UrlHelper urlHelper = ((Controller)htmlHelper.ViewContext.Controller).Url;
           TagBuilder imgTag = new TagBuilder("img");
           imgTag.MergeAttribute("src", imgSrc);
           imgTag.MergeAttributes((IDictionary<string, string>)imgHtmlAttributes, true);

           var language = htmlHelper.LanguageUrl(cultureName, languageRouteName, strictSelected);                      
           string url = language.Url;

           TagBuilder imglink = new TagBuilder("a");
           imglink.MergeAttribute("href", url);
           imglink.InnerHtml = imgTag.ToString();
           imglink.MergeAttributes((IDictionary<string, string>)htmlAttributes, true);

           //if the current page already contains the language parameter make sure the corresponding html element is marked
           string currentLanguage = htmlHelper.ViewContext.RouteData.GetRequiredString("lang");
           if (cultureName.Equals(currentLanguage, StringComparison.InvariantCultureIgnoreCase))
           {
              imglink.AddCssClass("selectedLanguage");
           }
           return new MvcHtmlString(imglink.ToString());
        }

The internalization support was done via a language route - original source here.

Ando
  • 11,199
  • 2
  • 30
  • 46
0

Nice solutions here, but what if you want to have more then just an image in the actionlink? This is how I do it:

     @using (Html.BeginForm("Action", "Controler", ajaxOptions))
     { 
        <button type="submit">
           <img src="image.png" />            
        </button>
     }

The drawback is that I still have to do a bit of styling on the button-element, but you can put all the html you want in there.

And it works with the Ajax helper as well: https://stackoverflow.com/a/19302438/961139

Community
  • 1
  • 1
0

I tried to put the output of the Html.Image into my Html.ImageLink helper.

@(new HtmlString(Html.ActionLink(Html.Image("image.gif").ToString(), "myAction", "MyController").ToString().Replace("&lt;", "<").Replace("&gt;", ">")))

The problem for me is, that the ActionLink name is encoded so I have &lt instead of <.

I just removed this encoding and the result works for me. (Is there a better way of doing this instead using replace?)

tivo
  • 337
  • 1
  • 6