Thursday, October 30, 2008

Create a Picture Album using ListView in ASP.NET 3.5

In this article, we will see how to create a Picture Album using the ListView control. We will explore the GroupTemplate of ListView and explore how we can group multiple images together in the ListView, thereby creating an album effect.
In one of the previous articles Save and Retrieve Images from the Database using ASP.NET 2.0 and ASP.NET 3.5 we had discussed how to use image handlers to save and retrieve images in the database. In this article, we will build on that concept and use the same technique to display images in a ListView.

We will learn how to upload an image and then display the uploaded image on the same page in an ‘album mode’ using the ListView. At the end of this article, you will also learn how to use the different templates of a ListView. If you are unfamiliar with the ListView control, I suggest you to read my article Exploring the ListView control in ASP.NET 3.5 to get an understanding of the same. To keep the article simple and easy to understand, I have not covered any validations associated with image control or other control. The article also does not suggest you best practices. In this article, we will only discuss how to read images from the database and display it in the ListView control, and that would be the focus for this article. I assume you have some knowledge of creating ASP.NET 3.5 websites. We will be using Visual Studio 2008.

Let us start off by first creating a sample database and adding a table to it. We will call the database ‘PictureAlbum’ and the table will be called ‘Album’. This table will contain an image column along with some  other columns. Run the following script in your SQL 2005 Query window (or server explorer) to construct the database and the table.

CREATE DATABASE [PictureAlbum] 
GO 
USE [PictureAlbum] 
GO 
CREATE TABLE Album 
pic_id int IDENTITY NOT NULL, 
picture_tag varchar(50), 
pic image 
)

Step 1: Create a new ASP.NET website. In the code-behind, add the following namespace

C#
using System.Data.SqlClient;

VB.NET
Imports System.Data.SqlClient

Step 2: Drag and drop two label and one textbox control. Also drag drop a FileUpload control and a button control to upload the selected image on button click. As mentioned earlier, there are no validations performed. The source would look similar to the following:

<html xmlns="http://www.w3.org/1999/xhtml"> 
<head runat="server"> 
<title>Untitled Page</title> 
</head><body> 
<form id="form1" runat="server"> 
<div> 
Image 
<asp:Label ID="lblTags" runat="server" Text="Tags"></asp:Label> 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<asp:TextBox ID="txtTags" runat="server"></asp:TextBox> 
<br /> 
<asp:Label ID="lblImage" runat="server" Text="Upload Picture"></asp:Label> 
&nbsp;&nbsp;&nbsp;&nbsp; 
<asp:FileUpload ID="imgUpload" runat="server" /> 
<br />
<br /> 
<asp:Button ID="btnSubmit" runat="server" onclick="btnSubmit_Click" 
Text="Submit" /> 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp<asp:Label ID="lblResult" runat="server" ForeColor="#0066FF"></asp:Label> 
<br /> 
<hr /> 
</div>
</form> 
</body> 
</html>

Also in the web.config, add a <connectionStrings> tag as displayed below:

<connectionStrings> 
<add name="albumConnString" connectionString="Data Source=(local); Initial Catalog = PictureAlbum; Integrated Security=True;"providerName="System.Data.SqlClient" /> 
</connectionStrings>

Step 3: In the button click event, add the following code:

C#

protected void btnSubmit_Click(object sender, EventArgs e) 
SqlConnection connection = null; 
try
FileUpload img = (FileUpload)imgUpload; 
Byte[] imgByte = null; 
if (img.HasFile && img.PostedFile != null) 
{
//To create a PostedFile 
HttpPostedFile File = imgUpload.PostedFile; 
//Create byte Array with file len 
imgByte = new Byte[File.ContentLength]; 
//force the control to load data in array 
File.InputStream.Read(imgByte, 0, File.ContentLength); 
// Insert the picture tag and image into db 
string conn = ConfigurationManager.ConnectionStrings["albumConnString"].ConnectionString; 
connection = new SqlConnection(conn);
connection.Open(); 
string sql = "INSERT INTO Album(picture_tag,pic) VALUES(@tag,@pic) SELECT @@IDENTITY"; 
SqlCommand cmd = new SqlCommand(sql, connection); 
cmd.Parameters.AddWithValue("@tag", txtTags.Text); 
cmd.Parameters.AddWithValue("@pic", imgByte); 
int id = Convert.ToInt32(cmd.ExecuteScalar()); 
lblResult.Text = String.Format("Picture ID is {0}", id); 
catch 
lblResult.Text = "There was an error"; 
finally 
{
connection.Close(); 
}

VB.NET

Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As EventArgs) 
Dim connection As SqlConnection = Nothing 
Try
Dim img As FileUpload = CType(imgUpload, FileUpload) 
Dim imgByte As Byte() = Nothing 
If img.HasFile AndAlso Not img.PostedFile Is Nothing Then 
'To create a PostedFile 
Dim File As HttpPostedFile = imgUpload.PostedFile 
'Create byte Array with file len 
imgByte = New Byte(File.ContentLength - 1){} 
'force the control to load data in array 
File.InputStream.Read(imgByte, 0, File.ContentLength) 
End If 
' Insert the picture tag and image into db 
Dim conn As String = ConfigurationManager.ConnectionStrings("albumConnString").ConnectionString 
connection = New SqlConnection(conn)
connection.Open() 
Dim sql As String = "INSERT INTO Album(picture_tag,pic) VALUES(@tag,@pic) SELECT @@IDENTITY" 
Dim cmd As SqlCommand = New SqlCommand(sql, connection) 
cmd.Parameters.AddWithValue("@tag", txtTags.Text) 
cmd.Parameters.AddWithValue("@pic", imgByte) 
Dim id As Integer = Convert.ToInt32(cmd.ExecuteScalar()) 
lblResult.Text = String.Format("Picture ID is {0}", id) 
Catch
lblResult.Text = "There was an error" 
Finally
connection.Close() 
End Try 
End Sub

In the code above, we are creating a byte array equal to the length of the file. The byte array will store the image. We then load the image data into the array. The record containing the Picture Tags and Image is then inserted into the database using the ADO.NET code. The ID inserted is returned back using the @@Identity. We will shortly use this ID and pass it as a query string parameter to the ShowImage handler. The image will then be fetched against the Picture ID (pic_id). If you run the application as of now, you should be able to upload the images to the database.

Step 4: In order to display the image on the page, we will create an Http handler. To do so, right click project > Add New Item > Generic Handler > ShowImage.ashx. In the code shown below, we are using the Request.QueryString[“id”] to retrieve the PictureID(pic_id) from the handler url. The ID is then passed to the ‘ShowAlbumImage()’ method where the image is fetched from the database and returned in a MemoryStream object. We then read the stream into a byte array. Using the OutputStream.Write(), we write the sequence of bytes to the current stream and you get to see your image.

C#

<%@ WebHandler Language="C#" Class="ShowImage" %>
using System; 
using System.Configuration;
using System.Web; 
using System.IO;
using System.Data; 
using System.Data.SqlClient;

public class ShowImage : IHttpHandler 
public void ProcessRequest(HttpContext context) 
Int32 picid; 
if (context.Request.QueryString["id"] != null) 
picid = Convert.ToInt32(context.Request.QueryString["id"]); 
else 
throw new ArgumentException("No parameter specified");
context.Response.ContentType = "image/jpeg"; 
Stream strm = ShowAlbumImage(picid); 
byte[] buffer = new byte[4096];
int byteSeq = strm.Read(buffer, 0, 4096);
while (byteSeq > 0) 
context.Response.OutputStream.Write(buffer, 0, byteSeq);
byteSeq = strm.Read(buffer, 0, 4096); 
//context.Response.BinaryWrite(buffer);
}
public Stream ShowAlbumImage(int picid) 
{
string conn = ConfigurationManager.ConnectionStrings["albumConnString"].ConnectionString; 
SqlConnection connection = new SqlConnection(conn);
string sql = "SELECT pic FROM Album WHERE Pic_ID = @ID"; 
SqlCommand cmd = new SqlCommand(sql, connection); 
cmd.CommandType = CommandType.Text; 
cmd.Parameters.AddWithValue("@ID", picid); 
connection.Open(); 
object img = cmd.ExecuteScalar(); 
try 
return new MemoryStream((byte[])img); 
catch 
return null; 
finally 
connection.Close(); 

public bool IsReusable 
get 
return false; 
}
}

VB.NET

<%@ WebHandler Language="vb" Class="ShowImage" %>
Imports System 
Imports System.Configuration
Imports System.Web 
Imports System.IO
Imports System.Data 
Imports System.Data.SqlClient

Public Class ShowImage
Implements IHttpHandler 
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
Dim picid As Int32 
If Not context.Request.QueryString("id") Is Nothing Then
picid = Convert.ToInt32(context.Request.QueryString("id")) 
Else
Throw New ArgumentException("No parameter specified") 
End If
context.Response.ContentType = "image/jpeg" 
Dim strm As Stream = ShowAlbumImage(picid)
Dim buffer As Byte() = New Byte(4095){} 
Dim byteSeq As Integer = strm.Read(buffer, 0, 4096)
Do While byteSeq > 0 
context.Response.OutputStream.Write(buffer, 0, byteSeq)
byteSeq = strm.Read(buffer, 0, 4096) 
Loop
'context.Response.BinaryWrite(buffer); 
End Sub

Public Function ShowAlbumImage(ByVal picid As Integer) As Stream
Dim conn As String = ConfigurationManager.ConnectionStrings("albumConnString").ConnectionString 
Dim connection As SqlConnection = New SqlConnection(conn)
Dim sql As String = "SELECT pic FROM Album WHERE Pic_ID = @ID" 
Dim cmd As SqlCommand = New SqlCommand(sql, connection)
cmd.CommandType = CommandType.Text 
cmd.Parameters.AddWithValue("@ID", picid)
connection.Open() 
Dim img As Object = cmd.ExecuteScalar()
Try 
Return New MemoryStream(CType(img, Byte()))
Catch 
Return Nothing
Finally 
connection.Close()
End Try 
End Function

Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get 
Return False
End Get 
End Property
End Class

Step 5: We will now add the ListView to our page. We will see how to use the ‘GroupTemplate’ to display images in multiple columns (in our case 4 images). Add a SQLDataSource control to the page. After configuring it with the database, the code will look similar to the following

<asp:SqlDataSource ID="SqlDataSource1" runat="server" 
ConnectionString="<%$ ConnectionStrings:albumConnString %>" 
ProviderName="System.Data.SqlClient" 
SelectCommand="SELECT [pic_id],picture_tag, [pic] FROM [Album]"></asp:SqlDataSource>

Now add a ListView Control to the page and set its DataSource property to ‘SqlDataSource1’. After adding the LayoutTemplate, ItemTemplate and GroupTemplate the code will look similar to the following:

<asp:ListView ID="ListView1" GroupItemCount="4" runat="server" DataKeyNames="pic_id" 
DataSourceID="SqlDataSource1"> 
<LayoutTemplate> 
<asp:Placeholder 
id="groupPlaceholder" 
runat="server" /> 
</LayoutTemplate> 
<GroupTemplate> 
<div> 
<asp:Placeholder 
id="itemPlaceholder" 
runat="server" /> 
</div> 
</GroupTemplate> 
<ItemTemplate> 
<asp:Image 
id="picAlbum" runat="server" AlternateText='<% #Eval("picture_tag") %>' 
ImageUrl='<%# "ShowImage.ashx?id=" + Eval("pic_id") %>' /> 
</ItemTemplate>
<EmptyItemTemplate>
</EmptyItemTemplate> 
</asp:ListView>

If you observe, the <LayoutTemplate> contains a placeholder called ‘groupPlaceholder’. When the page is rendered, the groupPlaceholder is replaced with the contents of GroupTemplate. Similarly, the <GroupTemplate> includes the ‘itemPlaceholder’ placeholder. The itemPlaceholder is replaced with the contents of the ItemTemplate, which is an image. As you know, the <ItemTemplate> renders each time there is an item from the source. The ‘GroupItemCount’ attribute decides the number of items displayed in a <GroupTemplate> before a new row is created. Here we have set the ImageUrl to an expression which makes a call to the handler and displays the images from the database. In the code below, we pass the pic_id as a query string parameter to the Http Handler. Similarly the tags entered by you are shown as alternate text on the images. To see the text, hover your mouse over the image, when the application is run.

Note: One last thing. In order to display images as they are added, just add this line of code (ListView1.DataBind()) in the btnSubmit_Click, just before the try block ends.

...

lblResult.Text = String.Format("Picture ID is {0}", id);

ListView1.DataBind();

We are all set. It’s time to you run the application!! Start uploading images and you will observe that as you upload the images, the gallery gets updated and the image is displayed in an album mode using the ListView control as shown below:
























The source code for this article in C# and VB.NET can be downloaded from here. I hope you liked the article and I thank you for viewing it.

No comments: