Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

AJAX Photo Album Project

4.85/5 (13 votes)
16 Dec 2008CPOL4 min read 96.9K   3.1K  
The project shows how to create a photo album and slideshow using AJAX XmlHttpRequest.

AjaxPhotoAlbumProject

Introduction

This article describes several techniques on how to create a simple photo album and slideshow:

  • Save and retrieve images from a MS SQL Server database
  • Resize images
  • Retrieve images from the database using AJAX XmlHttpRequest
  • Use the AJAX UpdatePanel and ScriptManager to retrieve images from a database without refreshing the web page

Background

The AjaxPhotoAlbum project was developed to show several useful techniques:

  1. The sample photo album consists of thumbnails and an image control to browse the large image.
  2. When the user moves mouse over a thumbnail image, the large image also gets populated with the selected image. The large image is retrieved from the database with the AJAX XmlHttpRequest using the web page GetSelectedImage.aspx.

  3. The user can also click "Previous" (<<) and "Next" (>>) link buttons to get the previous / next image in a large image placeholder. The whole control is placed inside an AJAX UpdatePanel, and because of that, even if the images are retrieved from the database on postback events, the web page does not get refreshed, and only the large image control gets updated with the new image.
  4. The user can also run the slide show that will retrieve images from the database in an interval selected from an Interval drop down list control. In this case, an AJAX XmlHttpRequst object is used to send requests and get the responses using the web page behind GetSelectedImage.aspx.
  5. The images for the photo album are stored in a database. To add an image to a database, the user can click the "Add New Image" button. The original image is resized to two different sizes - small (120 px width - used for thumbnails) and large (250 px width - used for the large image placeholder) and saved to the "Images" table in a database.
  6. When an image is required to be retrieved from the database (either for a thumbnail or large image control), it is read into a byte array and then is written to the web page "ImagePage.aspx", the link to which in turn is used as "src" attribute of the image control on a web page.

Using the code

For this project, an MS SQL Server database "AjaxPhotoAlbum" with just one table "Images" was created. The script to create the table can be found in the download package, but below is the description of the "Images" table:

Column NameData Type
ImageIdint (PK, Identity Seed = 1)
ImageSmallimage
ImageLargeimage

This table will hold the images for the AjaxPhotoAlbum project. The connection string from the web.config file is shown here:

XML
<appSettings>
    <add key="DSN" 
      value="server=localhost;Integrated Security=SSPI;database=AjaxPhotoAlbum"/>
</appSettings>

The complete project structure includes four layers:

  • Presentation Layer: Default.aspx
    • GetSelectedImage.aspx (the web page that provides actual post backs in AJAX XmlHttpRequest calls)
    • ImagePage.aspx (the web page that presents the image retrieved from the database in an image control)
    • WebControls:
      • PhotoAlbumControl.ascx
      • SlideShow.ascx
    • JavaScript
      • AjaxImage.js
    • Images
    • CSS
  • Business Layer: App_Code
  • BL

    • ImageClass.cs
  • Data Access Layer: App_Code
  • DAL

    • SQLHelper.cs
  • MS SQL Server database: The AjaxPhotoAlbum database
    • Images table

PhotoAlbumControl.ascx is placed on the Default.aspx web page inside an AJAX UpdatePanel. Default.aspx also has a ScriptManager control. In order to make AJAX controls work, it is necessary to have the <httpHandlers> section under <system.web> node.

XML
<httpHandlers>
    <remove verb="*" path="*.asmx"/>
    <add verb="*" path="*.asmx" validate="false" 
      type="System.Web.Script.Services.ScriptHandlerFactory, 
            System.Web.Extensions, Version=1.0.61025.0, 
            Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    <add verb="*" path="*_AppService.axd" 
            validate="false" 
            type="System.Web.Script.Services.ScriptHandlerFactory, 
                  System.Web.Extensions, Version=1.0.61025.0, 
                  Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    <add verb="GET,HEAD" path="ScriptResource.axd" 
            type="System.Web.Handlers.ScriptResourceHandler, 
                   System.Web.Extensions, Version=1.0.61025.0, 
                   Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
            validate="false"/>
</httpHandlers>

With the above, when the user clicks "Previous" /"Next" buttons ("<<" or ">>"), the web page is posted back but is not refreshed as a whole. Only the imgPhoto web control is refreshed with the new selected image. The thumbnails are built as a DataList control. The images IDs are retrieved from the database, and on the DataList ItemDataBound() event, the thumbnail images attribute "src", "onclick", and "onmouseover" - are being populated.

C#
protected void ThumbnailsItemDatabound(object sender, DataListItemEventArgs e)
{
    Image tImage = (Image)dlThumbnails.Controls[e.Item.ItemIndex].Controls[3];
    string ajaxServerUrl = "GetSelectedImage.aspx";
    tImage.Attributes.Add("src", "ImagePage.aspx?s=s&id=" + 
      ((DataRowView)e.Item.DataItem).Row.ItemArray[0].ToString());
    tImage.Attributes.Add("onclick", 
      "getImage(‘" + ajaxServerUrl + "‘," + 
      ((DataRowView)e.Item.DataItem).Row.ItemArray[0].ToString() + ")");
    tImage.Attributes.Add("onmouseover", "getImage(‘" + 
      ajaxServerUrl + "‘," + 
      ((DataRowView)e.Item.DataItem).Row.ItemArray[0].ToString() + ")");
}

The "onclick" and "onmouseover" thumbnail image attributes are called in the AJAX JavaScript function getImage() to populate the large image control impPhoto with the image selected in the thumbnails. The getImage() JavaScript function sends the AJAX XmlHttpRequest to the web page GetSelectedImage.aspx, which in turns does the actual postback to retrieve the requested image ID from the database.

C#
//Sending information to server
function getImage(AjaxServerUrl, id)
{ 
    var url = AjaxServerUrl + "?id=" + id
    imageClient.open("GET", url);
    imageClient.onreadystatechange = getImageBack; 
    imageClient.send(null); 
}

The GetSelectedImage.aspx web page retrieves the required image ID and returns it as a response in XML format.

C#
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Tirex;
public partial class GetSelectedImage : System.Web.UI.Page
{
    DataSet ds = new DataSet();
    protected void Page_Load(object sender, EventArgs e)
    {
        Response.Clear();
        Response.ContentType = "text/xml";
        int id = 0;
        if (Request.Params["id"] != null)
        {
            id = Int32.Parse(Request.Params["id"].ToString());
        }
        else
        {
            if (Session["ImageId"] != null)
            { 
                id = Int32.Parse(Session["ImageId"].ToString());
            }
            id = ImageClass.GetImageId(id);
            Session["ImageId"] = id.ToString();
        } 
        Session["SelectedImageId"] = id.ToString();
        Response.Write("<Root>");
        Response.Write("<ImageId>");
        Response.Write(id);
        Response.Write("</ImageId>");
        Response.Write("</Root>");
    }
}

The response is in turn received and parsed by the getImageBack() JavaScript function. Please, consider the differences on how the response is processed for Internet Explorer and Firefox.

JavaScript
//Waiting and processing server response
function getImageBack(response)
{ 
    var imageId = 0;
    try
    { 
        if(imageClient.readyState == COMPLETE && imageClient.status == OK)
        { 
            //branch for Firefox version
            if (document.implementation.createDocument)//Firefox
            { 
                //Get Image
                var xmlElementImageId = 
                  imageClient.responseXML.getElementsByTagName(
                  "Root")[0].childNodes[0];
                imageId = xmlElementImageId.textContent;
            }
            //branch for IE/Windows ActiveX version
            else if (document.all)//IE
            { 
                xmlDocument = new ActiveXObject(‘Microsoft.XMLDOM‘);
                xmlDocument.async = false;
                //The responseText is loaded into XML document
                xmlDocument.loadXML(imageClient.responseText);
                //Get Image
                var xmlElementImageId = 
                    xmlDocument.selectSingleNode(‘Root/ImageId‘);
                imageId = xmlElementImageId.text;
            } 
            var imgPhoto;
            imgPhoto = window.document.getElementById(
                           "PhotoAlbumControl1_imgPhoto");
            imgPhoto.src = "ImagePage.aspx?s=l&id=" + imageId;
        }
    }
    catch(err)
    {
        alert(err.message);
    } 
}

The images are retrieved from the database as an array of bytes, and are written to the Image.aspx web page, with in turn is used as an "src" attribute for the thumbnails image or the imgPhoto image control.

C#
private void Page_Load(object sender, System.EventArgs e)
{
    string sSQL = "";
    SqlDataReader drData = null;
    SqlCommand cmd = null;
    SqlConnection cnn = new SqlConnection(SqlHelper.connectionString()); 
    cnn.Open();
    string id = Request.Params["id"].ToString();
    string s = Request.Params["s"].ToString(); 


    if (s == "s")
    {
        sSQL = "SELECT ImageSmall FROM Images WHERE ImageId=" + id.ToString();
    }
    else
    {
        sSQL = "SELECT ImageLarge FROM Images WHERE ImageId=" + id.ToString();
    } 
    //try
    //{
        using (cmd = new SqlCommand())
        {
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = sSQL;
            cmd.Connection = cnn;
            drData = cmd.ExecuteReader(); 
            if (drData.Read())
            { 
            if (s == "s")
            {
                Response.BinaryWrite((byte[])drData["ImageSmall"]);
            }
            else
            {
                Response.BinaryWrite((byte[])drData["ImageLarge"]);
            }
        }
        cnn.Close();
    } 
//}
//catch { }
}

The user can add new images to the photo album using the "Add New Image" button. To save the images into the database, they are first read into the array of bytes.

C#
len1 = File1.PostedFile.ContentLength;
imageSmall = new byte[len1];
imageLarge = new byte[len1];
File1.PostedFile.InputStream.Read(imageLarge, 0, len1);

Just before being saved into the database, the posted image is resized to the required sizes.

C#
imageSmall = ImageClass.ResizeImage(250, imageLarge);
imageLarge = ImageClass.ResizeImage(250, imageLarge);
C#
public static byte[] ResizeImage(int newWidth, byte[] pictureBytes)
{
    Bitmap b = (Bitmap)Bitmap.FromStream(new MemoryStream(pictureBytes));
    int imageWidth = b.Width;
    int imageHeight = b.Height;  
    double newHeightD = Convert.ToDouble(newWidth) / 
           Convert.ToDouble(imageWidth) * Convert.ToDouble(imageHeight);
    int newHeight = Convert.ToInt32(newHeightD);
    Bitmap output = new Bitmap(b, new Size(newWidth, newHeight));
    MemoryStream ms = new MemoryStream();
    output.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); 
    byte[] bmpBytes = ms.GetBuffer();
    output.Dispose();
    ms.Close();

    return bmpBytes;
}

The live sample can viewed at http://www.realproject.ca/default.aspx?contentid=1660.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)