MVC3 ActionLink with images (but without MvcFutures)?
Asked Answered
P

3

5

I was wondering if anyone knows if it possible to use any of the "out of the box" ASP.NET MVC3 helpers to generate a "link button"...I currently use following:

<a class="button" title="My Action" href="@Url.Action("MyAction", "MyController", new { id = item.Id })">
    <img alt="My Action" src="@Url.Content("~/Content/Images/MyLinkImage.png")" />
</a>

I am trying to avoid using MvcFutures, but even if I was able to use them, I don't think there is a extension method it there that will accomplish this either. (I believe solution in this case would be to roll custom helper as seen here)

Finally, this post also has a good idea to handle this via CSS, but that is not what I am asking...

Painless answered 24/2, 2011 at 22:8 Comment(4)
What is wrong with the snippet you posted above? It doesn't seem to have too much crud, and it has the virtue that you specify the HTML attributes in the normal standard way. (title, alt, etc.) And anyway, it's trivially easy for you to write such an extension method yourself if you so desire.Jaynejaynell
@Kirk Woll There is nothing wrong with what I am using now, but that is not the question. I am simply trying to see if there is a better wayPainless
What is wrong with the custom HTML helper solution?Dallasdalli
@Darin Dimitrov Nothing wrong with custom helper, I am asking if there is one "out of the box"...Painless
F
6

I am using the following to generate action links:

using System;
using System.Linq.Expressions;
using System.Text;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Routing;
using Fasterflect;

namespace StackOverflow.Mvc.Extensions
{
    public static class HtmlExtensions
    {
        #region ActionImage
        // href image link
        public static string ActionImage( this HtmlHelper helper, string href, string linkText, object htmlAttributes,
                                          string alternateText, string imageSrc, object imageAttributes )
        {
            var sb = new StringBuilder();
            const string format = "<a href=\"{0}\"{1}>{2}</a>";
            string image = helper.Image( imageSrc, alternateText, imageAttributes ).ToString();
            string content = string.IsNullOrWhiteSpace( linkText ) ? image : image + linkText;
            sb.AppendFormat( format, href, GetAttributeString( htmlAttributes ), content );
            return sb.ToString();
        }

        // controller/action image link
        public static string ActionImage( this HtmlHelper helper, string controller, string action, string linkText, object htmlAttributes,
                                          string alternateText, string imageSrc, object imageAttributes )
        {
            bool isDefaultAction = string.IsNullOrEmpty( action ) || action == "Index";
            string href = "/" + (controller ?? "Home") + (isDefaultAction ? string.Empty : "/" + action);
            return ActionImage( helper, href, linkText, htmlAttributes, alternateText, imageSrc, imageAttributes );
        }

        // T4MVC ActionResult image link
        public static string ActionImage( this HtmlHelper helper, ActionResult actionResult, string linkText, object htmlAttributes,
                                          string alternateText, string imageSrc, object imageAttributes )
        {
            var controller = (string) actionResult.GetPropertyValue( "Controller" );
            var action = (string) actionResult.GetPropertyValue( "Action" );
            return ActionImage( helper, controller, action, linkText, htmlAttributes, alternateText, imageSrc, imageAttributes );
        }       
        #endregion

        #region Helpers
        private static string GetAttributeString( object htmlAttributes )
        {
            if( htmlAttributes == null )
            {
                return string.Empty;
            }
            const string format = " {0}=\"{1}\"";
            var sb = new StringBuilder();
            htmlAttributes.GetType().Properties().ForEach( p => sb.AppendFormat( format, p.Name, p.Get( htmlAttributes ) ) );
            return sb.ToString();
        }       
        #endregion
    }
}

Note that the GetAttributeString method relies on the Fasterflect library to make reflection tasks easier, but you can replace that with regular reflection if you prefer not to take the additional dependency.

The Image helper extension used to be part of MvcContrib but appears to have been removed, most likely because the functionality is now built in to MVC. Regardless, I've included it below for completeness:

public static class ImageExtensions {
    public static MvcHtmlString Image(this HtmlHelper helper, string imageRelativeUrl, string alt, object htmlAttributes) {
        return Image(helper, imageRelativeUrl, alt, new RouteValueDictionary(htmlAttributes));
    }

    public static MvcHtmlString Image(this HtmlHelper helper, string imageRelativeUrl, string alt, IDictionary<string, object> htmlAttributes) {
        if (String.IsNullOrEmpty(imageRelativeUrl)) {
            throw new ArgumentException(MvcResources.Common_NullOrEmpty, "imageRelativeUrl");
        }

        string imageUrl = UrlHelper.GenerateContentUrl(imageRelativeUrl, helper.ViewContext.HttpContext);
        return MvcHtmlString.Create(Image(imageUrl, alt, htmlAttributes).ToString(TagRenderMode.SelfClosing));
    }

    public static TagBuilder Image(string imageUrl, string alt, IDictionary<string, object> htmlAttributes) {
        if (String.IsNullOrEmpty(imageUrl)) {
            throw new ArgumentException(MvcResources.Common_NullOrEmpty, "imageUrl");
        }

        TagBuilder imageTag = new TagBuilder("img");

        if (!String.IsNullOrEmpty(imageUrl)) {
            imageTag.MergeAttribute("src", imageUrl);
        }

        if (!String.IsNullOrEmpty(alt)) {
            imageTag.MergeAttribute("alt", alt);
        }

        imageTag.MergeAttributes(htmlAttributes, true);

        if (imageTag.Attributes.ContainsKey("alt") && !imageTag.Attributes.ContainsKey("title")) {
            imageTag.MergeAttribute("title", (imageTag.Attributes["alt"] ?? "").ToString());
        }
        return imageTag;
    }
}
Formless answered 24/2, 2011 at 23:0 Comment(12)
Thanks this looks nice, except is seems like it depends on MvcFutures (Microsoft.Web.Mvc)...Nice though!Painless
@Painless I'm pretty sure that it doesn't. I had a bunch of other extensions in the same file and simply forgot to remove it at the top.Formless
sorry for the noob question but where in a MVC solution does this file go? Views folder?Pimply
@Pimply created another folder or created another solution and reference it from thereRusson
@Morten, in the ActionImage extension for href image link, you seem to have a helper.Image(...) extension... would you mind providing the code for that one too? Thanks! BTW, +1 for the rest of the answer.Telson
@Lirik The Image helper is built-in afaicr. Did you remember to include the appropriate namespaces for it to show up?Formless
@Morten, usually if I have the assembly referenced then right clicking on the method and selecting "Resolve" fixes it (if the method is in one of the referenced assemblies). However, I have the same using statements as you do, but there is no potion to resolve which indicates that I'm either missing an assembly or I'm missing code.Telson
I've added the code for the Image extensions, hopefully that helps.Formless
I've found that the ActionImage that takes an ActionResult doesn't work correctly if used in an MVC Area. It only appends the controller and action but doesn't check the area. I made mine more robust by generating the href using "var url = new UrlHelper(helper.ViewContext.RequestContext); var href = url.Action(actionResult);" and passing it to the ActionImage that takes the href. Now it works better with T4MVC.Past
MVC4: 'System.Web.Mvc.ActionResult' does not contain a definition for 'GetPropertyValue' and no extension method 'GetPropertyValue' accepting a first argument of type 'System.Web.Mvc.ActionResult' could be found (are you missing a using directive or an assembly reference?)Aalii
The Properties extension method on Type doesn't exist, and neither does MvcResources. None of these dependencies is easy to find on SO or Google. Please correct the answer so can be compiled, or delete it. Thanks.Aalii
@EdPlunkett It's not my job to maintain whatever little code sample I post, just so you don't have to do any work at all. I do explain how reflection tasks are solved using Fasterflect, a library you can replace if you don't like it; how to do reflection is not really what this was about, but if you need help I suggest you ask a new question instead of complaining here. MvcResources is just a resource file. If you don't have it, create it or use a constant. If you don't understand resources and resource files, ask a question.Formless
L
1

The snippet you have looks quite good. You should wrap it in a general-purpose html helper and call it a day. I'm sure there are other more interesting aspects to your application than nit picking about UI helpers :)

Luna answered 25/2, 2011 at 8:51 Comment(1)
There are plenty of other aspects...It is just that this little piece is all over my views. So I do need to "extract it" but while at it I want to make sure I "don't repeat myself"(DRY)Painless
R
0

Check at the bottom of this blog post for an example with HTML extension methods from Stephen Walther

Russon answered 25/5, 2011 at 14:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.