"Begin somewhere: you cannot build a reputation on what you intend to do."
-- Liz Smith


ImageControl is an ASP.NET server control that works directly with the ImageResizer class for resizing and displaying images. (Most images on this site are processed with this control.) In a nutshell, the control combines the functionality of the Image and HyperLink controls, while adding dynamic, server-side resizing.

The ImageResizer is the back end to this control. Ideally, the resizer is not called directly, though it is possible. This control invokes the resizer as needed. If you use the designer, this control can be easily dropped onto a form, or coded directly into the HTML. All of the resizer options are extended through this control, as well as options to provide a target link for an image, add a drop shadow, and more.

The methods and properties are listed below.

Methods
(None) As a standard web control, there are no public methods that will not be called in the event pipeline (ie Render, Load, etc.).

Properties
UseDropShadow (bool) (Optional/True) Adds an HTML/table based drop shadow to the right and bottom of the image.
ImageUrl (string) Path to the master image. If no ImageWidth or ImageHeight is specified, the master image is used. The height and width will still be read to generate complete HTML. If ImageWidth or ImageHeight is specified, the ImageResizer will handle any resizing for the ImageControl.
hAlign (enum) (Optional/Center) Accepts a System.Web.UI.Webcontrols.HorizontalAlign type. Specifies the table alignment for the output HTML.
href (string) (Optional/null) If supplied, the image will be rendered as a hyperlink to the URL specified.
Target (string) (Optional/null) If a target window or frame is required for the ImageUrl, the target can be specified with this parameter.
The following properties are for the ImageResizer type. See the ImageResizer documentation for more information.
ImageWidth (int) (Optional/0) Any non-zero value will be passed to the ImageResizer for evaluation and resizing if necessary. Zero or unspecified values will cause the image to be displayed in its original size.
ImageHeight (int) (Optional/0) Any non-zero value will be passed to the ImageResizer for evaluation and resizing if necessary. Zero or unspecified values will cause the image to be displayed in its original size.
Quality (int) 1 - 100, 1 is the lowest, 100 is the highest.
AspectRatio (enum) Determines how the Resize method calculates the new height and width, and, if applicable, which areas to crop. See the ImageResizer documentation for more information on the type.
AntiLeechKey (string) (Optional) Acts as a salt value to the file name. By default this is not used. When a string is passed in, the salt value changes the cached filename slightly. Every time this key is changed, the output filename is changed, too. This is a simple way to help prevent bandwidth leeching by routinely (either programmatically or manually) purging the cache and setting the key to a new value. Links within the application will never break, but external links will.
AntiLeech (bool) (Optional) If true, ImageResizer will use the specified AntiLeechKey when resizing images.
ShadowFolders (bool) (Optional) Flag indicating whether or not the new images should be placed in a like folder structure in the image cache folder. For example, if true, "/images/family/photo.jpg" would be placed in "/cachefolder/images/family/photo.jpg/[id].jpg". If false, all images are placed in the "/cachefolder." While this shortens the path slightly, management is a bit more difficult.
ResizeCacheFolder Virtual path to the cache folder. The ASP.NET account (or Network Service Account on Windows 2003) requires write permissions. If you wish to use the Purge method, this account requires delete permissions.

Developer Features

This ImageControl was created as a wrapper to the ImageResizer class, allowing the user to worry primarily about the presentation of the image (the linkability, drop shadow, etc.). You'll notice that as a user control, there are no methods you need to worry about invoking, and the majority of the parameters are optional intended to give granular control when needed.

You will notice a bit of optional code in the control. When displaying the image, I wanted the control to read the image's width and height to be used when rendering the HTML. It's good design to always use height and width parameters on your images, and the control should be able to handle this for us. Because of the time and load GDI+ requires to read image header information, I've decided to use UnitedBinary's AutoImageInfo -- available free at their website. Even though this requires the COM Interop and is not a native solution, it is incredibly fast. If you don't want to use this functionality (it's optional -- however, omitting it contributes to the page "wobbling" effect), this section of code can be commented out:

UnitedBinary.Imaging.AutoImageInfo aii = new UnitedBinary.Imaging.AutoImageInfo();
aii.ReadHeader(HttpContext.Current.Server.MapPath(ImagePath));
imgWidth = aii.Width;
imgHeight = aii.Height;

If a 100% native solution is required and you must have the image dimensions, this can be done very easily via GDI+ using code similar to the following (in fact, this is done in the ImageResizer class):

img = System.Drawing.Image.FromFile(Filepath,false);
imgWidth = img.Width;
imgHeight = img.Height;

While this works, GDI+ will load the entire image into memory. This is okay in the ImageResizer since we need to do this anyway to resize the image. On subsequent hits, though, we're returning a cached version. An average 1600x1200 image can take a few hundred milliseconds to load, which is slow and unscalable. Using AutoImageInfo, only the headers are read and the image dimensions can be read in about 10 to 20 milliseconds. Depending on your server architecture and needs, this may be acceptable.

The parameters for the ImageResizer class are wrapped in the ImageControl. Because these are unlikely to change, you can easily populate most of these values in the constructor with values from the web.config file. You'll see this sample in the constructor of the control:

//populate some default values from web.config
try
{
  System.Collections.Specialized.NameValueCollection config =
    System.Configuration.ConfigurationSettings.AppSettings;
  if (config["ResizeCacheFolder"] != null)
    this.ResizeCacheFolder = config["ResizeCacheFolder"];
  if (config["AntiLeechKey"] != null)
    this.AntiLeechKey = config["AntiLeechKey"];
}
catch {}

Using this approach still allows for a great deal of flexibility since the values can be overriden with parameters at any time if needed.

This control is far from being 100% complete. As a perfectionist it's tough to let it go, but it's time to move on to new projects. Here's a list of things I will eventually revisit -- your feedback is welcomed.
  • Use a .NET native solution for reading image headers.
  • Improve the drop shadowing; perhaps using GDI+ to generate the shadows or allow for shadow offsets.
  • Allow for some basic image processing, like sharpening or desaturation.
For more information on some of these issues, please read this blog post.


Downloads
Download C# Source (3k)
Download C# Source and Sample Files (26k)