Problem You want to display a page of images stored in your database and scaled on the fly to thumbnail format. Solution Implement the first of the two ASP.NET pages described in Recipe 15.3, changing the Page_Load method in the code-behind class to scale the full-sized image retrieved from the database to the appropriate size for a thumbnail presentation. In the Page_Load method of the code-behind class for the page, use the .NET language of your choice to: Create a System.Drawing.Image object from the byte array retrieved from the database. Use a constant to define the height of the thumbnail and calculate the width to maintain the aspect ratio of the image. Use the GetThumbnailImage method of the Image object to scale the image to the desired size. Load the thumbnail image into a MemoryStream and write it to the Response object. Examples 15-16 and 15-17 show the VB and C# code-behind class for our example that illustrates this solution. (See the CH15ImageFromDatabaseVB .aspx file and VB and C# code-behind files in Recipe 15.3 for our starting point.) To display the thumbnails, create another ASP.NET page, add a DataList control with image tags in the ItemTemplate, and use data binding to set the src attributes of the image tags. In the .aspx file for the page: Use a DataList control to provide the ability to generate a list using data binding. Use a HeaderTemplate to label the table of images. Use an ItemTemplate to define an image that is displayed in the DataList. In the code-behind class for the page, use the .NET language of your choice to: Open a connection to the database. Read the list of images from the database. Assign the data source to the DataList control and bind it. Set the src attribute of the image tags used to display the thumbnail images in the ItemDataBound event of the DataList. Examples 15-18, 15-19 through 15-20 show the .aspx file and VB and C# code-behind files for the application that uses the dynamically generated images. The output produced by the page is shown in Figure 15-4. Figure 15-4. Displaying thumbnails output Discussion The rationale for this recipe is similar to that of Recipe 15.3. You need a convenient way to display images from a database, in this case a page of thumbnail images, and it must efficiently move the image data to the browser while maintaining the maximum flexibility in selecting images from the data store. This recipe uses the same approach as in Recipe 15.3 where, with one page, an image is retrieved from the database and streamed to the browser, and a second page is used to generate the image requests and display the results, which is a set of thumbnails here. Additionally, the image retrieval page must scale the images to thumbnail size. In our example that demonstrates the solution, the Page_Load method of the code-behind for the image building page (the CH15ThumbnailFromDatabase page) is modified to scale the full-size image retrieved from the database to the appropriate size for a thumbnail presentation. The first step to scale the image is to create a System.Drawing.Image object from the byte array retrieved from the database. This requires loading the byte array into a MemoryStream and using the FromStream method of the Image class to create the image. Next, we need to calculate how much to reduce the image. A constant is used to define the height of the thumbnail, and the width is calculated by determining how much the height is being reduced and multiplying the value times the width of the full-size image. | Reduce the height and width by the same scale factor to keep the aspect ratio correct. If the height or width is reduced by a different amount, the image will be distorted. |
|
| Handling mixed calculations of integers and doubles can result in unexpected results. Visual Basic is more tolerant and will allow the division of two integers with the quotient set to a variable of type double and result in the correct value. C# will return an integer result from the division of two integers, truncating the result. It is best to cast at least the numerator to the type of the resultant variable. |
|
Now that the width and height of the thumbnail are determined, the GetThumbnailImage method of the full-size image can be used to return a scaled-down image to use as the thumbnail. Once the thumbnail image is created, it can be loaded into a MemoryStream and written to the Response object in the same manner described in Recipe 15.3. The web form used to display the thumbnails uses a DataList control to provide the ability to generate the list using data binding. The DataList is configured to display four columns horizontally by setting the RepeatColumns attribute to "4" and the RepeatDirection attribute to "Horizontal". This will start a new row in the table used to display the images after every fourth image. A HeaderTemplate is used to label the table of images. The template can contain any HTML that can be properly displayed in a table. In this example, the template consists of a single table row containing a single cell with the heading for the table. The colspan attribute is set to "4" to cause the cell to span across all columns in the table, and the align attribute is set to "center" to center the heading. An ItemTemplate is used to define an item displayed in the DataList. In this example, the template consists of an img tag that has the ID and Runat attributes set to provide access to the item from the code-behind. The Page_Load method in the code-behind reads the list of images from the database and binds them to the DataList control. This is accomplished by opening a connection to the database, querying for a list of the image IDs, then setting the DataSource and calling the dataBind method of the DataList. The only data we need from the database is the list of IDs for the images. These will be used to set the image sources, and the reading of the image data will be done by the code described earlier. The src attribute of the image tags used to display the thumbnail images is set in the ItemDataBound event of the DataList. The ItemDataBound event is executed for every item in the DataList, including the header. Therefore, it is important to check the item type since image tags only appear in the data items. Data items can be an Item or an AlternatingItem. If the item is a data item, we need to get a reference to the image control in the item. This is accomplished by using the FindControl method of the item to locate the image control using the ID assigned in the .aspx file. This reference must be cast to an HtmlImage type since the return type of FindControl is an Object. Once a reference to the image is obtained, the src attribute can be set to the name of the ASP.NET page that is used to generate the thumbnail image passing the ID of the image in the URL. The ID of the image is obtained from the DataItem method of the item. This must be cast to a DbDataRecord type to allow "looking up" the ID of the image using the name of the column included in the SQL query used in the bindData method described earlier. This example presents a useful method of displaying thumbnails of images stored in a database. When used with reasonably small images, the performance is acceptable for most applications. If the images are large, however, you may want to create the thumbnail images offline and store them in the database to avoid the performance hit for real-time conversion. See Also Recipes 15.1 and 15.3 Example 15-16. Page_Load method for generating thumbnail image (.vb) Imports System.Configuration Imports System.Data Imports System.Data.Common Imports System.Data.OleDb Imports System.Drawing Imports System.Drawing.Imaging Imports System.IO … Private Sub Page_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Load 'height of the thumbnail created from the original image Const THUMBNAIL_HEIGHT As Integer = 75 Dim dbConn As OleDbConnection Dim dc As OleDbCommand Dim imageData( ) As Byte Dim strConnection As String Dim strSQL As String Dim ms As MemoryStream Dim fullsizeImage As Image Dim thumbnailImage As Image Dim thumbnailWidth As Integer Dim imageID As String If (Not Page.IsPostBack) Then Try 'get the ID of the image to retrieve from the database imageID = Request.QueryString(QS_IMAGE_ID) 'get the connection string from web.config and open a connection 'to the database strConnection = ConfigurationManager. _ ConnectionStrings("dbConnectionString").ConnectionString dbConn = New OleDb.OleDbConnection(strConnection) dbConn.Open( ) 'build the query string and get the data from the database strSQL = "SELECT ImageData " &_ "FROM BookImage " &_ "WHERE BookImageimage/jpg" Response.BinaryWrite(ms.ToArray( )) Finally 'clean up If (Not IsNothing(dbConn)) Then dbConn.Close( ) End If End Try End If End Sub 'Page_Load | Example 15-17. Page_Load method for generating thumbnail image (.cs) using System; using System.Configuration; using System.Data; using System.Data.OleDb; using System.Drawing; using System.Drawing.Imaging; using System.IO; … protected void Page_Load(object sender, EventArgs e) { // height of the thumbnail created from the original image const int THUMBNAIL_HEIGHT = 75; OleDbConnection dbConn = null; OleDbCommand dc = null; byte[] imageData = null; String strConnection = null; String strSQL = null; MemoryStream ms = null; System.Drawing.Image fullsizeImage = null; System.Drawing.Image thumbnailImage = null; int thumbnailWidth; String imageID = null; if (!Page.IsPostBack) { try { // get the ID of the image to retrieve from the database imageID = Request.QueryString[QS_IMAGE_ID]; // get the connection string from web.config and open a connection // to the database strConnection = ConfigurationManager. ConnectionStrings["dbConnectionString"].ConnectionString; dbConn = new OleDbConnection(strConnection); dbConn.Open( ); // build the query string and get the data from the database strSQL = "SELECT ImageData " + "FROM BookImage " + "WHERE BookImageimage/jpg"; Response.BinaryWrite(ms.ToArray( )); } finally { if (dbConn != null) { dbConn.Close( ); } } } } // Page_Load | Example 15-18. Displaying thumbnail images (.aspx) <%@ Page Language="VB" MasterPageFile="~/ASPNetCookbookVB.master" AutoEventWireup="false" CodeFile="CH15TestThumbnailsFromDatabaseVB.aspx.vb" Inherits="ASPNetCookbook.VBExamples.CH15TestThumbnailsFromDatabaseVB" Title="Display Thumbnails" %> <asp:Content runat="server" ContentPlaceHolder> <div align="center" > Display Thumbnail Images (VB) </div> <div align="center"> <asp:DataList Runat="server" RepeatColumns="4" RepeatDirection="Horizontal" RepeatLayout="Table" Width="50%" OnItemDataBound="dlImages_ItemDataBound"> <HeaderTemplate> <tr> <td colspan="4" align="center"> Thumbnails of Images In Database<br /><br /> </td> </tr> </HeaderTemplate> <ItemTemplate> <img runat="server" border="0" src="/books/1/505/1/html/2/" alt="image"/> </ItemTemplate> </asp:DataList>< </div> </asp:Content> | Example 15-19. Displaying thumbnail images code-behind (.vb) Option Explicit On Option Strict On Imports System.Configuration.ConfigurationManager Imports System.Data Imports System.Data.Common Imports System.Data.OleDb Namespace ASPNetCookbook.VBExamples ''' <summary> ''' This class provides the code-behind for ''' CH15TestThumbnailsFromDatabaseVB.aspx ''' </summary> Partial Class CH15TestThumbnailsFromDatabaseVB Inherits System.Web.UI.Page '''*********************************************************************** ''' <summary> ''' This routine provides the event handler for the page load event. It ''' is responsible for initializing the controls on the page. ''' </summary> ''' ''' <param name="sender">Set to the sender of the event</param> ''' <param name="e">Set to the event arguments</param> Private Sub Page_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Load Dim dbConn As OleDbConnection = Nothing Dim dc As OleDbCommand Dim dr As OleDbDataReader Dim strConnection As String Dim strSQL As String If (Not Page.IsPostBack) Then Try 'get the connection string from web.config and open a connection 'to the database strConnection = _ ConnectionStrings("dbConnectionString").ConnectionString dbConn = New OleDb.OleDbConnection(strConnection) dbConn.Open( ) 'build the query string and get the data from the database strSQL = "SELECT BookImageID " &_ "FROM BookImage" dc = New OleDbCommand(strSQL, dbConn) dr = dc.ExecuteReader( ) 'set the source of the data for the repeater control and bind it dlImages.DataSource = dr dlImages.DataBind( ) Finally 'clean up If (Not IsNothing(dbConn)) Then dbConn.Close( ) End If End Try End If End Sub 'Page_Load '''*********************************************************************** ''' <summary> ''' This routine is the event handler that is called for each item in the ''' datalist after a data bind occurs. It is responsible for setting the ''' source of the image tag to the URL of the page that will generate the ''' thumbnail images with the ID of the appropriate image for the item. ''' </summary> ''' ''' <param name="sender">Set to the sender of the event</param> ''' <param name="e">Set to the event arguments</param> Protected Sub dlImages_ItemDataBound(ByVal sender As Object, _ ByVal e As System.Web.UI.WebControls.DataListItemEventArgs) Dim img As System.Web.UI.HtmlControls.HtmlImage 'make sure this is an item in the data list (not header etc.) If ((e.Item.ItemType = ListItemType.Item) Or _ (e.Item.ItemType = ListItemType.AlternatingItem)) Then 'get a reference to the image used for the bar in the row img = CType(e.Item.FindControl("imgThumbnail"), _ System.Web.UI.HtmlControls.HtmlImage) 'set the source to the page that generates the thumbnail image img.Src = "CH15ThumbnailFromDatabaseVB.aspx?ImageBookImageID")) End If End Sub 'dlImages_ItemDataBound End Class 'CH15TestThumbnailsFromDatabaseVB End Namespace | Example 15-20. Displaying thumbnail images code-behind (.cs) using System; using System.Configuration; using System.Data; using System.Data.Common; using System.Data.OleDb; using System.Web.UI.WebControls; namespace ASPNetCookbook.CSExamples { /// <summary> /// This class provides the code-behind for /// CH15TestThumbnailsFromDatabaseCS.aspx /// </summary> public partial class CH15TestThumbnailsFromDatabaseCS : System.Web.UI.Page { ///*********************************************************************** /// <summary> /// This routine provides the event handler for the page load event. /// It is responsible for initializing the controls on the page. /// </summary> /// /// <param name="sender">Set to the sender of the event</param> /// <param name="e">Set to the event arguments</param> protected void Page_Load(object sender, EventArgs e) { OleDbConnection dbConn = null; OleDbCommand dc = null; OleDbDataReader dr = null; String strConnection = null; String strSQL = null; if (!Page.IsPostBack) { try { // get the connection string from web.config and open a connection // to the database strConnection = ConfigurationManager. ConnectionStrings["dbConnectionString"].ConnectionString; dbConn = new OleDbConnection(strConnection); dbConn.Open( ); // build the query string and get the data from the database strSQL = "SELECT BookImageID " + "FROM BookImage"; dc = new OleDbCommand(strSQL, dbConn); dr = dc.ExecuteReader( ); // set the source of the data for the repeater control and bind it dlImages.DataSource = dr; dlImages.DataBind( ); } finally { // clean up if (dbConn != null) { dbConn.Close( ); } } } } // Page_Load ///*********************************************************************** /// <summary> /// This routine is the event handler that is called for each item in the /// datalist after a data bind occurs. It is responsible for setting the /// source of the image tag to the URL of the page that will generate the /// thumbnail images with the ID of the appropriate image for the item. /// </summary> /// /// <param name="sender">Set to the sender of the event</param> /// <param name="e">Set to the event arguments</param> protected void dlImages_ItemDataBound(Object sender, System.Web.UI.WebControls.DataListItemEventArgs e) { System.Web.UI.HtmlControls.HtmlImage img = null; // make sure this is an item in the data list (not header etc.) if ((e.Item.ItemType == ListItemType.Item) || (e.Item.ItemType == ListItemType.AlternatingItem)) { // get a reference to the image used for the bar in the row img = (System.Web.UI.HtmlControls.HtmlImage) (e.Item.FindControl("imgThumbnail")); // set the source to the page that generates the thumbnail image img.Src = "CH15ThumbnailFromDatabaseCS.aspx?ImageBookImageID"]).ToString( ); } } // dlImages_ItemDataBound } // CH15TestThumbnailsFromDatabaseCS } | |