Recipe 15.2. Drawing Button Images on the Fly


Problem

You need to create a button image on the fly using text generated during the running of your application.

Solution

Create a web form that is responsible for creating the button image using the System.Drawing classes and then streaming the image to the Response object.

In the .aspx file, enter an @ Page directive, but omit any head or body tags. The @ Page directive links the ASP.NET page to the code-behind class that draws the image.

In the code-behind class for the page, use the .NET language of your choice to:

  1. Import the System.Drawing and System.Drawing.Imaging namespaces.

  2. Create a makeButton (or similarly named) method that creates a bitmap for a button using text generated during the running of the applicationfor example, text passed in the URL.

  3. Create a MemoryStream object and save the bitmap in JPEG format (or other format) to the memory stream.

  4. Write the resulting binary stream to Response object.

Examples 15-1, 15-2 through 15-3 show the .aspx file and VBand C# code-behind files for an application that creates a button image whose label is provided by the application user.

To use a dynamically generated image in your application, you need to set the Src attribute of the image tags for your button bitmaps to the URL of the ASP.NET page that creates the images, passing the image text in the URL.

In the .aspx file for the page, add an img tag for displaying the dynamically created image.

In the code-behind class for the page that uses the image, use the .NET language of your choice to set to the Src attribute of the image tag to the URL for the web form that will draw the image, passing the text it needs in the URL.

Examples 15-4, 15-5 through 15-6 show the .aspx file and VB and C# code-behind files for an application that uses the dynamic image generation. Figure 15-1 shows some typical output from the application.

Figure 15-1. Creating a button image on the fly


Discussion

Creating button images on the fly can be handy for two reasons. First, using button images may help provide the look you want for your application. Second, generating them on the fly can avoid having to create and save a whole series of button images to the file system on the prospect they may be needed someday. For example, you might want to use images to improve the appearance of reports.

The approach we favor for generating images on the fly involves first drawing them and then streaming the images to the Response object. How does this work? The process begins when the browser first makes a request for a page to display. During the page rendering, whenever the browser encounters an image tag, it sends a request for that image to the server. The browser expects the server to stream the requested image back to the browser with the content type set to indicate an image of a certain type is being returnedfor example, image/jpg, indicating an image in JPEG format. Our approach does that, but with a twist. Instead of a static image, which is the norm, our approach returns an image that has been created on the fly on the server. The browser neither knows nor cares where the stream is coming from, which is why our approach works well.

Two web forms are used in our example that illustrates this solution. The first one renders no HTML but processes a user request for a dynamically created button image. A second web form is used to display the requested image.

The .aspx file of the first form contains no head or body; it contains the @ Page directive to link the code-behind class for the page.

In the Page_Load event handler of the code-behind of the first page, the text for the image button passed in the URL is retrieved and passed to the makeButton method in the buttonText parameter to create the button image.

The makeButton method first creates the font that will be used to label the button image and then measures the space that will be required to display the text. Because we have no Graphics object in this scenario and a Graphics object cannot be created by itself (it must always be associated with a specific device context), we have to create a Bitmap solely for the purpose of creating the Graphics object. Here, a dummy one-pixel-by-one-pixel bitmap is created:

 

bfont = New Font("Trebuchet MS", 10) button = New Bitmap(1, 1, PixelFormat.Format32bppRgb) g = Graphics.FromImage(button) tSize = g.MeasureString(buttonText, bfont)

bfont = new Font("Trebuchet MS", 10); button = new Bitmap(1, 1, PixelFormat.Format32bppRgb); g = Graphics.FromImage(button); tSize = g.MeasureString(buttonText, bfont);

Next, we need to calculate the size of the image required to contain the text, allowing for some space around the text. The constants HORT_PAD and VERT_PAD are used to define the space at the ends of the text and above/below the text.

 

buttonWidth = CInt(Math.Ceiling(tSize.Width + (HORT_PAD * 2))) buttonHeight = CInt(Math.Ceiling(tSize.Height + (VERT_PAD * 2)))

buttonWidth = Convert.ToInt32(Math.Ceiling(tSize.Width + (HORT_PAD * 2))); buttonHeight = Convert.ToInt32(Math.Ceiling(tSize.Height + (VERT_PAD * 2)));

Now that we know how big to create the image, the Bitmap used for the button image can be created. The PixelFormat.Format32bppRgb defines the Bitmap to be 32 bits per pixel, using eight bits each for the red, green, and blue color components. For the image being created, the format is not that important. For more graphically appealing images, however, the format plays a significant role.

 

button = New Bitmap(buttonWidth, _ buttonHeight, _ PixelFormat.Format32bppRgb)

button = new Bitmap(buttonWidth, buttonHeight, PixelFormat.Format32bppRgb);

Next, the entire image is filled with a background color. This requires creating a brush with the desired color and calling the FillRectangle method with the full size of the image being created. (Remember, the graphics coordinates always start at 0.) The FromHtml method of the ColorTranslator class is used to convert the HTML style color designation to the color required for the brush being created.

 

g = Graphics.FromImage(button) g.FillRectangle(New SolidBrush(ColorTranslator.FromHtml("#F0F0F0")), _ 0, _ 0, _ buttonWidth - 1, _ buttonHeight - 1)

g = Graphics.FromImage(button); g.FillRectangle(new SolidBrush(ColorTranslator.FromHtml("#F0F0F0")), 0, 0, buttonWidth - 1, buttonHeight - 1);

When filling an image with a background color, be careful of the color choices you make, since browsers do not consistently display all colors. It is best to stick to the 216 colors of the web-safe palette.


After filling the button image background color, a border is drawn around the perimeter of the image. This requires creating a pen with the desired color and width to do the drawing.

 

g.DrawRectangle(New Pen(Color.Navy, 2), _ 0, _ 0, _ buttonWidth - 1, _ buttonHeight - 1)

g.DrawRectangle(new Pen(Color.Navy, 2), 0, 0, buttonWidth - 1, buttonHeight - 1);

Finally, the text is drawn in the center of the image button. The centering is accomplished by offsetting the upper-left corner of the text block by the same amount as the spacing that was allowed around the text.

 

g.DrawString(buttonText, _ bfont, _ New SolidBrush(Color.Navy), _ HORT_PAD, _ VERT_PAD)

g.DrawString(buttonText, bfont, new SolidBrush(Color.Navy), HORT_PAD, VERT_PAD);

When the button image bitmap is returned to the Page_Load method, we need to create a MemoryStream object and save the bitmap in JPEG format to the memory stream. (We chose the JPEG format because it happened to work well with the images in this example. Depending on the circumstances, you may need to use another format, such as GIF.)

 

ms = New MemoryStream button.Save(ms, ImageFormat.Jpeg)

ms = new MemoryStream(); button.Save(ms, ImageFormat.Jpeg);

The final step in generating the button image is to write the binary stream to the Response object. This requires setting the ContentType property to match the format we saved the image to in the memory stream. In this example, the image was saved in JPEG format, so the ContentType must be set to image/jpg. This informs the browser that the stream being returned is an image of the proper type. We use the BinaryWrite method to write the image to the Response object.

 

Response.ContentType = "image/jpg" Response.BinaryWrite(ms.ToArray())

Response.ContentType = "image/jpg"; Response.BinaryWrite(ms.ToArray());

Using our example web form that dynamically creates button images requires setting the Src attribute of the image tag to the URL for the web form just described, passing the text for the image in the URL. A sample URL is shown here:

 src="/books/1/505/1/html/2/CH15CreateButtonVB.aspx?ButtonText=Dynamic" 

In our example, the Src attribute of the img tag is set in the create button click event of the test page code-behind.

Dynamically generating images can be resource intensive. To improve your application's performance, the generated images should be cached. Recipe 16.2 provides an example of how to cache the results as a function of the data passed in the QueryString.


See Also

Recipe 16.2 and MSDN Help for more on the .NET drawing libraries

Example 15-1. Create images dynamically (.aspx)

 <%@ Page Language="VB" AutoEventWireup="false"  CodeFile="CH15CreateButtonVB.aspx.vb"  Inherits="ASPNetCookbook.VBExamples.CH15CreateButtonVB" %> 

Example 15-2. Create images dynamically code-behind (.vb)

 Option Explicit On Option Strict On Imports System Imports System.Drawing Imports System.Drawing.Imaging Imports System.IO Namespace ASPNetCookbook.VBExamples ''' <summary> ''' This class provides the code-behind for ''' CH15CreateButtonVB.aspx ''' </summary> Partial Class CH15CreateButtonVB Inherits System.Web.UI.Page 'constants used to create URLs to this page Public Const PAGE_NAME As String = "CH15CreateButtonVB.aspx" Public Const QS_BUTTON_TEXT As String = "ButtonText" '''*********************************************************************** ''' <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 buttonText As String Dim button As Bitmap Dim ms As MemoryStream 'get button text from the query string and create the image buttonText = Request.QueryString(QS_BUTTON_TEXT) button = makeButton(buttonText) 'write image to response object ms = New MemoryStream button.Save(ms, ImageFormat.Jpeg) Response.ContentType = "image/jpg" Response.BinaryWrite(ms.ToArray( )) End Sub 'Page_Load '''*********************************************************************** ''' <summary> ''' This routine creates a button with the passed text. ''' </summary> ''' ''' <param name="buttonText"> ''' Set to the text to place on the button ''' </param> ''' ''' <returns>Bitmap of button that was created</returns> Private Function makeButton(ByVal buttonText As String) As Bitmap 'define the space around the text on the button Const HORT_PAD As Integer = 10 Const VERT_PAD As Integer = 2 Dim button As Bitmap Dim g As Graphics Dim bfont As Font Dim tSize As SizeF Dim buttonHeight As Integer Dim buttonWidth As Integer 'create the font that will used then create a dummy button to get 'a graphics object that provides the ability to measure the height 'and width required to display the passed string bfont = New Font("Trebuchet MS", 10) button = New Bitmap(1, 1, PixelFormat.Format32bppRgb) g = Graphics.FromImage(button) tSize = g.MeasureString(buttonText, bfont) 'calculate the size of button required to display the text adding 'some space around the text buttonWidth = CInt(Math.Ceiling(tSize.Width + (HORT_PAD * 2))) buttonHeight = CInt(Math.Ceiling(tSize.Height + (VERT_PAD * 2))) 'create a new button using the calculated size button = New Bitmap(buttonWidth, _ buttonHeight, _ PixelFormat.Format32bppRgb) 'fill the button area g = Graphics.FromImage(button) g.FillRectangle(New SolidBrush(ColorTranslator.FromHtml("#F0F0F0")), _ 0, _ 0, _ buttonWidth - 1, _ buttonHeight - 1) 'draw a rectangle around the button perimeter using a pen width of 2 g.DrawRectangle(New Pen(Color.Navy, 2), _ 0, _ 0, _ buttonWidth - 1, _ buttonHeight - 1) 'draw the text on the button (centered) g.DrawString(buttonText, _  bfont, _  New SolidBrush(Color.Navy), _  HORT_PAD, _  VERT_PAD) g.Dispose( ) Return (button) End Function 'makeButton End Class 'CH15CreateButtonVB End Namespace 

Example 15-3. Create images dynamically code-behind (.cs)

 using System; using System.Drawing; using System.Drawing.Imaging; using System.IO; namespace ASPNetCookbook.CSExamples { /// <summary> /// This class provides the code-behind for /// CH15CreateButtonCS.aspx /// </summary> public partial class CH15CreateButtonCS : System.Web.UI.Page { // constants used to create URLs to this page public const String PAGE_NAME = "CH15CreateButtonCS.aspx"; public const String QS_BUTTON_TEXT = "ButtonText"; ///*********************************************************************** /// <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) { String buttonText = null; Bitmap button = null; MemoryStream ms = null; // get button text from the query string and create image buttonText = Request.QueryString[QS_BUTTON_TEXT]; button = makeButton(buttonText); // write image to response object ms = new MemoryStream( ); button.Save(ms, ImageFormat.Jpeg); Response.ContentType = "image/jpg"; Response.BinaryWrite(ms.ToArray( )); } // Page_Load ///*********************************************************************** /// <summary> /// This routine creates a button with the passed text. /// </summary> /// /// <param name="buttonText"> /// Set to the text to place on the button /// </param> private Bitmap makeButton(String buttonText) { // define the space around the text on the button const int HORT_PAD = 10; const int VERT_PAD = 2; Bitmap button = null; Graphics g = null; Font bfont = null; SizeF tSize; int buttonHeight; int buttonWidth; // create the font that will used then create a dummy button to get // a graphics object that provides the ability to measure the height // and width required to display the passed string bfont = new Font("Trebuchet MS", 10); button = new Bitmap(1, 1, PixelFormat.Format32bppRgb); g = Graphics.FromImage(button); tSize = g.MeasureString(buttonText, bfont); // calculate the size of button required to display the text adding // some space around the text buttonWidth = Convert.ToInt32(Math.Ceiling(tSize.Width +   (HORT_PAD * 2))); buttonHeight = Convert.ToInt32(Math.Ceiling(tSize.Height + (VERT_PAD * 2))); // create a new button using the calculated size button = new Bitmap(buttonWidth, buttonHeight, PixelFormat.Format32bppRgb); // fill the button area g = Graphics.FromImage(button); g.FillRectangle(new SolidBrush(ColorTranslator.FromHtml("#F0F0F0")), 0, 0, buttonWidth - 1, buttonHeight - 1); // draw a rectangle around the button perimeter using a pen width of 2 g.DrawRectangle(new Pen(Color.Navy, 2), 0, 0, buttonWidth - 1, buttonHeight - 1); // draw the text on the button (centered) g.DrawString(buttonText,  bfont,  new SolidBrush(Color.Navy),  HORT_PAD,  VERT_PAD); g.Dispose( ); return (button); } // makeButton } // CH15CreateButtonCS } 

Example 15-4. Using the dynamically created images (.aspx)

 <%@ Page Language="VB" MasterPageFile="~/ASPNetCookbookVB.master" AutoEventWireup="false" CodeFile="CH15TestCreateButtonVB.aspx.vb" Inherits="ASPNetCookbook.VBExamples.CH15TestCreateButtonVB" Title="Test Dynamic Button Creation" %> <asp:Content  runat="server" ContentPlaceHolder> <div align="center" > Test Dynamic Button Creation (VB) </div> <table width="50%" align="center" border="0"> <tr> <td >Text For Button: </td> <td> <asp:TextBox  Runat="server" /> </td> <td> <input  runat="server"   type="button"   value="Create"   name="btnCreate"   onserverclick="btnCreate_ServerClick"/> </td> </tr> <tr> <td  runat="server"    colspan="3"    align="center"    > <br /> Last Button Created - <img  runat="server"   border="0" src="/books/1/505/1/html/2/"   align="middle" alt="button"/> </td> </tr> </table> </asp:Content> 

Example 15-5. Using the dynamically created images code-behind (.vb)

 Option Explicit On Option Strict On Imports System Namespace ASPNetCookbook.VBExamples ''' <summary> ''' This class provides the code-behind for ''' CH15TestCreateButtonVB.aspx ''' </summary> Partial Class CH15TestCreateButtonVB 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 If (Not Page.IsPostBack) Then 'make image button table cell invisible initially tdCreatedButton.Visible = False End If End Sub 'Page_Load '''*********************************************************************** ''' <summary> ''' This routine provides the event handler for the create button click ''' event. It is responsible for initializing the source property of the ''' image button to the URL of the dynamic button creation page. ''' </summary> ''' ''' <param name="sender">Set to the sender of the event</param> ''' <param name="e">Set to the event arguments</param> Protected Sub btnCreate_ServerClick(ByVal sender As Object, _               ByVal e As System.EventArgs) _ 'update the image tag with the URL to the page that will 'create the button and with the button text in the URL imgButton.Src = "CH15CreateButtonVB.aspx?ButtonText=" & _ txtButtonText.Text tdCreatedButton.Visible = True End Sub 'btnCreate_Click End Class 'CH15TestCreateButtonVB End Namespace 

Example 15-6. Using the dynamically created images code-behind (.cs)

 using System; namespace ASPNetCookbook.CSExamples { /// <summary> /// This class provides the code-behind for /// CH15TestCreateButtonCS.aspx /// </summary> public partial class CH15TestCreateButtonCS : 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) { if (!Page.IsPostBack) { // make image button table cell invisible initially tdCreatedButton.Visible = false; } } // Page_Load ///*********************************************************************** /// <summary> /// This routine provides the event handler for the create button click /// event. It is responsible for initializing the source property of the /// image button to the URL of the dynamic button creation page. /// </summary> /// /// <param name="sender">Set to the sender of the event</param> /// <param name="e">Set to the event arguments</param> protected void btnCreate_ServerClick(Object sender, System.EventArgs e) { // update the image tag with the URL to the aspx page that will // create the button and with the button text in the URL imgButton.Src = "CH15CreateButtonCS.aspx?ButtonText=" + txtButtonText.Text; tdCreatedButton.Visible = true; } // btnCreate_ServerClick } // CH15TestCreateButtonCS } 



ASP. NET Cookbook
ASP.Net 2.0 Cookbook (Cookbooks (OReilly))
ISBN: 0596100647
EAN: 2147483647
Year: 2003
Pages: 202

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net