Skip navigation

Build Your Own Photo Gallery: Part I

Getting Started

CodeTalk

LANGUAGES: C#

ASP.NET VERSIONS: 2.0

 

Build Your Own Photo Gallery: Part I

Getting Started

 

By Bipin Joshi

 

You probably preserve many moments of your life in photos. And you probably want to share those moments with your friends and relatives. A photo gallery is the perfect place to share your snapshots with others. This three-part series will demonstrate how you can build a photo gallery complete with categories, thumbnails, and security using ASP.NET 2.0 and SQL Server 2005. In this first part we ll start with database design and a few utility classes.

 

Functional Requirements

Let s first summarize our expectations for the photo gallery application we are going to build:

  • The administrator should be able to create albums in the system.
  • The administrator should be able to mark the albums as public or private.
  • The administrator should be able to upload photos to the albums.
  • The photos should be stored in a SQL Server database and not as physical files.
  • The administrator should be able to specify attributes for the photos, such as title and description.
  • The photo thumbnails should be generated automatically as per the specified dimensions without any need to upload small images.
  • The full photo is to be displayed when a user clicks on the thumbnail.

 

ASP.NET Feature Illustration

We will use ASP.NET 2.0 and SQL Server 2005 Express Edition to develop the photo gallery. The application will illustrate:

  • How to store and retrieve images in a SQL Server database.
  • How to display images in data bound controls such as DataList and FormView.
  • How to create thumbnail images on the fly.
  • How to work with a file upload control to upload files on the server.
  • How to implement security using Forms authentication, Membership, Roles, and login controls.

 

Creating the Web Application

To begin, create a new ASP.NET Web site using Visual Studio .NET or Visual Web Developer. Figure 1 shows the New Web Site dialog box of Visual Studio .NET.

 


Figure 1: Creating a new Web site in VS.NET.

 

All the administrative pages are stored in a folder named Admin; other pages are stored directly under the virtual root. Create a folder named Admin inside the application folder.

 

Database Schema

All the data, including photos and album definitions, will be stored in a SQL Server 2005 database. To add a new SQL Server database in your Web site, right click on the App_Data folder and select Add New Item to open the dialog box shown in Figure 2.

 


Figure 2: The Add New Item dialog box.

 

Name the database PhotoDb.mdf. Once the database is created we can create tables in it. Figure 3 lists all the tables and their columns that we need. Figure 4 shows the database diagram of these two tables.

 

Table Name

Columns

Description

Albums

AlbumID (int, Identity)

Name (varchar)

Description (varchar)

IsPublic (byte)

DateCreated (DateTime)

 

This table stores the photo album definitions.

Photos

PhotoID (int, Identity)

AlbumID (int)

Name (varchar)

Description (varchar)

Image (Image)

ThumbnailHeight (int)

ThumbnailWidth (int)

DateCreated (DateTime)

This table stores photos and related information.

Figure 3: Create these tables and columns in PhotoDb.mdf.

 


Figure 4: Database diagram of the Albums and Photos tables.

 

Handling Data Access

To perform our database operations we use the SQL Data Source control. Data source controls act as a bridge between your database and the user interface. They help reduce the amount of code you write for data access. The SQL Data Source control also allows you to develop two-tier applications. Though most of the data access is taken care via the SQL Data Source control, at times we need to execute queries on our own. For that purpose, we ll create a class named SqlHelper that encapsulates such common tasks as opening a connection, closing a connection, and executing the queries.

 

To create the SqlHelper class, right click on your App_Code folder and select Add New Item. Then select Class from the Add New Item dialog box and name it SqlHelper. Figure 5 lists the main methods of the SqlHelper class.

 

Method

Description

public static int ExecuteNonQuery(string query)

Allows you to execute action queries without any parameters.

public static int ExecuteNonQuery(string query, SqlParameter[] p)

Allows you to execute action queries with parameters.

public static SqlDataReader ExecuteReader(string sql)

Allows you to execute SELECT queries without any parameters and get returned results in a SqlDataReader.

public static SqlDataReader ExecuteReader(string sql, SqlParameter[] p)

Allows you to execute SELECT queries with parameters and get returned results in a SqlDataReader.

public static object ExecuteScalar(string sql)

Allows you to execute queries without any parameters that return a single value.

public static object ExecuteScalar(string sql, SqlParameter[] p)

Allows you to execute queries with parameters that return a single value.

public static DataSet ExecuteDataSet(string sql)

Allows you to execute SELECT queries without any parameters and get returned results in a DataSet.

public static DataSet ExecuteDataSet(string sql, SqlParameter[] p)

Allows you to execute SELECT queries with parameters and get returned results in a DataSet.

Figure 5: Methods of the SqlHelper class.

 

As you can see, all the methods of SqlHelper are static so that you can call them without creating an instance of SqlHelper. Listing One shows the complete source code of the SqlHelper class.

 

We store the database connection string in the web.config file. Figure 6 shows the relevant markup of web.config.

 

 providerName="System.Data.SqlClient"/>

Figure 6: The database connection string in the web.config file.

 

As you can see, we use the section to store the connection string. Note the connection string format. SQL Server 2005 Express edition allows you to dynamically attach database files. The .mdf file to attach is specified via the AttachDbFileName attribute. Because we use the default authentication mode of SQL Server 2005 Express, we set the Integrated Security attribute to True.

 

In the static constructor of the SqlHelper class, we pick up the above connection string. In each of the public methods, we essentially create a new instance of the SqlConnection and SqlCommand objects. We then configure the command object by setting its properties, such as CommandText and Connection. If our query has any parameters, we also populate the Parameters collection of the command object using a private method (FillParameters). We then call the ExecuteNonQuery, ExecuteScalar, or ExecuteReader methods of the command object.

 

Web Forms and Master Pages

Create the Web forms and master pages, as shown in Figure 7.

 

Web Form Name

Folder

Description

AdminMasterPage.master

Admin

This is the master page for administrative Web forms.

AlbumManager.aspx

Admin

Manages albums; i.e., add, edit, and delete album definitions.

PhotoManager.aspx

Admin

Manages photos. Allows you to upload photos and categorize them in albums.

MasterPage.master

Root

Acts as a master page for the album and photo pages.

Default.aspx

Root

Acts as the home page of the photo gallery and displays a list of all albums in the system.

DisplayAlbum.aspx

Root

Displays thumbnails of all the images from an album, along with their names and descriptions.

DisplayPhoto.aspx

Root

Displays a selected photo, along with its name and description.

GetPhoto.aspx

Root

Gets a photo or its thumbnail from the SQL Server database.

Login.aspx

Root

Users are required to log in to access private albums. This form acts as a log-in page for private albums and administrative tasks.

Figure 7: List of Web forms used in the application.

 

Creating the PhotoHelper Utility Class

Any photo gallery application needs to frequently deal with images and their thumbnails. We ll create a utility class named PhotoHelper that allows us to retrieve photos and thumbnails from the database and display them in Web forms. To create this class, right click on the App_Code folder and choose Add New Item. Name the class PhotoHelper. Figure 8 lists the methods of this class.

 

Method

Description

public static Image GetThumbnail(int photoid)

This method accepts a photo ID and returns its thumbnail image as an Image.

public static Image GetThumbnailForAlbum(int albumid)

This method accepts an album ID and returns the thumbnail of the latest photo from that album as an Image.

public static Image GetPhoto(int photoid)

This method accepts a photo ID and returns the whole photo image in the form of an Image.

Figure 8: Methods of the PhotoHelper class.

 

Note that you need to import System.Drawing, System.Drawing.Imaging, and System.IO before you proceed with the coding of this class:

 

using System.Drawing;

using System.Drawing.Imaging;

using System.IO;

 

Let s examine all the methods of PhotoHelper; Figure 9 lists the complete source code for the GetThumbnail utility method.

 

public static Image GetThumbnail(int photoid)

{

string sql = "SELECT IMAGE,THUMBNAILHEIGHT,THUMBNAILWIDTH

 FROM PHOTOS WHERE PHOTOID=@id";

SqlParameter[] p = new SqlParameter[1];

p[0] = new SqlParameter("@id", photoid);

SqlDataReader reader = SqlHelper.ExecuteReader(sql, p);

byte[] imagedata = null;

int height = 0, width = 0;

while (reader.Read())

{

imagedata = (byte[])reader.GetValue(0);

height = reader.GetInt32(1);

width = reader.GetInt32(2);

}

reader.Close();

MemoryStream ms = new MemoryStream(imagedata);

Image bigImage = Image.FromStream(ms);

Image smallImage = bigImage.GetThumbnailImage(width,

 height, null, IntPtr.Zero);

return smallImage;

}

Figure 9: The GetThumbnail method.

 

The GetThumbnail method accepts the photo ID whose thumbnail is to be created. Then it fetches the actual image and its thumbnail height and width from the database using the ExecuteReader method of the SqlHelper class. The image data is read into a byte array called imagedata. This image is further converted into a MemoryStream. This is necessary because later we need to create an Image from this stream. The Image class has a static method named FromStream. This method accepts a stream and constructs an image out of it. The return type of the FromStream method is another class named Image. The Image class further has a method named GetThumbnailImage that creates a thumbnail of the image. The parameters to the GetThumbnailImage method are:

  • thumbWidth: Indicates the width of the thumbnail.
  • thumbHeight: Indicates the height of the thumbnail.
  • GetThumbnailImageAbort: Indicates a callback method for determining when the GetThumbnailImage method should abort the execution.
  • callbackData: Is of type IntPtr and must be zero.

 

We finally return the thumbnail Image instance back to the caller. The second method, GetThumbnailForAlbum, is shown in Figure 10.

 

public static Image GetThumbnailForAlbum(int albumid)

{

SqlParameter[] p = new SqlParameter[1];

p[0] = new SqlParameter("@albumid",albumid);

int photoid = int.Parse(SqlHelper.ExecuteScalar("SELECT TOP

 1 PHOTOID FROM PHOTOS WHERE ALBUMID=@albumid ORDER BY

 DATECREATED DESC", p).ToString());

return GetThumbnail(photoid);

}

Figure 10: The GetThumbnailForAlbum method.

 

Albums by themselves do not have a photo. However, it would be nice to have a thumbnail for the album cover. That is precisely what the GetThumbnailForAlbum method does. It simply fetches the latest photo added to the album and returns a thumbnail for it in the form of an Image.

 

Finally, Figure 11 lists the source code for the GetPhoto method. The code in Figure 11 is similar to Figure 9 but does not create a thumbnail. The GetPhoto method accepts the ID of the photo to be retrieved and returns the complete image retrieved from the database.

 

public static Image GetPhoto(int photoid)

{

string sql = "SELECT IMAGE FROM PHOTOS WHERE PHOTOID=@id";

SqlParameter[] p = new SqlParameter[1];

p[0] = new SqlParameter("@id", photoid);

SqlDataReader reader = SqlHelper.ExecuteReader(sql, p);

byte[] imagedata = null;

while (reader.Read())

{

imagedata = (byte[])reader.GetValue(0);

}

reader.Close();

MemoryStream ms = new MemoryStream(imagedata);

Image bigImage = Image.FromStream(ms);

return bigImage;

}

Figure 11: The GetPhoto method.

 

Creating Master Pages

We need two master pages. One, AdminMasterPage.master, for administrative pages, and the other, MasterPage.master, for the remaining pages. Figure 12 shows the general layout of AdminMasterPage.master.

 


Figure 12: Layout of AdminMasterPage.master.

 

To create this master page, add a new Master Page in the Admin folder using the Add New Item dialog box (again, see Figure 1); name it AdminMasterPage.master. Figure 13 shows the markup of this master page.

 

<%@ Master Language="C#" AutoEventWireup="true" CodeFile=

 "AdminMasterPage.master.cs" Inherits="AdminMasterPage" %>

 "~/Images/logo.gif" NavigateUrl="~/Admin/Default.aspx">

 DynamicHorizontalOffset="2"

Font-Names="Verdana" Font-Size="12px" ForeColor="#7C6F57"

 StaticSubMenuIndent="10px"

Width="100%">

 Text="Manage Albums" Value="Manage Albums">

 Text="Manage Photos" Value="Manage Photos">

 runat="server">



Figure 13: Markup of AdminMasterPage.master.

 

The master page has a HyperLink at the top. The ImageUrl property of the hyperlink is pointed to the logo of the Web site. To allow the administrator to navigate through the administrative pages we use a menu control. To design the navigation menu, drag and drop a Menu control onto the master page. Open the Menu Item Editor from the smart tags of the Menu control by choosing Edit Menu Items (see Figure 14). Add two menu items and set their properties as shown in Figure 15.

 


Figure 14: The Menu Item Editor.

 

Menu Item For

Text

NavigateUrl

Managing album definitions

Manage Albums

~/admin/albummanager.aspx

Managing photos

Manage Photos

~/admin/photomanager.aspx

Figure 15: Configuring the menu.

 

Because AdminMasterPage.master is applied only to administrative pages, we put code to ensure that only administrative users can access such pages. Figure 16 shows the Page_Load event handler of AdminMasterPage.master.

 

protected void Page_Load(object sender, EventArgs e)

{

if (!Roles.IsUserInRole("ADMIN"))

{

throw new Exception("You are not allowed

 to access this page!");

}

}

Figure 16: Page_Load of AdminMasterPage.master.

 

Here we check if the user belongs to a role called ADMIN using the IsUserInRole method of the Roles object. If the IsUserInRole method returns false, we throw an exception.

 

The MasterPage.master looks similar to AdminMasterPage.master, but does not contain the menu. Figure 17 shows the markup of this Web form.

 

<%@ Master Language="C#" AutoEventWireup="true"

 CodeFile="MasterPage.master.cs" Inherits="MasterPage" %>

 ImageUrl="~/Images/logo.gif" NavigateUrl="~/Default.aspx">

  [HyperLink1]

 runat="server">



Figure 17: Markup of MasterPage.master.

 

As before, the master page also has a HyperLink at the top. The ImageUrl property of the hyperlink is pointed to the logo of the Web site and the NavigateUrl property points to Default.aspx.

 

Conclusion

In the first of this three-part series we designed SQL Server database tables that will be used further to store photos. We also developed two utility classes, SqlHelper and PhotoHelper. The former will be used for all database interactions and the latter will be used for generating thumbnails and retrieving photos. We also developed two master pages, one for administrative pages and one for album pages. We also used role-based security to ensure that only administrators can load administrative pages. In Part II we ll develop the administrative pages for managing albums and photos.

 

The sample code for this series is available for download.

 

Bipin Joshi is the founder and owner of BinaryIntellect Consulting (http://www.binaryintellect.com), where he conducts professional training programs on .NET technologies. He is the author of Developer s Guide to ASP.NET 2.0 (http://www.binaryintellect.com/books) and co-author of three WROX books on .NET 1.x. He writes regularly for http://www.DotNetBips.com, a community Web site he founded in the early days of .NET. He is a Microsoft MVP, MCAD, MCT, and member of ASPInsiders. He jots down his thoughts about .NET, life, and Yoga at http://www.bipinjoshi.com. He also conducts workshops on Yoga and Meditation, where he helps IT professionals develop a positive personality. You can contact him at mailto:[email protected].

 

Begin Listing One the SqlHelper class

using System;

using System.Data;

using System.Data.SqlClient;

public class SqlHelper

{

 private static string strConn;

static SqlHelper()

{

strConn = ConfigurationManager.ConnectionStrings[

 "ConnectionString"].ConnectionString;

}

public static int ExecuteNonQuery(string query)

{

SqlConnection cnn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(query, cnn);

cnn.Open();

int retval = cmd.ExecuteNonQuery();

cnn.Close();

return retval;

}

public static int ExecuteNonQuery(string query,

 SqlParameter[] p)

{

SqlConnection cnn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(query, cnn);

FillParameters(cmd, p);

cnn.Open();

int retval = cmd.ExecuteNonQuery();

cnn.Close();

return retval;

}

public static SqlDataReader ExecuteReader(string sql)

{

SqlConnection cnn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(sql, cnn);

return cmd.ExecuteReader(CommandBehavior.CloseConnection);

}

public static SqlDataReader ExecuteReader(

 string sql, SqlParameter[] p)

{

SqlConnection cnn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(sql, cnn);

FillParameters(cmd, p);

cnn.Open();

return cmd.ExecuteReader(CommandBehavior.CloseConnection);

}

public static object ExecuteScalar(string sql)

{

SqlConnection cnn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(sql, cnn);

cnn.Open();

object retval = cmd.ExecuteScalar();

cnn.Close();

return retval;

}

public static object ExecuteScalar(string sql,

 SqlParameter[] p)

{

SqlConnection cnn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(sql, cnn);

FillParameters(cmd, p);

cnn.Open();

object retval = cmd.ExecuteScalar();

cnn.Close();

return retval;

}

public static DataSet ExecuteDataSet(string sql)

{

SqlConnection cnn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(sql, cnn);

SqlDataAdapter da = new SqlDataAdapter();

da.SelectCommand = cmd;

DataSet ds = new DataSet();

da.Fill(ds);

return ds;

}

public static DataSet ExecuteDataSet(string sql,

 SqlParameter[] p)

{

SqlConnection cnn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(sql, cnn);

FillParameters(cmd, p);

SqlDataAdapter da = new SqlDataAdapter();

da.SelectCommand = cmd;

DataSet ds = new DataSet();

da.Fill(ds);

return ds;

}

private static void FillParameters(SqlCommand cmd,

 SqlParameter[] parameters)

{

for (int i = 0; i < parameters.Length ; i++)

{

cmd.Parameters.Add(parameters[i]);

}

}

}

End Listing One

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish