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

Simple Photo Gallery With ASP.NET 2.0

4.60/5 (20 votes)
30 May 20067 min read 1   12.9K  
An article on creating a simple photo gallery that is easy to maintain and aesthetically pleasing
Sample Image - Nileblitz_Photo_Gallery.jpg

Introduction

A while ago, I spent considerable time scouring the net, looking for my perfect photo gallery. I tried the different solutions already available on the net, but for one reason or another, I was unable to fit them to my needs.

  • I needed a photo gallery that "just works." All you need to do is plunk a couple of pictures in a folder and that's that.
  • The gallery also needed to be aesthetically pleasing.
  • The gallery should be easy to use.

I decided to give it a try myself. Within a weekend, I believe, I created something that fit all my needs. Hopefully it will fit yours, too.

Step 1

The first step is to create a "New Website..." project in Visual Studio .NET 2005. After the creation is complete, you will have a Default.aspx file and a web.config file. Create a folder PicStore in the root. This is the folder that will hold your galleries.

Inside the PicStore folder, create a Default sub-folder. We are creating this to denote the default set of galleries. This is for the future, when I plan to expand the application to include the galleries of my friends. They will reside in other folders, but the Default set of galleries will be loaded when the application is first browsed by the user.

Inside the Default folder, create your galleries. I suggest, for the sake of the article, creating Photogallery 1, Photogallery 2, etc. In each of these galleries, create a pics folder. This folder will hold your pictures.

Note:

  1. Only JPG file format is accepted.
  2. Photos have to be named in the pattern 001.JPG, 002.JPG, 003.JPG, ...
  3. If you want to include a title for any photo, rename it to the pattern 00X~Title_With_Underscores_For_Spaces.JPG, where X is the number of the photo.

The gallery has been designed to neatly show pictures that are a maximum of 750 pixels wide. You can, of course, put bigger or smaller pictures, but you then need to adjust the background accordingly. I would suggest to keep all the pictures of the same maximum width. You could use the free tool Irfanvie to batch format all your images to a particular size. There is an option in "Batch conversion> Advanced options" to set the long side of all images to a particular size. This fitted my requirements perfectly.

Optionally, you could also create a sibling folder thumbs to the pics folder, if you want the users to be able to see the thumbnails of your pictures instead of the image names in the navigation. I would suggest that the long side of your thumbnails should be limited to 100 pixels.

There are some images like arrow2.gif, bg.gif, etc. provided in my sample code. You could download these and put them in the appropriate folders in the project root, or alternatively, remove references to these from the code.

The final thing that we have to do is add the keys PicRootPath and PicRootDefaultPath to the web.config, as follows:

XML
<appSettings>
    <add key="PicRootPath" value="~/PICStore/"/>
    <add key="PicRootDefaultPath" value="~/PicStore/Default/" />
</appSettings>

That was the preparation of the project. Now, we will add code for the gallery.

Step 2

Now, we create an App_Code folder in the project, if it doesn't exist already. Inside this, we will create a class ContentInfoLoader. This class will be responsible for loading information about the galleries and the photos for our application. The constructor of our class will load the root path of the galleries and the default set of galleries. Following is the code for this:

C#
Configuration rootWebConfig = 
    WebConfigurationManager.OpenWebConfiguration("~/");
if(0<rootWebConfig.AppSettings.Settings.Count)
{
    KeyValueConfigurationElement picRootElement = 
        rootWebConfig.AppSettings.Settings["PicRootPath"];
    if(null!=picRootElement)
    {
        _picRootPath=picRootElement.Value;
    }

    picRootElement = rootWebConfig.AppSettings.Settings["PicRootDefaultPath"];
    if (null != picRootElement)
    {
        _picRootDefaultPath = picRootElement.Value;
    }
}

The paths are stored in private variables which can be accessed via the readonly properties. Now we create a simple method to get the list of galleries available in our set. Following is the code for the same:

C#
public string[] GetGalleryPaths(string picRootRealPath) 
{
    if (Directory.Exists(picRootRealPath))
    {
        return Directory.GetDirectories(picRootRealPath);
    }
    else
    {
        return null;
    }
}

As you can see, the method takes the path for the root of the pictures (it could be the default one or any other) and it returns a string array containing the gallery paths. Note that these are physical paths. We need to create another method to get a list of photos in each of the galleries currently selected. The following is the code that does this:

C#
public string[] GetPhotoList(string galleryName, string picRootRealPath)
{
    string galleryPath=picRootRealPath + "\\" + galleryName + "\\pics";
    if (Directory.Exists(galleryPath))
    {
        return Directory.GetFiles(galleryPath,"*.JPG");
    }
    else
    {
        return null;
    }
}

Note that this method takes the name of the gallery and the root path. It returns a list of photos with their physical paths.

Step 3

Now we create a master page to go with our gallery application page.

First, I would suggest deleting the Default.aspx file that was automatically created by the project. Next, right click on the project in the Solution Explorer and click on "Add New Item..." Choose "Master Page" and name it SuperMasterPage.master. Add the following JavaScript code to your master page, just after the body tag:

JavaScript
<script type="text/javascript">

    var offsetfromcursorX=12 //Customize x offset of tooltip
    var offsetfromcursorY=10 //Customize y offset of tooltip

    //Customize x offset of tooltip DIV relative to pointer image
    var offsetdivfrompointerX=10
    //Customize y offset of tooltip DIV relative 
    //to pointer image. Tip: Set it to 
    //(height_of_pointer_image-1).
    var offsetdivfrompointerY=7

    //write out tooltip DIV
    document.write('<div id="dhtmltooltip"></div>')
    //write out pointer image
    document.write('<img id="dhtmlpointer" ' + 
        'src="http://www.codeproject.com/SourceCode/images/arrow2.gif">')

    var ie=document.all
    var ns6=document.getElementById && !document.all
    var enabletip=false
    if (ie||ns6)
    var tipobj=document.all? document.all["dhtmltooltip"] : 
        document.getElementById? 
        document.getElementById("dhtmltooltip") : ""

    var pointerobj=document.all? document.all["dhtmlpointer"] : 
        document.getElementById? 
        document.getElementById("dhtmlpointer") : ""

    function ietruebody()
    {
        return (document.compatMode && document.compatMode!="BackCompat")? 
            document.documentElement : document.body
    }

    function ddrivetip(thetext, thewidth, thecolor)
    {
        if (ns6||ie)
        {
            if (typeof thewidth!="undefined")
                tipobj.style.width=thewidth+"px"
            if (typeof thecolor!="undefined" && thecolor!="")
                tipobj.style.backgroundColor=thecolor
                tipobj.innerHTML=thetext
                enabletip=true
                return false
        }
    }

    function positiontip(e)
    {
        if (enabletip)
        {
            var nondefaultpos=false
            var curX=(ns6)?e.pageX : event.clientX+ietruebody().scrollLeft;
            var curY=(ns6)?e.pageY : event.clientY+ietruebody().scrollTop;
            //Find out how close the mouse is to the corner of the window
            var winwidth=ie&&!window.opera? ietruebody().clientWidth : 
                window.innerWidth-20
            var winheight=ie&&!window.opera? 
                ietruebody().clientHeight : window.innerHeight-20
            var rightedge=ie&&!window.opera? 
                winwidth-event.clientX-offsetfromcursorX : 
                winwidth-e.clientX-offsetfromcursorX
            var bottomedge=ie&&!window.opera? 
                winheight-event.clientY-offsetfromcursorY : 
                winheight-e.clientY-offsetfromcursorY
            var leftedge=(offsetfromcursorX<0)? 
                offsetfromcursorX*(-1) : -1000

            //if the horizontal distance isn't enough to 
            //accomodate the width of the context menu
            if (rightedge<tipobj.offsetWidth)
            {
                //move the horizontal position of 
                //the menu to the left by it's width
                tipobj.style.left=curX-tipobj.offsetWidth+"px"
                nondefaultpos=true
            }
            else if (curX<leftedge)
                tipobj.style.left="5px"
            else
            {
                //position the horizontal position of 
                //the menu where the mouse is positioned
                tipobj.style.left=curX+offsetfromcursorX-
                    offsetdivfrompointerX+"px"
                pointerobj.style.left=curX+offsetfromcursorX+"px"
            }

            //same concept with the vertical position
            if (bottomedge<tipobj.offsetHeight)
            {
                tipobj.style.top=curY-tipobj.offsetHeight-
                    offsetfromcursorY+"px"
                nondefaultpos=true
            }
            else
            {
                tipobj.style.top=curY+offsetfromcursorY+
                    offsetdivfrompointerY+"px"
                pointerobj.style.top=curY+offsetfromcursorY+"px"
            }
            tipobj.style.visibility="visible"
            if (!nondefaultpos)
                pointerobj.style.visibility="visible"
            else
                pointerobj.style.visibility="hidden"
        }
    }

    function hideddrivetip()
    {
        if (ns6||ie)
        {
            enabletip=false
            tipobj.style.visibility="hidden"
            pointerobj.style.visibility="hidden"
            tipobj.style.left="-1000px"
            tipobj.style.backgroundColor=''
            tipobj.style.width=''
        }
    }

    document.onmousemove=positiontip

</script>

This JavaScript code will be used to display a tooltip for showing the gallery names and thumbnails.

Note: Wherever you see "/SourceCode/" in the code, please replace it with whatever is the "virtual directory" in which you have created your application. If you have created it in the root of your machine website, remove the /SourceCode/ where necessary.

Step 4

Now we need to create our actual gallery display page so that it can use the class that we created above. Right click on your project and click on "Add New Item..." Choose a "Web Form" and make sure that the checkboxes "Place code in a separate file" and "Select master pages" are checked. Open the PicViewer.aspx source. You will see an asp:content tag. Inside this tag, place the following code:

HTML
<table width="760px" cellpadding="1" cellspacing="0">
    <tr>
        <td colspan="5"><asp:PlaceHolder ID="galleryPlaceholder" 
            runat="server"></asp:PlaceHolder>
        </td>
    </tr>
    <tr>
        <td bgcolor="#494949" width="700px">
            <asp:Label ID="lblGalleryName" runat="server" 
                CssClass="SubHeadingWhite">&#9660; How to use</asp:Label>
        </td>
        <td bgcolor="#494949"></td>
        <td bgcolor="#494949"><asp:Label ID="lblPhotoName" 
            runat="server" CssClass="SubTitleWhite"></asp:Label></td>
    </tr>
    <tr>
        <td align="center" colspan="5"><asp:Image ID="galleryImage" 
            runat="server" BorderStyle="Solid" 
            BorderWidth="5px" BorderColor="Black" />
        </td>
    </tr>
</table>

The above code places a placeholder called galleryPlaceholder, to which we will add the gallery and photo navigation bars later. Apart from that, you can also see the label, which will show the title of the picture if any, or else shows the number of the picture.

When the PicViewer page is loaded, we load the navigation of the galleries. If this is the first time (no gallery is being displayed), we show the Howtouse.jpg instruction photo. If this is not the first time, i.e. the user has clicked on a gallery, we show the first picture. If the user has clicked on a picture, we show the picture and the title. Following is the code of the Page_Load subroutine:

C#
protected void Page_Load(object sender, EventArgs e)
{
    ContentInfoLoader cil = new ContentInfoLoader();
    string galleriesPath = "";
    if (Session["PicRootPath"] != null && 
        Session["PicRootPath"].ToString() != "")
    {
        galleriesPath = Session["PicRootPath"].ToString();
    }
    else
    {
        galleriesPath = cil.PicRootDefaultPath;
        Session["PicRootPath"] = galleriesPath;
    }
     
    LoadGalleriesNav(galleriesPath,cil);
    if (Request["gallery"] != "" && Request["gallery"] != null)
    {
        lblGalleryName.Text = Request["gallery"];
        Int32 photoCount = 
            LoadPhotosNav(Request["gallery"], galleriesPath, cil);
        string photoName;
        if (Request["photo"] == "" || Request["photo"] == null)
        {
            //If the user has just clicked on
            //the gallery, load the first photo
            photoName = cil.GetPhotoList(Request["gallery"], 
                Server.MapPath(galleriesPath))[0];
            photoName = photoName.Substring(photoName.LastIndexOf("\\"));
        }
        else
        {
            //Otherwise load the photo as in the querystring
            photoName = Request["photo"];
        }
        char[] charSeparators = new char[] { '~', '.' };
        if (photoName.Contains("~"))
        {
            string photoTitle = photoName.Split(charSeparators)[1];
            lblPhotoName.Text = photoTitle.Replace("_", " ");
        }
        else
        {
            lblPhotoName.Text = photoName.Split(charSeparators)[0];
        }
        galleryImage.ImageUrl = galleriesPath + 
            Request["gallery"] + "/pics/" + photoName;
    }
    else
    {
        galleryImage.ImageUrl = galleriesPath + "Howtouse.jpg";
    }
}

The navigation for the gallery and photos are basically unordered lists formatted using CSS. There are some major differences between the gallery list and the photos list, apart from the obvious size, shape and color:

  • Gallery list, when we mouse-over, we see the name of the gallery.
  • Photo list, when we mouse-over, we see the title if the thumbs directory doesn't exist, else we see the thumbnail and the title.

In both navigations, if a particular gallery is selected or a particular gallery and a photo is selected, those items are grayed out and cannot be selected. Apart from this, most of the code is quite simple and self-explanatory. Please let me know if further explanation is required. Following is the code:

C#
/// <summary>
/// This method is supposed to load the navigation for the galleries
/// </summary>
public void LoadGalleriesNav(string galleriesPath, ContentInfoLoader cil)
{
    string[] galleryPathList = new string[1];

    // Load the path info for all the galleries
    if (galleriesPath != "")
    {
        galleryPathList = 
            cil.GetGalleryPaths(Server.MapPath(galleriesPath));
    }

    // Generate the unordered list
    HtmlGenericControl blstGalleries = new HtmlGenericControl("ul");
    blstGalleries.Attributes.Add("id", "navlist_a");

    // Generate the navigation for the galleries
    foreach (string galleryPath in galleryPathList)
    {
        string galleryName = 
            galleryPath.Substring(galleryPath.LastIndexOf("\\") + 1);
        HtmlGenericControl galleryListItem = new HtmlGenericControl("li");
        HtmlAnchor galleryAnchor = new HtmlAnchor();
        if (Request["gallery"] != "" && Request["gallery"] != null)
        {
            // If the gallery has not been chosen, generate a link
            if (Request["gallery"].ToUpper() != galleryName.ToUpper())
            {
                galleryAnchor.Attributes.Add("onMouseOver", "ddrivetip('" + 
                    galleryName.ToUpper() + "', " + 
                    galleryName.Length * 7 + ")");
                galleryAnchor.Attributes.Add("onMouseOut", "hideddrivetip()");
                galleryAnchor.HRef = "PicViewer.aspx?gallery=" + galleryName;
                galleryAnchor.InnerHtml = "<em></em>";
            }
            else
            {
                // if a gallery has been chosen
                // already then gray out the selection
                galleryAnchor.Disabled = true;
                galleryAnchor.InnerHtml = "<em style='border-top" + 
                    ":1em solid #696969'></em>";
            }
        }
        else
        {// If the gallery has not been chosen, generate a link
            galleryAnchor.Attributes.Add("onMouseOver", "ddrivetip('" + 
                galleryName.ToUpper() + "', " + 
                galleryName.Length * 7 + ")");
            galleryAnchor.Attributes.Add("onMouseOut", "hideddrivetip()");
            galleryAnchor.HRef = "PicViewer.aspx?gallery=" + galleryName;
            galleryAnchor.InnerHtml = "<em></em>";
        }
        // add the anchor to the list
        galleryListItem.Controls.Add(galleryAnchor);
        blstGalleries.Controls.Add(galleryListItem);
    }
    galleryPlaceholder.Controls.Add(blstGalleries);
}


/// <summary>
/// Load the navigation for the photos
/// </summary>
public Int32 LoadPhotosNav(string galleryName, 
    string galleriesPath, ContentInfoLoader cil)
{
    string[] photoList = new string[1];
    if (galleriesPath!= "")
    {
        photoList = cil.GetPhotoList(galleryName, 
            Server.MapPath(galleriesPath));
    }
    if (photoList != null && photoList.Length != 0)
    {
        HtmlGenericControl blstPhotos = new HtmlGenericControl("ul");
        blstPhotos.Attributes.Add("id", "navlist_b");

        foreach (string iPhotoName in photoList)
        {
            string photoName;
            photoName = 
                iPhotoName.Substring(iPhotoName.LastIndexOf("\\") + 1);
            HtmlGenericControl photoListItem = new HtmlGenericControl("li");
            HtmlAnchor photoAnchor = new HtmlAnchor();
            string originalPhotoName = photoName;
            char[] charSeparators = new char[] { '~','.' };
            string photoTitle = "";
            if (photoName.Contains("~"))
            {
                photoTitle = photoName.Split(charSeparators)[1];
                photoName = photoName.Split(charSeparators)[0] + ".JPG";
            }

            if (Request["photo"] != "" && Request["photo"] != null)
            {
                // if a photo has been selected then we need
                // to show that square as gray otherwise
                // clickable and mouseover effects.
                if (Request["photo"].ToUpper() != photoName.ToUpper())
                {
                    if (Directory.Exists(Server.MapPath(galleriesPath + 
                        galleryName + "/thumbs/")))
                    {
                        photoAnchor.Attributes.Add("onMouseOver", 
                            "ddrivetip('<img src=\\'" + 
                            ResolveUrl(galleriesPath) + galleryName + 
                            "/thumbs/" + photoName + 
                            "\\' /><br>" + 
                            photoTitle.Replace("_", " ") + "', " + 
                            photoTitle.Length + ")");
                    }
                    else
                    {
                        if (photoTitle != "")
                        {
                            photoAnchor.Attributes.Add("onMouseOver", 
                                "ddrivetip('" + photoTitle.Replace("_", 
                                " ") + "', " + photoTitle.Length + ")");
                        }
                        else
                        {
                            photoAnchor.Attributes.Add("onMouseOver", 
                                "ddrivetip('" + photoName + "', " + 
                                photoName.Length + ")");
                        }
                    }

                    photoAnchor.Attributes.Add("onMouseOut", 
                        "hideddrivetip()");
                    photoAnchor.HRef = "PicViewer.aspx?gallery=" + 
                        galleryName + "&photo=" + originalPhotoName;
                    photoAnchor.InnerHtml = "<em></em>";
                }
                else
                {
                    photoAnchor.Disabled = true;
                    photoAnchor.InnerHtml = "<em style='border-top" + 
                        ":0.5em solid #696969'></em>";
                }
            }
            else
            {
                if (Directory.Exists(Server.MapPath(galleriesPath + 
                    galleryName + "/thumbs/")))
                {
                    photoAnchor.Attributes.Add("onMouseOver", 
                        "ddrivetip('<img src=\\'" + 
                        ResolveUrl(galleriesPath) + 
                        galleryName + "/thumbs/" + photoName + 
                        "\\' /><br>" + photoTitle.Replace("_", 
                        " ") + "', " + photoTitle.Length + ")");
                }
                else
                {
                    if (photoTitle != "")
                    {
                        photoAnchor.Attributes.Add("onMouseOver", 
                            "ddrivetip('" + 
                            photoTitle.Replace("_", " ") + "', " + 
                            photoTitle.Length + ")");
                    }
                    else
                    {
                        photoAnchor.Attributes.Add(
                            "onMouseOver", "ddrivetip('" + 
                            photoName + "', " + photoName.Length + ")");
                    }
                }

                photoAnchor.Attributes.Add("onMouseOut", "hideddrivetip()");
                photoAnchor.HRef = "PicViewer.aspx?gallery=" + galleryName + 
                                   "&photo=" + originalPhotoName;
                photoAnchor.InnerHtml = "<em></em>";
            }
            photoListItem.Controls.Add(photoAnchor);
            blstPhotos.Controls.Add(photoListItem);
        }
        galleryPlaceholder.Controls.Add(blstPhotos);
        return photoList.Length;
    }
    else
    {
        return 0;
    }
}

The application is now ready for testing.

How to Install the Demo/Source Code

Please note that you have to have .NET Framework 2.0 installed, and your website should be set to use ASP.NET 2.0 (IIS website properties).

  • Create a virtual directory SourceCode on your IIS.
  • Extract the contents of demo/sourcecode in the corresponding physical folder.

Known Issues

Among the known issues are the following:

  • You cannot add more than 999 photos in a gallery.
  • When the gallery is loaded, the navigation square for the first photo is not grayed out.
  • You cannot provide a description of the photo, only a title.
  • There is no user-friendly photo upload utility.

I am sure there are other things that need to be modified or some other functionality that needs to be added. Please let me know.

And Finally...

As I upgrade the application, I will also upgrade this article. So, watch this space. Feel free to send me your comments on this article. Please let me know whether the article was helpful to you or whether some improvement is needed.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here