Action Image MVC3 Razor

119

Qual é a melhor maneira de substituir links por imagens usando o Razor no MVC3. Eu simplesmente estou fazendo isso no momento:

<a href="@Url.Action("Edit", new { id=MyId })"><img src="../../Content/Images/Image.bmp", alt="Edit" /></a> 

Existe uma maneira melhor?

davy
fonte
15
Não está diretamente relacionado, mas eu sugiro fortemente que você use arquivos PNG ou JPG (dependendo do conteúdo da imagem) em vez de arquivos BMP. E como o @jgauffin sugeriu, também tente usar os caminhos relativos do aplicativo ( ~/Content). O caminho ../../Contentpode não ser válida a partir de diferentes rotas (por exemplo /, /Home, /Home/Index).
4603 Lucas
Obrigado Lucas. Eu uso png, mas o conselho para usar URL.Content é o que eu estava procurando. voto
positivo

Respostas:

217

Você pode criar um método de extensão para o HtmlHelper para simplificar o código no seu arquivo CSHTML. Você pode substituir suas tags por um método como este:

// Sample usage in CSHTML
@Html.ActionImage("Edit", new { id = MyId }, "~/Content/Images/Image.bmp", "Edit")

Aqui está um exemplo de método de extensão para o código acima:

// Extension method
public static MvcHtmlString ActionImage(this HtmlHelper html, string action, object routeValues, string imagePath, string alt)
{
    var url = new UrlHelper(html.ViewContext.RequestContext);

    // build the <img> tag
    var imgBuilder = new TagBuilder("img");
    imgBuilder.MergeAttribute("src", url.Content(imagePath));
    imgBuilder.MergeAttribute("alt", alt);
    string imgHtml = imgBuilder.ToString(TagRenderMode.SelfClosing);

    // build the <a> tag
    var anchorBuilder = new TagBuilder("a");
    anchorBuilder.MergeAttribute("href", url.Action(action, routeValues));
    anchorBuilder.InnerHtml = imgHtml; // include the <img> tag inside
    string anchorHtml = anchorBuilder.ToString(TagRenderMode.Normal);

    return MvcHtmlString.Create(anchorHtml);
}
Lucas
fonte
5
Excelente trecho. Qualquer pessoa que queira usar isso com T4MVC só tem que mudar o tipo de routeValuespara ActionResulte, em seguida, na url.Actionmudança de função routeValuespararouteValues.GetRouteValueDictionary()
JConstantine
12
@ Kasper Skov: Coloque o método em uma classe estática e faça referência ao namespace dessa classe no Web.config do /configuration/system.web/pages/namespaceselemento.
Umar Farooq Khawaja
4
Bom !, em vez de alt, eu aceito um objeto para receber propriedades HTML usando um objeto anônimo, em seguida, var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);e, finalmente,foreach (var attr in attributes){ imgBuilder.MergeAttribute(attr.Key, attr.Value.ToString());}
guzart
7
Eu não consegui fazer isso funcionar até que percebi que, como estou usando o Areas, uma referência ao namespace da classe (como apontado pelo Umar) precisa ser adicionada a TODOS os arquivos web.config na pasta Views de todas as áreas e também a /Viewspasta de nível superior
Mark_Gibson 03/04
2
Se você precisar disso apenas em uma única página, em vez de alterar os arquivos Web.config, poderá adicionar uma instrução @using no .cshtml e referenciar o espaço para nome
JML
64

Você pode usar o Url.Contentque funciona para todos os links, pois ele converte o til ~no uri raiz.

<a href="@Url.Action("Edit", new { id=MyId })">
    <img src="@Url.Content("~/Content/Images/Image.bmp")", alt="Edit" />
</a>
jgauffin
fonte
3
Isso funciona muito bem no MVC3. Obrigado! <a href="@Url.Action("Index","Home")"><img src="@Url.Content("~/Content/images/myimage.gif")" alt="Home" /></a>
Rk1962
24

Com base na resposta de Lucas acima, esta é uma sobrecarga que assume um nome de controlador como parâmetro, semelhante ao ActionLink. Use essa sobrecarga quando a imagem estiver vinculada a uma ação em um controlador diferente.

// Extension method
public static MvcHtmlString ActionImage(this HtmlHelper html, string action, string controllerName, object routeValues, string imagePath, string alt)
{
    var url = new UrlHelper(html.ViewContext.RequestContext);

    // build the <img> tag
    var imgBuilder = new TagBuilder("img");
    imgBuilder.MergeAttribute("src", url.Content(imagePath));
    imgBuilder.MergeAttribute("alt", alt);
    string imgHtml = imgBuilder.ToString(TagRenderMode.SelfClosing);

    // build the <a> tag
    var anchorBuilder = new TagBuilder("a");

    anchorBuilder.MergeAttribute("href", url.Action(action, controllerName, routeValues));
    anchorBuilder.InnerHtml = imgHtml; // include the <img> tag inside
    string anchorHtml = anchorBuilder.ToString(TagRenderMode.Normal);

    return MvcHtmlString.Create(anchorHtml);
}
Crake
fonte
1
sem comentários no seu add aqui ... bem, digo uma boa modificação no código fornecido. +1 de mim.
Zack Jannsen
11

Bem, você pode usar a solução @Lucas, mas também há outra maneira.

 @Html.ActionLink("Update", "Update", *Your object value*, new { @class = "imgLink"})

Agora, adicione esta classe em um arquivo CSS ou em sua página:

.imgLink
{
  background: url(YourImage.png) no-repeat;
}

Com essa classe, qualquer link terá a imagem desejada.

AdrianoRR
fonte
2
@ KasperSkov Esqueci esse pequeno problema. Por alguma razão, essa substituição específica do auxiliar actionLink não funciona com o exemplo acima. Você tem o ControllerNamede sua ação. Assim:@Html.ActionLink("Update", "Update", "*Your Controller*",*object values*, new {@class = "imgLink"})
AdrianoRR
3

Isso acabou sendo um tópico muito útil.

Para quem é alérgico a chaves, aqui está a versão VB.NET das respostas de Lucas e Crake:

Public Module ActionImage
    <System.Runtime.CompilerServices.Extension()>
    Function ActionImage(html As HtmlHelper, Action As String, RouteValues As Object, ImagePath As String, AltText As String) As MvcHtmlString

        Dim url = New UrlHelper(html.ViewContext.RequestContext)

        Dim imgHtml As String
        'Build the <img> tag
        Dim imgBuilder = New TagBuilder("img")
        With imgBuilder
            .MergeAttribute("src", url.Content(ImagePath))
            .MergeAttribute("alt", AltText)
            imgHtml = .ToString(TagRenderMode.Normal)
        End With

        Dim aHtml As String
        'Build the <a> tag
        Dim aBuilder = New TagBuilder("a")
        With aBuilder
            .MergeAttribute("href", url.Action(Action, RouteValues))
            .InnerHtml = imgHtml 'Include the <img> tag inside
            aHtml = aBuilder.ToString(TagRenderMode.Normal)
        End With

        Return MvcHtmlString.Create(aHtml)

    End Function

    <Extension()>
    Function ActionImage(html As HtmlHelper, Action As String, Controller As String, RouteValues As Object, ImagePath As String, AltText As String) As MvcHtmlString

        Dim url = New UrlHelper(html.ViewContext.RequestContext)

        Dim imgHtml As String
        'Build the <img> tag
        Dim imgBuilder = New TagBuilder("img")
        With imgBuilder
            .MergeAttribute("src", url.Content(ImagePath))
            .MergeAttribute("alt", AltText)
            imgHtml = .ToString(TagRenderMode.Normal)
        End With

        Dim aHtml As String
        'Build the <a> tag
        Dim aBuilder = New TagBuilder("a")
        With aBuilder
            .MergeAttribute("href", url.Action(Action, Controller, RouteValues))
            .InnerHtml = imgHtml 'Include the <img> tag inside
            aHtml = aBuilder.ToString(TagRenderMode.Normal)
        End With

        Return MvcHtmlString.Create(aHtml)

    End Function

End Module
dansan
fonte
1

Este método de extensão também funciona (para ser colocado em uma classe estática pública):

    public static MvcHtmlString ImageActionLink(this AjaxHelper helper, string imageUrl, string altText, string actionName, object routeValues, AjaxOptions ajaxOptions)
    {
        var builder = new TagBuilder("img");
        builder.MergeAttribute("src", imageUrl);
        builder.MergeAttribute("alt", altText);
        var link = helper.ActionLink("[replaceme]", actionName, routeValues, ajaxOptions);
        return new MvcHtmlString( link.ToHtmlString().Replace("[replaceme]", builder.ToString(TagRenderMode.SelfClosing)) );
    }
Diego
fonte
1

Para adicionar a todo o trabalho impressionante iniciado por Luke, estou postando mais um que usa um valor de classe css e trata class e alt como parâmetros opcionais (válidos no ASP.NET 3.5+). Isso permitirá mais funcionalidade, mas reduzirá o número de métodos sobrecarregados necessários.

// Extension method
    public static MvcHtmlString ActionImage(this HtmlHelper html, string action,
        string controllerName, object routeValues, string imagePath, string alt = null, string cssClass = null)
    {
        var url = new UrlHelper(html.ViewContext.RequestContext);

        // build the <img> tag
        var imgBuilder = new TagBuilder("img");
        imgBuilder.MergeAttribute("src", url.Content(imagePath));
        if(alt != null)
            imgBuilder.MergeAttribute("alt", alt);
        if (cssClass != null)
            imgBuilder.MergeAttribute("class", cssClass);

        string imgHtml = imgBuilder.ToString(TagRenderMode.SelfClosing);

        // build the <a> tag
        var anchorBuilder = new TagBuilder("a");

        anchorBuilder.MergeAttribute("href", url.Action(action, controllerName, routeValues));
        anchorBuilder.InnerHtml = imgHtml; // include the <img> tag inside
        string anchorHtml = anchorBuilder.ToString(TagRenderMode.Normal);

        return MvcHtmlString.Create(anchorHtml);
    }
Zack Jannsen
fonte
Além disso, para quem é novo no MVC, dica útil - o valor de routeValue deve ser @ RouteTable.Routes ["Home"] ou qualquer que seja o seu ID de "route" na RouteTable.
Zack Jannsen
1

modificação do slide alterada Helper

     public static IHtmlString ActionImageLink(this HtmlHelper html, string action, object routeValues, string styleClass, string alt)
    {
        var url = new UrlHelper(html.ViewContext.RequestContext);
        var anchorBuilder = new TagBuilder("a");
        anchorBuilder.MergeAttribute("href", url.Action(action, routeValues));
        anchorBuilder.AddCssClass(styleClass);
        string anchorHtml = anchorBuilder.ToString(TagRenderMode.Normal);

        return new HtmlString(anchorHtml);
    }

Classe CSS

.Edit {
       background: url('../images/edit.png') no-repeat right;
       display: inline-block;
       height: 16px;
       width: 16px;
      }

Crie o link basta passar o nome da classe

     @Html.ActionImageLink("Edit", new { id = item.ID }, "Edit" , "Edit") 
dnxit
fonte
0

Eu juntei a resposta de Lucas e " ASP.NET MVC Helpers, Mesclando dois objetos htmlAttributes juntos " e mais controllerName para o seguinte código:

// Exemplo de uso em CSHTML

 @Html.ActionImage("Edit",
       "EditController"
        new { id = MyId },
       "~/Content/Images/Image.bmp",
       new { width=108, height=129, alt="Edit" })

E a classe de extensão para o código acima:

using System.Collections.Generic;
using System.Reflection;
using System.Web.Mvc;

namespace MVC.Extensions
{
    public static class MvcHtmlStringExt
    {
        // Extension method
        public static MvcHtmlString ActionImage(
          this HtmlHelper html,
          string action,
          string controllerName,
          object routeValues,
          string imagePath,
          object htmlAttributes)
        {
            ///programming/4896439/action-image-mvc3-razor
            var url = new UrlHelper(html.ViewContext.RequestContext);

            // build the <img> tag
            var imgBuilder = new TagBuilder("img");
            imgBuilder.MergeAttribute("src", url.Content(imagePath));

            var dictAttributes = htmlAttributes.ToDictionary();

            if (dictAttributes != null)
            {
                foreach (var attribute in dictAttributes)
                {
                    imgBuilder.MergeAttribute(attribute.Key, attribute.Value.ToString(), true);
                }
            }                        

            string imgHtml = imgBuilder.ToString(TagRenderMode.SelfClosing);

            // build the <a> tag
            var anchorBuilder = new TagBuilder("a");
            anchorBuilder.MergeAttribute("href", url.Action(action, controllerName, routeValues));
            anchorBuilder.InnerHtml = imgHtml; // include the <img> tag inside            
            string anchorHtml = anchorBuilder.ToString(TagRenderMode.Normal);

            return MvcHtmlString.Create(anchorHtml);
        }

        public static IDictionary<string, object> ToDictionary(this object data)
        {
            ///programming/6038255/asp-net-mvc-helpers-merging-two-object-htmlattributes-together

            if (data == null) return null; // Or throw an ArgumentNullException if you want

            BindingFlags publicAttributes = BindingFlags.Public | BindingFlags.Instance;
            Dictionary<string, object> dictionary = new Dictionary<string, object>();

            foreach (PropertyInfo property in
                     data.GetType().GetProperties(publicAttributes))
            {
                if (property.CanRead)
                {
                    dictionary.Add(property.Name, property.GetValue(data, null));
                }
            }
            return dictionary;
        }
    }
}
Tomas Kubes
fonte
0

Isso seria muito bom trabalho

<a href="<%:Url.Action("Edit","Account",new {  id=item.UserId }) %>"><img src="../../Content/ThemeNew/images/edit_notes_delete11.png" alt="Edit" width="25px" height="25px" /></a>
user3181441
fonte