Introduction

Recently, I wanted to modify the main logo that appears at the top of each page on my website. I wanted to create some variations during the holiday seasons. This is similar to how Google customizes their Google logo to recognize various holidays and events, except I just wanted some fun variations for Christmas and Halloween.

After spending time coming up with a few designs, I found I was having trouble choosing between the best of them. It occurred to me that it would be nice if I could use all my best designs, randomly picking one of them each time the page is served. So I started to think about how best to do this without adding a bunch of overhead to every single page request.

I've done similar things in the past. But I did it by simply copying over my logo file on the web server. This was a little awkward. I had to be careful when juggling all the different versions of the logo, especially since the names would change. And if I wasn't careful and made a mistake, I'd end up with no logo displayed for a while until I corrected the error. And, of course, this approach didn't didn't support randomly cycling through more than one image.

The RandomImageRotator Web User Control

With this in mind, I decided to create a web user control that reads an XML file that contains a list of images. Each image listed in the file has an active attribute that indicates if that image should be displayed. Listing 1 shows an example XML file.

The control automatically and randomly selects from the list of images where the active attribute is set to true. This allows me to store all the different logo images on the server (no more renaming files). And to change which images are displayed, I only need to open and edit the XML file. Note that the images can start with the tilde (~) character to signify a path relative to the application root. They could also be complete URLs to reference images on other sites.

Listing 2 shows the RandomImageRotator control. As stated previously, the control is for an ASP.NET WebForms application but it could easily be modified to work with ASP.NET MVC.

As you can see, the control is pretty simple. The control contains one ASP.NET server control: An Image control named Image1. The AlternateText and ToolTip properties simply defer to the Image control.

The XmlFileName and CacheLifetimeMinutes properties are specific to the RandomImageRotator control.

The XmlFileName property specifies the filename of your XML file. The code uses LINQ to XML to read and parse this file. Because opening and parsing an XML file is relatively time consuming, I didn't want to do this on every request. So the code caches the list of active image files along with the date and time they were last refreshed from the file. By caching this information in static memory, it is considerably faster than if it read the XML file on each request. By default, the code updates the image list every 60 minutes but you can set the CacheLifetimeMinutes property to change this.

The CacheLifetimeMinutes property specifies how long the data from the XML file is cached. Since the list of image files and the date and time they were last updated must be stored in static variables, it was necessary for the code to take concurrency issues into consideration.

Each HTTP request loads and new instance of the RandomImageRotator control in a different thread. If one thread is loading the XML file when another thread starts checking if the cache needs to be refreshed, the second thread won't detect that the data is being refreshed and will try to refresh the data again. The code deals with this by using a lock statement to ensure only one thread can enter the locked section at the same time.

Whether the image list was current or had to be refreshed, the code then uses the Random class to randomly select one of the current image files. The selected filename is then assigned to the ImageUrl of the hosted Image control.

If there's an exception, there is probably something seriously wrong and the code just lets the exception propagate to the caller. However, if no XML filename was specified or if the XML file contains no active image files, the code just sets the ImageUrl property to null and continues without complaint.

Conclusion

In case it wasn't obvious, the code does not cycle through images while the user is viewing the page. Rather, it simply picks a single image to display each time the page is served to a browser.

That's about all there is to it. The attached download contains a complete working WebForms project that tests the RandomImageRotator control.