Click here to Skip to main content
Click here to Skip to main content

C# Photo Album Viewer

, 1 Apr 2012
Rate this:
Please Sign up or sign in to vote.
A C# Windows application to store and retrieve photos from a SQL Server database.

Sample Image - AlbumViewer.jpg

C# Photo Album Viewer

This sample is a C# Windows application to demonstrate storing and retrieving of images from a database.

Although, this article and code has been updated to support .NET 4.0 and SQL Server 2012 it was previously build for .NET 1.0 and SQL Server 2000 so all methods and techniques should work in previous versions of those applications with little or no modifications.   

The Database Tables and Stored Procedures 

The download file contains the script to create the database and stored procedures necessary for this demo application.

Initial Load

After the main form for this application is initialized the first thing necessary is to obtain any existing Albums and Photos and load them in the TreeView.

private void LoadAlbums()
{
    // Get all albums, including photos, from the database
    ReadOnlyCollection<album> albums = Data.GetPhotoAlbums();

    // Now iterate through them and add to treeview
    foreach(Album album in albums)
    {
        TreeNode albumNode = new TreeNode(album.Name);
                
        // Add the album struct to the Tag for later
        // retrieval of info without database call
        albumNode.Tag = album;

        treeAlbums.Nodes.Add(albumNode);

        // Add each photo in album to treenode for the album
        foreach(Photo photo in album.Photos)
        {
            TreeNode photoNode = new TreeNode(photo.Name);
            photoNode.Tag = photo;

            albumNode.Nodes.Add(photoNode);
        }                
    }
}

This is fairly straight forward. The TreeNode Tag object is being set to the struct so that later when selecting and item there is no additional database calls necessary to populate the display controls.

Obtaining the via the Data.GetPhotoAlbums method is, again, a straight forward ADO.NET implementation.

public static ReadOnlyCollection<album> GetPhotoAlbums()
{
    List<album> albums = new List<album>();
    using(SqlConnection conn = new SqlConnection(ConnectionString))
    {
        using(SqlCommand cmd = new SqlCommand("GetAlbums", conn))
        {
            cmd.CommandType = System.Data.CommandType.StoredProcedure;
            conn.Open();

            // Use using here so SqlDataReader will be closed automatically
            using(SqlDataReader reader = cmd.ExecuteReader())
            {
                while(reader.Read())
                {
                    albums.Add(new Album()
                    {
                        Id = reader.GetInt32(0),
                        Name = reader.GetString(1),
                        Description = reader.GetString(2)
                    }
                    );
                }
            }
        }

        // Now get all the photos for each each album
        // This could be obtained by a single query with multiple
        // resultsets but for illustrative purposes it is broken
        // into two processes
        using(SqlCommand cmd = new SqlCommand("GetPhotosByAlbum", conn))
        {
            cmd.CommandType = System.Data.CommandType.StoredProcedure;
            cmd.Parameters.Add("@albumId", SqlDbType.Int);
            for(int x = 0; x < albums.Count; x++)
            {
                cmd.Parameters["@albumId"].Value = albums[x].Id;

                List<photo> photos = new List<photo>();
                // Use using here so SqlDataReader will be closed automatically
                using(SqlDataReader reader = cmd.ExecuteReader())
                {
                    while(reader.Read())
                    {
                        photos.Add(new Photo()
                            {
                                Id = reader.GetInt32(0),
                                Name = reader.GetString(1),
                                Description = reader.GetString(2),
                                Image = (byte[])reader.GetValue(3)
                            }
                        );
                    }
                }

                // Annoying because
                // albums[x].Photos = photos.AsReadOnly();
                // produces the error, Cannot modify the return value of xxx because it is not a variable
                // The error could be avoided by using class rather than struct
                Album temp = albums[x];
                temp.Photos = photos.AsReadOnly();
                albums[x] = temp;
            }
        }
    }

    return albums.AsReadOnly();
}

The first step is to initialize the SqlConnection and SqlCommand objects. Then execute the store procedure and use a SqlDataReader to iterate through the results and create a collection of Albums to return. After this the Photos are added to each Album in a similar manner. As noted, this could be done with one store procedure, however, to illustrate the process it has been broken in to two methods.

Storing the images

After selecting the Add Photo context menu item a multi select FileOpenDialog allows images to be selected for addition. For each file a System.IO.FileStream is created and used to read into a byte array. The byte array is then passed to a method which uses it as an input parameter for a stored procedure to add it to an image field of the database table.

The last step is to add the image to the treeview. A helper class call TreeItem is used for this purpose. This class stores the description and database index id of the image. This class is also used for albums so an enum defines the type of object it is. After a new instance of this class is created it is assigned to the Tag member of the TreeNode. The node is then added to the selected album node.

private void OnAddPhoto(object sender, EventArgs e)
{
    if(DialogResult.OK == openFileDialog1.ShowDialog())
    {
        // Retrieve the Album to add photo(s) to
        Album album = (Album)treeAlbums.SelectedNode.Tag;

        // We allow multiple selections so loop through each one
        foreach(string file in openFileDialog1.FileNames)
        {
            // Create a new stream to load this photo into
            System.IO.FileStream stream = new System.IO.FileStream(file, System.IO.FileMode.Open, System.IO.FileAccess.Read);
            // Create a buffer to hold the stream bytes
            byte[] buffer = new byte[stream.Length];
            // Read the bytes from this stream
            stream.Read(buffer, 0, (int)stream.Length);
            // Now we can close the stream
            stream.Close();

            Photo photo = new Photo()
            {
                // Extract out the name of the file an use it for the name
                // of the photo
                Name = System.IO.Path.GetFileNameWithoutExtension(file),
                Image = buffer
            };

            // Insert the image into the database and add it to the tree
            Data.AddPhoto(album.Id, photo);
            buffer = null;

            // Add the photo to the album node
            TreeNode node = treeAlbums.SelectedNode.Nodes.Add(photo.Name);
            node.Tag = photo;
        }
    }
}

public static void AddPhoto(int albumId, Photo photo)
{
    using(SqlConnection conn = new SqlConnection(ConnectionString))
    {
        using(SqlCommand cmd = new SqlCommand("InsertPhoto", conn))
        {
            cmd.CommandType = System.Data.CommandType.StoredProcedure;
            conn.Open();

                // Add the name parameter and set the value
            cmd.Parameters.AddWithValue("@name", photo.Name);
            // Add the description parameter and set the value
            cmd.Parameters.AddWithValue("@desc", photo.Description);
            // Add the image parameter and set the value
            cmd.Parameters.AddWithValue("@photo", photo.Image);
            // Add the album parameter and set the value
            cmd.Parameters.AddWithValue("@albumId", albumId);

            // Add the return value parameter
            SqlParameter param = cmd.Parameters.Add("RETURN_VALUE", SqlDbType.Int);
            param.Direction = ParameterDirection.ReturnValue;

            // Execute the insert
            cmd.ExecuteNonQuery();

            // Return value will be the index of the newly added photo
            photo.Id = (int)cmd.Parameters["RETURN_VALUE"].Value;
        }
    }
}

Display the image

The AfterSelect event captures the selection of an image or album. If an images is selected the Tag member of the selected node is retrieved and cast into a TreeItem. The Id member of this class is then passed to a stored procedure to retrieve the desired image. The return of the ExecuteScalar method is cast into a byte array which is then read into a System.IO.MemoryStream. This stream object is then used to create a Bitmap which is then used assigned to the picturebox for display.

private void AfterSelect(object sender, TreeViewEventArgs e)
{
    DisplayPanel.Visible = true;

    if(treeAlbums.SelectedNode.Tag is Album)
    {
        Album album = (Album)treeAlbums.SelectedNode.Tag;

        DisplayName.Text = album.Name;
        DisplayDescription.Text = album.Description;

        pictureBox.Image = null;
    }
    else if(treeAlbums.SelectedNode.Tag is Photo)
    {
        Photo photo = (Photo)treeAlbums.SelectedNode.Tag;

        DisplayName.Text = photo.Name;
        DisplayDescription.Text = photo.Description;

        System.IO.MemoryStream stream = new System.IO.MemoryStream(photo.Image, true);
        stream.Write(photo.Image, 0, photo.Image.Length);

        // Draw photo to scale of picturebox
        DrawToScale(new Bitmap(stream));
    }
    else
    {
        DisplayPanel.Visible = false;
    }
}

Conclusion

This is a relatively simple implementation of a photo album viewer application. Using .NET and C# definitely made this application very easy to make. Storing the images in a SQL Server database adds a bit to the distribution but also simplifies the application by having one source to store and retrieve images from. You also don't have to worry about missing files or broken links.

Updates  

4/1/2012 - Updated to .NET 4.0, SQL Server 2012 and apply lessons learned learned for last 10 years.  

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)

Share

About the Author


Comments and Discussions

 
Questionc# PinmemberManjiri Sakhardande22-Feb-14 20:23 
GeneralMy vote of 5 PinmemberMember 974042115-Jul-13 21:18 
GeneralMy vote of 5 PinmvpKanasz Robert24-Sep-12 6:16 
GeneralMy vote of 5 PinmemberPerić_Željko15-Mar-12 21:52 
QuestionNeed help ASAP PinmemberBehnam0713-Feb-12 4:05 
AnswerRe: Need help ASAP PinmvpMark Nischalke13-Feb-12 5:25 
GeneralRe: Need help ASAP PinmemberBehnam0713-Feb-12 21:02 
GeneralRe: Need help ASAP PinmvpMark Nischalke14-Feb-12 1:10 
GeneralThanks PinmemberRockingDownTheHighway9-Feb-11 16:44 
GeneralRe: Thanks PinmvpMark Nischalke10-Feb-11 1:54 
GeneralRe: honest feedback PinmvpMark Nischalke27-Dec-10 9:26 
GeneralRe: honest feedback PinmvpMark Nischalke27-Dec-10 10:01 
GeneralRe: honest feedback PinmvpMark Nischalke27-Dec-10 10:23 
GeneralRe: honest feedback PinmemberAli Al Omairi28-Dec-10 8:22 
GeneralRe: honest feedback PinmvpMark Nischalke28-Dec-10 8:35 
GeneralRe: honest feedback PinmemberAli Al Omairi28-Dec-10 8:45 
QuestionHow come no foreign keys Pinmembermacupryk2-Feb-10 11:03 
AnswerRe: How come no foreign keys PinmvpMark Nischalke2-Feb-10 11:09 
Generalpls help PinmemberJaedah14-Jan-10 5:19 
GeneralRe: pls help PinmvpMark Nischalke14-Jan-10 6:14 
GeneralMy vote of 1 PinmemberBooGhost29-Dec-09 9:14 
GeneralRe: My vote of 1 PinmvpMark Nischalke14-Jan-10 6:11 
GeneralRe: My vote of 1 PinmemberChona117126-Apr-10 3:51 
Questionscrolling in picture box Pinmembersdeepa19-Dec-08 18:39 
AnswerRe: scrolling in picture box PinmemberMark Nischalke20-Dec-08 2:29 
QuestionSaving Image to Database Pinmembertonyg2smith6-Oct-06 3:30 
AnswerRe: Saving Image to Database Pinmembertonyg2smith6-Oct-06 3:32 
AnswerRe: Saving Image to Database PinmemberMark Nischalke6-Oct-06 3:57 
Generalretrieving the image again from the data base Pinmemberkarimkamal26-Apr-06 15:57 
GeneralRe: retrieving the image again from the data base PinmemberMark Nischalke26-Apr-06 16:38 
GeneralRe: retrieving the image again from the data base Pinmemberkarimkamal26-Apr-06 18:34 
GeneralRe: retrieving the image again from the data base Pinmemberkarimkamal26-Apr-06 20:19 
GeneralAsp .net Pinmemberbabamara9-Apr-06 6:47 
GeneralRe: Asp .net PinmemberChona117126-Apr-10 4:01 
GeneralRe: Asp .net PinmemberChona117126-Apr-10 4:04 
Generalimage detail PinmemberJA_Baller30-Mar-06 7:03 
GeneralRe: image detail PinmemberMark Nischalke30-Mar-06 8:35 
QuestionHow to do slide show? Pinmemberdinoniko12-Oct-05 14:27 
AnswerRe: How to do slide show? PinmemberMark Nischalke12-Oct-05 15:39 
QuestionQ ? PinmemberAhmed jamil Kattan4-Jun-05 4:41 
AnswerRe: Q ? PinmemberMark Nischalke4-Jun-05 5:05 
GeneralRe: Q ? PinmemberAhmed jamil Kattan4-Jun-05 20:31 
GeneralRe: Q ? Pinmembervandil23-Aug-05 11:56 
Questionis it recomended? PinmemberJordi Corominas25-Nov-04 0:19 
AnswerRe: is it recomended? PinmemberMark Nischalke25-Nov-04 3:23 
GeneralRe: is it recomended? Pinmemberdonalvn14-Dec-04 14:43 
GeneralRe: is it recomended? Pinmembercandynata12-Dec-05 14:47 
AnswerRe: is it recomended? PinmemberMah Jin Khai29-Mar-06 6:27 
GeneralLarge Images Pinmembersfawcett6-Sep-04 20:22 
QuestionASP.NET? PinsussAnonymous15-Jun-04 11:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 1 Apr 2012
Article Copyright 2002 by Not Active
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid