Search
Close this search box.

Creating an Image Grid (Photo Gallery) with ASP.NET using ListView Control

ASP.NET has a good number of default controls which usually satisfy your needs. Especially the ListView control, which is a template based fully customizable control which can be tweaked to suit your needs. However, ASP.NET does not have a good Image Grid control (a kind of control you would want to use as a photo gallery), I will show you how you can create one simple Image Grid for yourself. I am using Visual Studio 2012 Express for Web for this exercise, however you can achieve the same with full version of Visual Studio 2012 or even with Visual Studio 2010.

The following is something we want to achieve

Step – 1 : Initial Setup

First create an empty ASP.NET Web Application. Now create one folder ‘Controls’. You don’t need to put your control in ‘Controls’ folder, however it is a good practice to put relevant things in its own folder.

Here you can see I have also create some other folders such as ‘Helpers’, ‘Images’ and Default.aspx (Remember? We chose empty ASP.NET web app, so Default. aspx will not be available by default).

Now, right click the folder and ‘Add’ –> ‘New Item’ –> ASP.NET Web User Control with name ‘ImageGrid.ascx’

Step-2 : Logger

As a habit, I always log exceptions so that later on the developer (I) will be able to determine if any error/exception occurred in production. For that purpose, I am going to write a simple logger which logs messages to Windows Event Log. You can write your own custom logger, or you may already have some good logging strategy in place (for example – ELMAH). If that is the case, then skip this step. Make sure you comment out Logger.Log(ex.Message) statements in the next sections, which uses the Logger class I am creating here.

Create a C# class named ‘Logger’ in Helpers folder.


using System;
using System.Diagnostics;

namespace SampleControls.Helpers {
/// <summary>
/// A simple Logger which writes to Windoes Event Log
/// </summary>
public
class Logger {
  static EventLog log;

  static Logger() {
    log = new EventLog("Application", Environment.MachineName, "Image Grid");
  }

  /// <summary>
  /// Logs the message into Windows Event Log.
  /// </summary>
  /// <param name="message">Error Message</param>
 public
  static void Log(string message) {
    log.WriteEntry(message, EventLogEntryType.Warning, 21001);
  }
}
}  // namespace Helpers

Step-3 : Image Grid MarkUp

Time to start creating the Image Grid. Open the ImageGrid.ascx you created in Step 1. In the markup page you will be adding a customized list view. Paste the following code in the markup.


<asp:ListView runat="server" ID="ImageListView" ItemPlaceholderID="itemPlaceHolder" 
     GroupPlaceholderID="groupPlaceHolder" OnItemCommand="ImageListView_ItemCommand">
    <LayoutTemplate>
        <h1>
            <asp:Label Text="" runat="server" ID="titleLabel" OnLoad="titleLabel_Load" />
        </h1>
        <div runat="server" id="groupPlaceHolder">
        </div>
    </LayoutTemplate>
    <GroupTemplate>
        <span>
            <div id="itemPlaceHolder" runat="server"></div>
        </span>
    </GroupTemplate>
    <ItemTemplate>
        <asp:ImageButton ID="itemImageButton" runat="server" 
          CommandArgument="<%# Container.DataItem %>" 
          ImageUrl="<%# Container.DataItem %>" Width="320" Height="240" 
          OnCommand="itemImageButton_Command"/>
        <asp:LinkButton ID="deleteLinkButton" runat="server" CommandName="Remove" 
          CommandArgument="<%# Container.DataItem %>" Text="Delete" Visible="false" 
          OnLoad="deleteLinkButton_Load"  />
    </ItemTemplate>
    <EmptyItemTemplate>
        <td />
    </EmptyItemTemplate>
    <EmptyDataTemplate>
        <h3>No images available</h3>
    </EmptyDataTemplate>
    <InsertItemTemplate>
        <p>
            <asp:Label Text="Please upload an image" runat="server" ID="imageUploadLabel" />
            <asp:FileUpload runat="server" ID="imageUpload" OnLoad="imageUpload_Load" />
            <asp:Button ID="uploadButton" Text="Upload" runat="server" />
        </p>
        <p>
            <asp:Label Text="" runat="server" ID="imageUploadStatusLabel" />
        </p>
    </InsertItemTemplate>
</asp:ListView>

This ListView is not bound to any DataSource yet, as we are going to bind it to a List<string> providing image path programmatically.

The most important piece of code lies in ItemTemplate and InsertItemTemplate. Inside ItemTemplate, we have 2 buttons. One ImageButton to display the image and a LinkButton to Delete the Image.The ImageButton binds to the current iterated item of the DataSource using Container.DataItem as ImageUrl. The same url is also used as CommandArgument which will be passed to the EventArgs while firing OnCommand, which is handled by itemImageButton_Command in order to show the Full Image.

The LinkButton is not Visible by default. It will be visible only when the AdminMode is enabled (We’ll soon cover how to set AdminMode). This check is handled in the Load event of the LinkButton.

Similarly the InsertItemTemplate is also visible only when the AdminMode is enabled.

In other words, If AdminMode is set then you can upload new Image as well as delete existing Images. If it is not set then all you see is the 320×240 icons of the Images in the ImageFolderPath (We’ll also cover how to set the ImageFolderPath in a moment). All can do is click on the image icon and it will display the FullImage.

Step-4 : Image Grid Code-Behind

Now it is time to write the Code-Behind.  Instead of pasting the full code-behind, let us take it in steps so that we can better understand it piece-by-piece.

    public partial class ImageGrid : System.Web.UI.UserControl
    {
        private string virtualPath;
        private string physicalPath;

        /// <summary>
        /// Relative path to the Images Folder
        /// </summary>
        public string ImageFolderPath { get; set; }

        /// <summary>
        /// Title to be displayed on top of Images
        /// </summary>
        public string Title { get; set; }


        /// <summary>
        /// Get or Set the Admin Mode 
        /// </summary>
        public bool AdminMode { get; set; }

We have 2 private members to store the virtual and actual path of the Image Folder, which can be set via the public property ImageFolderPath. Any public property of a User Control (ascx) can be set via attributes.

For Example, just as you would set <asp:TextBox Text=”something ….. you would use <cc:MyCustomControlOrWhatever MyProperty=”” ……. So when you will actually use this grid in an ASPX page, it will be very simple to set any folder containing images in ImageFolderPath attribute and whether you want to display the AdminMode controls (upload and delete functionality). If you want to control AdminMode programmatically by identifying whether the user has access or not, it is just as easy, because ultimately it is just a public property of the ImageGrid class.

        /// <summary>
        /// Page load operations
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Page_Load(object sender, EventArgs e)
        {
            //Update the path
            UpdatePath();

            //Show AdminMode specific controls
            if (AdminMode)
            {
                ImageListView.InsertItemPosition = InsertItemPosition.FirstItem;
            }

        }

        /// <summary>
        /// Pre render operations
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Page_PreRender(object sender, EventArgs e)
        {
            //Binds the Data Before Rendering
            BindData();
        }

When the ImageGrid loads, it will first update the virtual and physical path by using the values inside ImageFolderPath. If AdminMode is enabled, it will enable the InsertItemTemplate. Before actual rendering, the data binding will happen and the ImageGrid will be updated with current Images. UpdatePath() and BindData() are shown in the snippet below.

/// <summary>
/// Updates the path variables
/// </summary>
private
void UpdatePath() {
  // use a default path
  virtualPath = "~/Images";
  physicalPath = Server.MapPath(virtualPath);

  // If ImageFolderPath is specified then use that path
  if (!string.IsNullOrEmpty(ImageFolderPath)) {
    physicalPath = Server.MapPath(ImageFolderPath);
    virtualPath = ImageFolderPath;
  }
}

/// <summary>
/// Binds the ImageListView to current DataSource
/// </summary>
private
void BindData() {
  ImageListView.DataSource = GetListOfImages();
  ImageListView.DataBind();
}

/// <summary>
/// Gets list of images
/// </summary>
/// <returns></returns>
private
List<string> GetListOfImages() {
  var images = new List<string>();

  try {
    var imagesFolder = new DirectoryInfo(physicalPath);
    foreach (var item in imagesFolder.EnumerateFiles()) {
      if (item is FileInfo) {
        // add virtual path of the image to the images list
        images.Add(string.Format("{0}/{1}", virtualPath, item.Name));
      }
    }
  } catch (Exception ex) {
    // log exception
    Logger.Log(ex.Message);
  }

  return images;
}

The above snippet is self-explanatory. Just note one thing that the code takes the default path as “~/Images”. In other words, if you don’t specify ImageFolderPath, then by default the code will pick up Images inside ‘Images’ folder. Another good practice would be to put all the images in such a separate folder.

Now let’s walk through some other event handlers.

/// <summary>
/// Sets Title
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected
void titleLabel_Load(object sender, EventArgs e) {
  var titleLabel = sender as Label;
  if (titleLabel == null) return;

  titleLabel.Text = Title;
}
/// <summary>
/// Enables delete functionality for Admin Mode
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected
void deleteLinkButton_Load(object sender, EventArgs e) {
  // In case of AdminMode, we would want to show the delete button
  // which is not visible by iteself for Non-Admin users
  if (AdminMode) {
    var deleteButton = sender as LinkButton;
    if (deleteButton == null) return;

    deleteButton.Visible = true;
  }
}
/// <summary>
/// Redirects to the full image when the image is clicked
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected
void itemImageButton_Command(object sender, CommandEventArgs e) {
  Response.Redirect(e.CommandArgument as string);
}

When the titleLabel control is loaded, it picks text provided via the Title property. Another customizable thing.

As explained previously, the deleteLinkButton gets visible on-the-fly if AdminMode is enabled.

When an image is clicked, the Url is passed via CommandArgument, which is then used to Redirect.

/// <summary>
/// Performs commands for bound buttons in the ImageListView. In this case
/// 'Remove (Delete)'
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected
void ImageListView_ItemCommand(object sender, ListViewCommandEventArgs e) {
  /* We have not bound the control to any DataSource derived controls,
  nor do we use any key to identify the image. Hence, it makes more sense not to
  use 'delete' but to use a custom command 'Remove' which can be fired as a
  generic ItemCommand, and the ListViewCommandEventArgs e will have
  the CommandArgument passed by the 'Remove' button In this case, it is the
  bound ImageUrl that we are passing, and making use it of to delete the
  image.*/
  switch (e.CommandName) {
    case "Remove":
      var path = e.CommandArgument as string;
      if (path != null) {
        try {
          FileInfo fi = new FileInfo(Server.MapPath(path));
          fi.Delete();

          // Display message
          Parent.Controls.Add(
              new Label(){Text = GetFileName(path) + " deleted successfully!"});

        } catch (Exception ex) {
          Logger.Log(ex.Message);
        }
      }
      break;
    default:
      break;
  }
}

The delete operation is performed by the above snippet. Here, instead of ‘Delete’ we have passed a custom ‘Remove’ command, which seems odd. This is done order to utilize the CommandArgument which is not available had we used the ‘Delete’ command.

/// <summary>
/// Saves the Posted File
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected
void imageUpload_Load(object sender, EventArgs e) {
  // Get the required controls
  var imageUpload = sender as FileUpload;
  if (imageUpload == null) return;

  var parent = imageUpload.Parent;
  if (parent == null) return;

  var imageUploadStatus = parent.FindControl("imageUploadStatusLabel") as Label;
  if (imageUploadStatus == null) return;

  // If a file is posted, save it
  if (this.IsPostBack) {
    if (imageUpload.PostedFile != null &&
        imageUpload.PostedFile.ContentLength > 0) {
      try {
        imageUpload.PostedFile.SaveAs(
            string.Format("{0}\\{1}", physicalPath,
                          GetFileName(imageUpload.PostedFile.FileName)));
        imageUploadStatus.Text =
            string.Format("Image {0} successfully uploaded!",
                          imageUpload.PostedFile.FileName);
      } catch (Exception ex) {
        Logger.Log(ex.Message);
        imageUploadStatus.Text = string.Format("Error uploading {0}!",
                                               imageUpload.PostedFile.FileName);
      }
    } else {
      imageUploadStatus.Text = string.Empty;
    }
  }
}

When the FileUpload control is loaded and IsPostBack is true, it checks for the PostedFile property which contains uploaded image. The Image is saved in the physical path which was set initially.


/// <summary>
/// Get File Name
/// </summary>
/// <param name="path">full path</param>
/// <returns>string containing the file name</returns>
private
string GetFileName(string path) {
  DateTime timestamp = DateTime.Now;
  string fileName = string.Empty;
  try {
    if (path.Contains('\\')) fileName = path.Split('\\').Last();
    if (path.Contains('/')) fileName = path.Split('/').Last();
  } catch (Exception ex) {
    Logger.Log(ex.Message);
  }
  return fileName;
       }

The above snippet show the GetFileName helper method used in some of the handlers.

Whew! Finally… it is time to use the control !!!

Step-5 : ImageGrid in Action

All this lengthy and confusing work will finally pay off Smile You just need to add the following code in your page. We had added a Default.aspx. Paste the following markup in there.

Just below the page directive:

<%@ Register TagPrefix="cc" TagName="ImageControl" Src="~/Controls/ImageGrid.ascx" %>

Inside the body (or Content control if you have been using MasterPages):

        <cc:ImageControl runat="server" ID="MyImageGrid"  />

Now, put some images inside the Image folder and hit F5. I have kept some of my personal photos in the Images folder and here is the output:

If you want to specify a different path and give a title, just use the following snippet instead:


<cc:ImageControl runat="server" ID="MyImageGrid" ImageFolderPath="~/Images/Wallpapers" 
                 Title="C# Wallpapers" AdminMode="true" />

Now I have specified my C# wallpapers path. Additionally, I have also specified AdminMode=”true”. With that in place, the result would look like the following:

Now you can see the Title, Image Upload control, as well as Delete Links.

I believe you would want to control AdminMode programmatically, like the following snippet:


    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if(User.IsInRole("Administrator")) MyImageGrid.AdminMode = true;
        }

    }

Before using this last piece, however, make sure you have setup Roles and the current user accessing the page is in ‘Administrator’ role. Of course, Membership and Roles are totally different topics and out of the scope of this tutorial.

Download the Source Code

You can download the source code from the following location:

Well, I hope you have enjoyed this tutorial. If so, please do comment.

Thank You!

This article is part of the GWB Archives. Original Author: Om Talsania’s Geekypedia

Related Posts