Getting Images to render properly in the ASP.NET Designer
I'm embarrassed to say that I didn't quite know how to get controls to render image urls properly in the designer. The situation is as follows: You create a custom server control and you have an image property and you allow the use of the ~/images/someimage.gif syntax to specify the image path. I use this sort of thing frequently and have many controls that use custom image urls. For example, I have an error/notification control that has ErrorImageUrl and InfoImageUrl properties, a calendar that has a ButtonImageUrl etc.
So the obvious thing and what I've been using is Control.ResolveUrl() and it works great at runtime for specifying related links.But if you try to get this control to render in the designer as well you'll quickly find that ResolveUrl() doesn't work properly at design time because there's no explicit ApplicationPath available at design time. So the designer can't properly render the Image Url which results in a broken image in the designer.
There's a really easy solution however - use Control.ResolveClientUrl() which creates a relative URL rather than a fully rooted base URL as ResolveUrl() does. Say I'm in an app like /WebLog and I have an image path like ~/images/Warning.gif defined, the rendered image URL will look like this for each of the methods:
ResolveUrl:
/WebLog/Images/warning.gif <!--- fully rooted
ResolveClientUrl:
images/warning.gif or if in a subpath: ../images/warning.gif <!-- relative paths
ResolveClientUrl() creates a relative URL rather than a fully rooted URL which works because at design time there's no application root path to inject. A relative path works just fine with the designer's default rendering. The designer is smart enough to figure out the base path and the relative path for pages/controls in the current project and consolidates the two properly creating an appropriate relative path that works both at runtime and design time. Problem solved.
FWIW, I took a look to see what ResolveClientUrl does at design time and it uses IUrlResolutionService to handle the task at design time (from Reflector):
IUrlResolutionService service = (IUrlResolutionService)this.Page.Site.GetService(typeof(IUrlResolutionService));
if (service != null)
{
return service.ResolveClientUrl(relativeUrl);
}
Beyond that, ResolveUrl() relies on the Request.ApplicationPath to create its base path and then adds the Control.TemplateSourceDirectory on top of it to figure out path information. ResolveClientUrl() uses Control.TemplateSourceDirectory and co-relates that to the base path of the application.
I've always been using ResolveUrl() but now that I'm looking at this more closely it seems that using relative paths - especially for images - is a much better call since it works both at runtime and design time. Relative paths are shorter for one thing saving a few bytes in the Response too. Also checking into System.Web it looks like Microsoft too is using ResolveClientUrl for things like ImageUrls on an image control, so this is obviously something that I've missed for the duration - duh! Offhand I can't think of any reason why I wouldn't want to use ResolveClientUrl.
Other Posts you might also like
The Voices of Reason
# re: Getting Images to render properly in the ASP.NET Designer
Thanks.
# re: Getting Images to render properly in the ASP.NET Designer
"~/images/_selected.png"
What it gives me back is:
"images/_selected.png"
Trying then to pass the resolved URL to Bitmap.FromFile() throws my program into some weird loop for a moment and then I just have a broken image. What am I missing?
# re: Getting Images to render properly in the ASP.NET Designer
I've written a utility which resolves path to files within the current theme. I need this because I have lots of images depending on values from the database (e. g. open/closed envelope symbols, ...) for which Skin files are not a good solution, because it is too dynamic.
So, I have a syntax like
"~$/myimage.gif""~/App_Themes/Current_Theme/myimage.gif"And how can I obtain a service instance from my utility class, which has no direct access to a Page instance? Is there another and maybe better way than introducing a parameter of the Page type?
Thanks,
Marco