Click here to Skip to main content
15,886,137 members
Articles / Programming Languages / C#

C# Photo Album Viewer

Rate me:
Please Sign up or sign in to vote.
4.71/5 (96 votes)
1 Apr 2012CDDL3 min read 750K   14.5K   285   135
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.

C#
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.

C#
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.

C#
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.

C#
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)



Comments and Discussions

 
GeneralRe: My vote of 1 Pin
L Viljoen26-Apr-10 3:51
professionalL Viljoen26-Apr-10 3:51 
Questionscrolling in picture box Pin
sdeepa19-Dec-08 18:39
sdeepa19-Dec-08 18:39 
AnswerRe: scrolling in picture box Pin
Not Active20-Dec-08 2:29
mentorNot Active20-Dec-08 2:29 
QuestionSaving Image to Database Pin
tonyg2smith6-Oct-06 3:30
tonyg2smith6-Oct-06 3:30 
AnswerRe: Saving Image to Database Pin
tonyg2smith6-Oct-06 3:32
tonyg2smith6-Oct-06 3:32 
AnswerRe: Saving Image to Database Pin
Not Active6-Oct-06 3:57
mentorNot Active6-Oct-06 3:57 
Generalretrieving the image again from the data base Pin
karimkamal26-Apr-06 15:57
karimkamal26-Apr-06 15:57 
GeneralRe: retrieving the image again from the data base Pin
Not Active26-Apr-06 16:38
mentorNot Active26-Apr-06 16:38 
I would suggest using SqlParameter rather than using textBox1.Text. This will help to prevent sql injection attacks. What data type is S_number?
GeneralRe: retrieving the image again from the data base Pin
karimkamal26-Apr-06 18:34
karimkamal26-Apr-06 18:34 
GeneralRe: retrieving the image again from the data base Pin
karimkamal26-Apr-06 20:19
karimkamal26-Apr-06 20:19 
GeneralAsp .net Pin
babamara9-Apr-06 6:47
babamara9-Apr-06 6:47 
GeneralRe: Asp .net Pin
L Viljoen26-Apr-10 4:01
professionalL Viljoen26-Apr-10 4:01 
GeneralRe: Asp .net Pin
L Viljoen26-Apr-10 4:04
professionalL Viljoen26-Apr-10 4:04 
Generalimage detail Pin
JA_Baller30-Mar-06 7:03
JA_Baller30-Mar-06 7:03 
GeneralRe: image detail Pin
Not Active30-Mar-06 8:35
mentorNot Active30-Mar-06 8:35 
QuestionHow to do slide show? Pin
dinoniko12-Oct-05 14:27
dinoniko12-Oct-05 14:27 
AnswerRe: How to do slide show? Pin
Not Active12-Oct-05 15:39
mentorNot Active12-Oct-05 15:39 
QuestionQ ? Pin
Ahmed jamil Kattan4-Jun-05 4:41
Ahmed jamil Kattan4-Jun-05 4:41 
AnswerRe: Q ? Pin
Not Active4-Jun-05 5:05
mentorNot Active4-Jun-05 5:05 
GeneralRe: Q ? Pin
Ahmed jamil Kattan4-Jun-05 20:31
Ahmed jamil Kattan4-Jun-05 20:31 
GeneralRe: Q ? Pin
vandil23-Aug-05 11:56
vandil23-Aug-05 11:56 
Questionis it recomended? Pin
Jordi Corominas25-Nov-04 0:19
Jordi Corominas25-Nov-04 0:19 
AnswerRe: is it recomended? Pin
Not Active25-Nov-04 3:23
mentorNot Active25-Nov-04 3:23 
GeneralRe: is it recomended? Pin
Member 152509214-Dec-04 14:43
Member 152509214-Dec-04 14:43 
GeneralRe: is it recomended? Pin
candynata12-Dec-05 14:47
candynata12-Dec-05 14:47 

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

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