Figure 1 - Slide Show Control for ASP.NET
Introduction
So where do you think this garden picture is taken? California? Maine? Florida, maybe? Believe it or not its smack in the middle of Manhattan in a section called Alphabet City in a garden community called La Plaza Cultural. The garden not only has flowers and herbs, but features an apple tree, a pear tree, and a peach tree, just to name a few. Whoever said that Manhattan was just a cement jungle, obviously never wandered into La Plaza Cultural.
The picture in figure 1 is shown in my web browser to illustrate a quick web control I threw together to allow me to view a set of pictures in a directory through ASP.NET. The secret of this control is that upon pressing the Next or Previous Button, the ImageUrl in the page is dynamically changed and the page is redirected to itself to refresh the contents. The control works as follows:
1) All image filenames are read in from an image directory directly under the SlideShowControl Directory and each stored in a Session state variable
2) The index of the last file is also stored in a Session variable
3) Upon hitting Next, the index of the last file is incremented and the ImageUrl associated with the next index is assigned to the image control
4) The page is redirected back to itself to display the next image.
This article also illustrates how to embed a user control into another user control. You would think this would be easy, but it turns out to be trickier than if you were to do it in a windows form. Upon embedding one web user control into another, I started getting circular references and had to figure out a way to prevent one user controls reference from interfering with the other user controls reference. This involved removing additional references that were inserted by the visual studio designer into both ascx (user control) files and also inheriting one of the controls from an abstract base class instead of directly from System.Web.UI.UserControl. I also placed each user control in a separate web folder so the assemblies wouldn't step on each other. I'm still not sure if one of all of these techniques solved the problem in the end, but it would be nice if the problem actually didn't happen in the first place :-).
The two user controls created in the article are SlideShowControl and SlideShowControlContainer. The SlideShowControl deals with most of the functionality of the control pertaining to the image. The SlideShowControlContainer holds the SlideShowControl and the two buttons: Previous and Next. When the Previous or Next button is pressed, the previous and next functionality calls are propogated to the SlideShowControl.
Code
The heart of the code for the SlideShow resides in the SlideShowControl.ascx.cs file. Upon loading this control, the file paths are read into the session states and the initial image is drawn.
Listing 1 - Initial Page Load of the SlideShowControl
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack == false) {
// the the initial image URL imgCurrent.ImageUrl = GetImageUrl();
if (Session["CurrentImageIndex"] == null) {
// read in all the image paths and store in session states IndexImages();
// Draw the image by assigning the image control to a new ImageUrl // and redirecting the page back to itself DrawImage(); }
}
} |
The IndexImages in the SlideShowControl reads in all the images into the Session State. The Session state allows you to remember objects in your application as long as the Browser Session is alive. By persisting image file names, image indexes, and the total number of images, we can use this data throughout our session to determine which image URL needs to be shown in the Slide Show control.
The image file names are initially read in using the GetFile method of the Directory class. In this case, all files with the jpg extension are read into the Session state hash. Each filename hash value has the key (imagedirectory + unique integer value). We can use this combined key whenever we want to get the next or previous image file to assign to our Image URL in the .NET Web Image Control.
Listing 2 - Reading in all the Image Files and Assigning them to the Session
private void IndexImages() {
// determine the actual path of the asp.net application on the server string fullSitePath = this.Request.PhysicalApplicationPath;
// extend the path to include our slide show control image directory string serverImageDirectory = fullSitePath + ConfigurationSettings.AppSettings["ImageDirectory"];
// save the path information in the Session State Session["FullImagePath"] = serverImageDirectory;
// get all the files in the images directory with the desired extension string[] files = Directory.GetFiles(serverImageDirectory, String.Format("*.{0}", _imageExtension));
// store all the file names in the session state, keyed by // the directory name and a unique integer.
int count = 0; Session["CurrentImageIndex"] = 0; Session["ImageDirectory"] = _imageDirectory; foreach (string file in files) { Session[_imageDirectory + count.ToString()] = file.Substring(file.LastIndexOfAny(new char[]{'/', '\\'}) + 1); count++; }
// store the number of images in the session state Session["ImageCount"] = count;
} |
Once we have a list of image files, we will need to display them in the SlideShowControl by calling the DrawImage method. The DrawImage method of our control simply replaces the ImageUrl property in the Image control. In addition, the DrawImage method gets the dimension of the image from the image file and dynamically sets the dimensions in the Image control's Width and Height properties. Then it performs a redirect to the same page in order to update the image.
Listing 3 - Dynamically changing the Image inside the Control
public void DrawImage() { // assign the image url using the current file name imgCurrent.ImageUrl = GetImageUrl();
// also assign the correct dimensions for the image Size imageDimensions = GetImageDimensions();
imgCurrent.Width = new Unit(imageDimensions.X); imgCurrent.Height = new Unit(imageDimensions.Y);
// redirect back to the control's parent page string urlName = this.Request.Url.ToString();
this.Response.Redirect(urlName); } |
The methods for retrieving the image URL and the method for retrieving the image dimensions are shown in listing 4. The GetImageUrl method takes the server path of the image and converts it into a URL using the Page class's ResolveClientUrl method. Note that you can represent the root path with a tilda (~).
The GetImageDimensions constructs a bitmap from the path name of the current image file. Then it extracts the Width and Height from the image into a Size object and returns the size.
Listing 4 - Retrieving the Image URL and the Image Dimensions
private string GetImageUrl() { // use the pages ability to resolve the directory path into a browser readable URL return this.Page.ResolveClientUrl("~/" + _imageDirectory + "/" + GetCurrentFileName().ToString()); }
/// <summary> /// Gets the image Width and Height from the actual bitmap /// </summary> /// <returns>size containing Width and Height of Image</returns> private Size GetImageDimensions() { // get the image path for the current file string currentImagePath = Session["FullImagePath"] + "\\" + GetCurrentFileName();
// put it in a bitmap Bitmap bmp = new Bitmap(currentImagePath);
// use the bitmap class to get the dimensions of the bitmap Size result = new Size(bmp.Width, bmp.Height);
// clean up the memory bmp.Dispose();
// return the dimensions return result; } |
Button Event Handling
The previous and next click events are captured in the SlideShowControlContainer web user control. This control is the parent of the SlideShowControl web user control. The code for this control is shown in listing 5. Upon pressing a button in the browser, the SlideShowControlContainer calls the Next_Click or Previous_Click methods of the SlideShowControl.
Listing 5 - Methods in the SlideShowControlContainer User Control for handling the Previous and Next Buttons
public partial class SlideShowControlContainer : System.Web.UI.UserControl { protected void Page_Load(object sender, EventArgs e) { if (IsPostBack == false) { // Handle control logic for enabling and disabling the previous and next button when we have redirected back to the parent page btnPrevious.Enabled = (bool)Session["PreviousButtonEnabled"]; btnNext.Enabled = (bool)Session["NextButtonEnabled"]; } }
protected void btnPrevious_Click(object sender, EventArgs e) { // handle the previous click button SlideShowControl1.Previous_Click(sender, e); }
protected void btnNext_Click(object sender, EventArgs e) { // handle the next click button SlideShowControl1.Next_Click(sender, e); }
} |
The Next_Click methods simply increments the Session's CurrentImageIndex to point to the next image in the image directory. Previous_Click simply decrements the Session's CurrentImageIndex . After the CurrentImageIndex changes, the SlideShowControl forces the DrawImage method to be called in order to update the browser page to the new image.
Listing 6 - Methods in the SlideShowControl for Handling Changing the Image upon a Button Press
public void Previous_Click(object sender, EventArgs e) { // go to the previous image DecrementCurrentImageIndex(); DrawImage(); }
public void Next_Click(object sender, EventArgs e) { // go to the next image IncrementCurrentImageIndex(); DrawImage(); } |
Incrementing the index is performed simply by incrementing the CurrentImageIndex hash in the Session state. If the CurrentImageIndex goes beyond the CurrentImageCount, then we stop incrementing and disable the Next button as shown in listing 7.
Listing 7 - Incrementing the Current Image Index
public void IncrementCurrentImageIndex() { // previous button automatically enabled when next button is pressed Session["PreviousButtonEnabled"] = true;
// if we have not reached the last picture, // increment the current image index in the session
if (((int)Session["CurrentImageIndex"]) < ((int)Session["ImageCount"] - 1)) { Session["CurrentImageIndex"] = (int)Session["CurrentImageIndex"] + 1; }
// Set the next button enabled state based upon the current image index Session["NextButtonEnabled"] = ((int)Session["CurrentImageIndex"] < ((int)Session["ImageCount"] - 1)); } |
Conclusion
The great thing about ASP.NET helps you go beyond HTML to create highly functional web controls to greatly enhance the web users experience no matter what browser they are using. With ASP.NET, you can dynamically alter images and text on the fly. The SlideShowControl is a useful exercise in .NET for understanding how to change images dynamically on the web. The next time you have a load of images you want to show to your friends, don' t let it slide, let them view the pictures all in one place with a little help from C# and ASP.NET.