Image in DetailsView
One of our forum members in MS Phil has recently inquired if it's possible to display image from the database using a DetailsView control. Out of the box, it can't since the Imagefield rowfield only accepts a url for the image, but with the help of an auxilliary page we can.
Let's have our SqlDataSource setup first:
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:HissDBConnectionString %>"
SelectCommand="SELECT [Emp_No], [First_Name], [Last_Name] FROM [HR_EmployeeMaster] WHERE Company=@Company">
<SelectParameters>
<asp:ControlParameter Name="Company" ControlID="DropDownList1" PropertyName="SelectedValue" />
</SelectParameters>
</asp:SqlDataSource>
I have a select statement which will query all employees depending on their company which will be selected on my Dropdownlist control.
Then our DetailsView control:
<asp:DetailsView ID="DetailsView1" runat="server" AutoGenerateRows="False" DataKeyNames="Emp_No"
DataSourceID="SqlDataSource1" Height="50px" Width="125px" AllowPaging="True">
<Fields>
<asp:BoundField DataField="Emp_No" HeaderText="Emp_No" ReadOnly="True" SortExpression="Emp_No" />
<asp:BoundField DataField="First_Name" HeaderText="First_Name" SortExpression="First_Name" />
<asp:BoundField DataField="Last_Name" HeaderText="Last_Name" SortExpression="Last_Name" />
</Fields>
</asp:DetailsView>
Now that we have our DetailsView up and running, it's time to make our auxilliary page; whose function will be the placeholder of our streamed image. Create another web page, let's name it DisplayImage.aspx. In our code-behind and upon the Page_Load event we typed the following:
VB.NET:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim EmpNo As String= Request.QueryString("emp_no")
Dim commandString As String = "SELECT Photo FROM HR_EmpPhoto WHERE Emp_No= @empNo "
Using myConn As New SqlConnection(ConfigurationManager.ConnectionStrings("HissDBConnectionString").ToString())
Dim myCommand As SqlCommand()
Dim myReader As SqlDataReader
myConn.Open()
myCommand = myConn.CreateCommand()
myCommand.Parameters.Add("@empNo",SqlDbType.Varchar,10) .Value = EmpNo
myReader = myCommand.ExecuteReader(CommandBehavior.CloseConnection)
If myReader.Read() Then
Dim imgBuffer As Byte() = CType(myReader.Item("Photo"), Byte())
Response.ContentType = "image/jpeg"
Response.BinaryWrite(imgBuffer)
End If
myConn.Close()
End Using
End Sub
C#
protected void Page_Load(object sender, EventArgs e)
{
String empNo = Request.QueryString["emp_no"];
String commandString = "SELECT Photo FROM HR_EmpPhoto WHERE Emp_No=@empNo";
using (SqlConnection myConn = new SqlConnection (ConfigurationManager.ConnectionStrings["HissDBConnectionString"].ToString()))
{
myConn.Open();
SqlCommand myCommand = myConn.CreateCommand();
myCommand.Parameters.Add("@empNo",SqlDbType.Varchar,10) .Value = EmpNo
SqlDataReader myReader = myCommand.EndExecuteReader(CommandBehavior.CloseConnection);
if (myReader.HasRows ()){
byte[] imgBuffer = (byte[]) myReader["Photo"];
Response.ContentType = "image/jpeg";
Response.BinaryWrite (imgBuffer);
}
myConn.Close();
}
}
The query will try see if the there is a photo available for our employee. If it exists we then convert the content of our reader into a byte array so that we can pass it to our Response object's method BinaryWrite() as an output stream. The content type should be properly configured so that the user agent(browser) knows how to deal with it, in case of our image, knows how to display it. However, I was testing it even with a wrong content type "image/gif" or no content type at all but the browser was able to display it correctly. Nevertheless, one should use the correct content type.
We can test our page by manually type at the address bar: http://localhost/ImageInDetailsView/DisplayImage.aspx?emp_no=7342. If the image is displaying then we proceed to add our ImageField row in our DetailsView.
Add the following as the first row of our DetailsView:
<asp:ImageField HeaderText = "Photo" DataImageUrlField="Emp_No"
DataImageUrlFormatString="~\DisplayImage.aspx?emp_no={0}">
<ControlStyle Width="150px" />
</asp:ImageField>
We are binding our DataImageUrlField to the Emp_No column from our datasource, from here we're going to get the parameter to pass to our auxilliary page. In our DataImageUrlFormatString we set it to our DisplayImage.aspx then pass in our querystring the parameter emp_no={0}, this tells the field to get the first column from our datasource so it depends on what column number you placed your bound column. We limit the size of our image to 150px only so that the display of picture is constant, no matter the size of the picture. The same technique can also be used in a GridView control.
That's all there is to it. Next time I'm going to show you how to do without the auxilliary page.
UPDATED: Gavin noted that the code is open to sql-injection attack, so changed the code to a parameterized query. Thanks for pointing it out