Yesterday, I was creating a simple HTML helper for displaying images. Nothing fancy. The code for the helper is contained in Listing 1.
Listing 1 – HelpersImageHelper.cs
using System.Web.Mvc; using System.Web.Routing; namespace MvcApplication1.Helpers { public static class ImageHelper { public static string Image(this HtmlHelper helper, string id, string url, string alternateText) { return Image(helper, id, url, alternateText, null); } public static string Image(this HtmlHelper helper, string id, string url, string alternateText, object htmlAttributes) { // Create tag builder var builder = new TagBuilder("img"); // Create valid id builder.GenerateId(id); // Add attributes builder.MergeAttribute("src", url); builder.MergeAttribute("alt", alternateText); builder.MergeAttributes(new RouteValueDictionary(htmlAttributes)); // Render tag return builder.ToString(TagRenderMode.SelfClosing); } } }
This helper method uses a TagBuilder to render a standard HTML <img> tag. You can supply the image Id, URL, AlternateText, and HTML attributes when calling the Html.Image() helper.
However, I hit one roadblock. I wanted to enable users to pass application relative URLs to the Html.Image() helper. For example, I wanted users to be able to call the Html.Image() helper within an MVC view like this:
<%= Html.Image(“myImage”, “~/Content/Rover.jpg”, “Picture of Rover”) %>
When you use an ASP.NET Web Forms Web Control, you can assign paths to properties that contain the tilde character (~). For example, you can use a path like ~/Content/Rover.jpg. The control replaces the tilde with the application path automatically. So, you get /MyApp/Content/Rover.jpg. Being able to create application relative URLs is very useful because you might need to move your application to a new virtual directory in the future and you want your URLs to continue to work.
The ASP.NET Web Forms framework uses the Control.ResolveUrl() method to do this. Unfortunately, because this is a method of the Control class, this method is not available within an HTML helper.
I was stuck until Levi — one of the super genius developers on the ASP.NET MVC team — explained that I could use the Url.Content() method to do the same thing as the Control.ResolveUrl() method. So, here is how I can rewrite the Html.Image() helper to support application relative URLs:
Listing 2 – HelpersImageHelper.cs
using System.Web.Mvc; using System.Web.Routing; namespace MvcApplication1.Helpers { public static class ImageHelper { public static string Image(this HtmlHelper helper, string id, string url, string alternateText) { return Image(helper, id, url, alternateText, null); } public static string Image(this HtmlHelper helper, string id, string url, string alternateText, object htmlAttributes) { // Instantiate a UrlHelper var urlHelper = new UrlHelper(helper.ViewContext.RequestContext); // Create tag builder var builder = new TagBuilder("img"); // Create valid id builder.GenerateId(id); // Add attributes builder.MergeAttribute("src", urlHelper.Content(url)); builder.MergeAttribute("alt", alternateText); builder.MergeAttributes(new RouteValueDictionary(htmlAttributes)); // Render tag return builder.ToString(TagRenderMode.SelfClosing); } } }
Notice that I create an instance of a URL helper like this:
// Instantiate a UrlHelper
var urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
And then I can use the URL helper to resolve a URL like this:
// Add attributes
builder.MergeAttribute(“src”, urlHelper.Content(url));
Using UrlHelper.Content() does the same thing as Control.ResolveUrl(). I became curious about how the UrlHelper.Content() method worked internally. Because all of the the ASP.NET MVC framework source code is available to be downloaded from www.CodePlex.com, I was able to look up the implementation of the UrlHelper.Content() method quickly. It looks something like:
public string Content(string contentPath) { if (string.IsNullOrEmpty(contentPath)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "contentPath"); } if (contentPath[0] == '~') { return VirtualPathUtility.ToAbsolute(contentPath, this.RequestContext.HttpContext.Request.ApplicationPath); } return contentPath; }
Notice that the UrlHelper.Content() method does use the VirtualPathUtility class. So this has consequences for testability.
Thanks Stephen 🙂
This is very nice code.
Stephen,
Nice tip.
thanks a lot for this article
nice to know. Thanks.
Thanks Stephen…This will help.
So what does this do if you don’t supply the tilde? Return the relative pah you supply? Hmm interesting. I agree good tip.
Thanks for your help
New joinee
I know this is completely aside from the point of the article, but why would an img tag _always_ need an id? In fact, when (rarely) would it need one?
@Marc — Having an Id on an
tag is useful when you need to refer to it in your Cascading Style Sheet file or JavaScript code. Sounds like you would want an overload of the Image() method without an id parameter which would only require three more lines of code 🙂
Stephen,
Interesting article – made even more interesting by the fact that I’ve just read an asp.net article (http://www.asp.net/learn/mvc/tutorial-35-cs.aspx) about Html helpers and was wondering why it didn’t resolve the URLs internally – to reduce repetition in the aspx pages.
A bit of searching on the subject brought me to this page – imagine my surprise when I found that the code was not only almost identical to the orignal articel, but addressed the centralisation of URL resolution.
I assume that the original tutorial was written before this page – so, how about updating the tutorial?
Stephen, you said “Notice that the UrlHelper.Content() method does use the VirtualPathUtility class. So this has consequences for testability”
I’m migrating up from Preview 5 where an ImageHelper that I had written, like yours using the Url.Content, passed all my unit tests without a great deal of mocking or context setup. I’m now finding in v 1.0 that they are failing and Url.Content is returning an empty string.
How have you unit tested your helper?
I have been looking for a Image control and code for displaying images from a data base with a GUID for UserId I ca get the image tag to show but not the image I’ve run out of angles any ideas
thanks following your book up to chapter 6
it is bringing everything into light
still have a long way to go
thanks agian
It should be noted, that VirtualPathUtility.ToAbsolute() will choke and die when trying to resolve a url with querystring parameters..
In other words, something like this:
~/Handlers/Imagehander.ashx?id=432432
when passed in will fail miserably.
We use a custom PathHelper class to wrap VirtualPathUtility.. Basically, it splits off the query string params, translates the root of the url and re-appends the split query string
public static class PathHelper
{
public static string ToAbsolute(string path, string root)
{
if (path.IndexOf(‘?’) == -1)
path = VirtualPathUtility.ToAbsolute(path);
else
path = VirtualPathUtility.ToAbsolute(path.Substring(0, path.IndexOf(‘?’)), root) +
path.Substring(path.IndexOf(‘?’));
return path;
}
}
So your ImageHelper’s Image() method could be written like this:
public static string Image(this HtmlHelper helper, string id, string url, string alt, object htmlAttributes)
{
// Create our builder
var img = new TagBuilder(“img”);
img.GenerateId(id);
// Apply our attributes
img.MergeAttribute(“src”, PathHelper.ToAbsolute(url, helper.ViewContext.RequestContext.HttpContext.Request.ApplicationPath));
img.MergeAttribute(“alt”, alt);
img.MergeAttributes(new RouteValueDictionary(htmlAttributes));
// return rendered tag
return img.ToString(TagRenderMode.SelfClosing);
}
Yeah,
But I think time well give us throught the community the best helpers, for every situations
Thanks for your help
THANKS LEA
thank coder web master
Thank web Master
Yeah,
But I think time well give us throught the community the best helpers, for every situations
Really this is very nice stuff. Thanks for sharing.
Good Stephen. Definitely the right move…
Thank you for providing good information, I wish good work.
feqr t I tried to mock a call to a Linq to SQL query, but I am struggling.
Good Stephen. Definitely the right move…Thanks admin
How add ‘class’ CSS value as attribute ?
Html.Image(“phone”,”~/Content/Images/phone.gif”,”phone”, new {class=”phone”})
not working since class is keyword
Good Stephen. Definitely the right move…Thanks admin thanks codr
Thanks for sharing.Science Degree | Degrees
Nice & Great Site.
Online Nursing degree | Nutrition degree
Ma49ny thanks. A Great tip!
Great helper for HTML users. Thanks.
Thanks for sharing!
thnaks
Thanks for the tip. It will be of some help.
Commercial Property Management Software | Dubai Short Stays
It’s lucky e to know this, if it is really true. Companies tend not to realize when they create security holes from day-to-day operation.
selam hi This sounds fascinating sıcak sohbet I’m going to read that tracing articlekısa aşk şiirleri when I have a moment.
Wow. erotik film izle is
şifalı bitkiler zayıflama de
çet sohbet fer
netlog ger
müzik dinle err
şarkı dinle
cüneyt arkın filmleri kk
isyan sözleri fer
hikayeler er
kadir inanır filmleri izle der
escort bayanlar der
bedava chat dd
chat odaları der
liseli kızlar derf
kızlarla sohbet fder
sohbet errZ
Great overview. Your style of writing is really a joy to read. http://www.mindestenshaltbar.net/0308/stories/1938