65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 照片集查看器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (90投票s)

2002年4月13日

CDDL

3分钟阅读

viewsIcon

765559

downloadIcon

14507

一个用 C# 编写的 Windows 应用程序,用于从 SQL Server 数据库存储和检索照片。

Sample Image - AlbumViewer.jpg

C# 照片集查看器

此示例是一个 C# Windows 应用程序,演示了从数据库存储和检索图像。

尽管本文和代码已更新以支持 .NET 4.0 和 SQL Server 2012,但它以前是为 .NET 1.0 和 SQL Server 2000 构建的,因此所有方法和技术都应该在这些应用程序的早期版本中工作,只需稍作修改或无需修改。  

数据库表和存储过程 

下载文件包含创建此演示应用程序所需的数据库和存储过程的脚本。

初始加载

初始化此应用程序的主窗体后,首先需要获取任何现有的相册和照片,并将它们加载到 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);
        }                
    }
}

这相当简单。 TreeNode 的 Tag 对象被设置为 struct,这样以后在选择项目时,就不需要额外的数据库调用来填充显示控件。

通过 Data.GetPhotoAlbums 方法获取,同样,这是一个直接的 ADO.NET 实现。

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();
}

第一步是初始化 SqlConnectionSqlCommand 对象。然后执行存储过程,并使用 SqlDataReader 迭代结果并创建一个要返回的相册集合。在此之后,照片以类似的方式添加到每个相册中。如前所述,这可以通过一个存储过程来完成,但是,为了说明该过程,它被分解为两种方法。

存储图像

选择“添加照片”上下文菜单项后,多选 FileOpenDialog 允许选择图像进行添加。对于每个文件,都会创建一个 System.IO.FileStream 并用于读入一个 byte 数组。然后,byte 数组被传递给一个方法,该方法将其用作存储过程的输入参数,以将其添加到数据库表的图像字段中。

最后一步是将图像添加到 treeview。为此目的使用了名为 TreeItem 的帮助程序类。此类存储图像的描述和数据库索引 ID。此类也用于相册,因此一个 enum 定义了它的对象类型。创建此类的新实例后,它将被分配给 TreeNodeTag 成员。然后将该节点添加到选定的相册节点。

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;
        }
    }
}

显示图像

AfterSelect 事件捕获图像或相册的选择。如果选择了图像,则检索所选节点的 Tag 成员,并将其转换为 TreeItem。然后将此类的 Id 成员传递给存储过程以检索所需的图像。ExecuteScalar 方法的返回被转换为一个字节数组,然后将其读入 System.IO.MemoryStream。然后使用此流对象创建 Bitmap,然后将其用于分配给 picturebox 进行显示。

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;
    }
}

结论

这是一个相对简单的相册查看器应用程序的实现。使用 .NET 和 C# 绝对使此应用程序非常容易制作。将图像存储在 SQL Server 数据库中会增加一些分发量,但也简化了应用程序,因为它只有一个源可以存储和检索图像。您也不必担心文件丢失或链接损坏。

更新  

2012年4月1日 - 更新到 .NET 4.0、SQL Server 2012 并应用了过去 10 年的经验教训。 

© . All rights reserved.