Recipe 12.1 Drawing Button Images on the Fly

     

12.1.1 Problem

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

12.1.2 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 application ”for example, text passed in on 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.

Example 12-1 through Example 12-3 show the .aspx file and VB and 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.

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

Figure 12-1. Creating a button image on the fly
figs/ancb_1201.gif

12.1.3 Discussion

Creating button images on the fly can be handy for a couple of related reasons. First, using button images may help provide the look you want for your application. Second, generating them on the fly can avoid you having to create and save a whole series of button images to the filesystem on the prospect that 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 rendering of the page, 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 returned ”for example, " image/jpg ", indicating an image in JPEG format. Our approach does exactly that, but with a unique 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 just fine.

Two web forms are used in our example that illustrates this solution. The first one renders no HTML but instead 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 simply contains the @ Page directive to link the code-behind class for the page.

In the Page_Load event 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 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 1-pixel-by-1-pixel bitmap is created:

 
figs/vbicon.gif
 bfont = New Font("Trebuchet MS", 10) button = New Bitmap(1, 1, PixelFormat.Format32bppRgb) g = Graphics.FromImage(button) tSize = g.MeasureString(buttonText, bfont) 
figs/csharpicon.gif
 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.

 
figs/vbicon.gif
 buttonWidth = CInt(Math.Ceiling(tSize.Width + (HORT_PAD * 2))) buttonHeight = CInt(Math.Ceiling(tSize.Height + (VERT_PAD * 2))) 
figs/csharpicon.gif
 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 that will be used for the button image can be created. The PixelFormat.Format32bppRgb defines the Bitmap to be 32 bits per pixel, using 8 bits each for the red, green, and blue color components . For the image being created here, the format is not that important. For more graphically appealing images, however, the format plays a significant role.

 
figs/vbicon.gif
 button = New Bitmap(buttonWidth, _ buttonHeight, _ PixelFormat.Format32bppRgb) 
figs/csharpicon.gif
 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.

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.


 
figs/vbicon.gif
 g = Graphics.FromImage(button) g.FillRectangle(New SolidBrush(ColorTranslator.FromHtml("#F0F0F0")), _ 0, _ 0, _ buttonWidth - 1, _ buttonHeight - 1) 
figs/csharpicon.gif
 g = Graphics.FromImage(button); g.FillRectangle(new SolidBrush(ColorTranslator.FromHtml("#F0F0F0")), 0, 0, buttonWidth - 1, buttonHeight - 1); 

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.

 
figs/vbicon.gif
 g.DrawRectangle(New Pen(Color.Navy, 1), _ 0, _ 0, _ buttonWidth - 1, _ buttonHeight - 1) 
figs/csharpicon.gif
 g.DrawRectangle(new Pen(Color.Navy, 1), 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.

 
figs/vbicon.gif
 g.DrawString(buttonText, _ bfont, _ New SolidBrush(Color.Navy), _ HORT_PAD, _ VERT_PAD) 
figs/csharpicon.gif
 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.)

 
figs/vbicon.gif
 ms = New MemoryStream button.Save(ms, ImageFormat.Jpeg) 
figs/csharpicon.gif
 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 then use the BinaryWrite method to write the image to the Response object.

 
figs/vbicon.gif
 Response.ContentType = "image/jpg" Response.BinaryWrite(ms.ToArray( )) 
figs/csharpicon.gif
 Response.ContentType = "image/jpg"; Response.BinaryWrite(ms.ToArray( )); 

Using our example web form that dynamically creates button images merely 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="CH12CreateButtonVB.aspx?ButtonText=Test Button" 

In our example, the Src attribute of the img tag is set in the create button click event of the test page code-behind. To make things a little easier and avoid hardcoding page names and QueryString information, two constants ( PAGE_NAME and QS_BUTTON_TEXT ) are defined in the code-behind of the image creation page and are used here in building the URL.

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


12.1.4 See Also

Recipe 13.2; MSDN Help for more on the .NET drawing libraries

Example 12-1. Create images dynamically (.aspx)
 <%@ Page Language="vb" AutoEventWireup="false" Codebehind="CH12CreateButtonVB.aspx.vb" Inherits="ASPNetCookbook.VBExamples.CH12CreateButtonVB" %> 

Example 12-2. Create images dynamically code-behind (.vb)
 Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name : CH12CreateButtonVB.aspx.vb ' ' Description: This module provides the code behind for the ' CH12CreateButtonVB.aspx page. It streams a dynamically ' created button image to the browser. ' '***************************************************************************** Imports System Imports System.Drawing Imports System.Drawing.Imaging Imports System.IO Namespace ASPNetCookbook.VBExamples Public Class CH12CreateButtonVB Inherits System.Web.UI.Page 'constants used to create URLs to this page  Public Const PAGE_NAME As String = "CH12CreateButtonVB.aspx"   Public Const QS_BUTTON_TEXT As String = "ButtonText"  '*************************************************************************** ' ' ROUTINE: Page_Load ' ' DESCRIPTION: This routine provides the event handler for the page load ' event. It is responsible for initializing the controls ' on the page. '--------------------------------------------------------------------------- Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.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 '*************************************************************************** ' ' ROUTINE: MakeButton ' ' DESCRIPTION: This routine creates a button with the passed text. '---------------------------------------------------------------------------  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 1   g.DrawRectangle(New Pen(Color.Navy, 1), _   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 'CH12CreateButtonVB End Namespace 

Example 12-3. Create images dynamically code-behind (.cs)
 //---------------------------------------------------------------------------- // // Module Name: CH12CreateButtonCS.aspx.cs // // Description: This module provides the code behind for the // CH12CreateButtonCS.aspx page // //**************************************************************************** using System; using System.Drawing; using System.Drawing.Imaging; using System.IO; namespace ASPNetCookbook.CSExamples { public class CH12CreateButtonCS : System.Web.UI.Page { // constants used to create URLs to this page  public const String PAGE_NAME = "CH12CreateButtonCS.aspx";   public const String QS_BUTTON_TEXT = "ButtonText";  //************************************************************************ // // ROUTINE: Page_Load // // DESCRIPTION: This routine provides the event handler for the page // load event. It is responsible for initializing the // controls on the page. //------------------------------------------------------------------------ private void Page_Load(object sender, System.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 //************************************************************************ // // ROUTINE: makeButton // // DESCRIPTION: This routine creates a button with the passed text. //------------------------------------------------------------------------  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 1   g.DrawRectangle(new Pen(Color.Navy, 1),   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  } // CH12CreateButtonCS } 

Example 12-4. Using the dynamically created images (.aspx)
 <%@ Page Language="vb" AutoEventWireup="false" Codebehind="CH12TestCreateButtonVB.aspx.vb" Inherits="ASPNetCookbook.VBExamples.CH12TestCreateButtonVB" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Test Dynamic Button Creation</title> <link rel="stylesheet" href="css/ASPNetCookbook.css"> </head> <body leftmargin="0" marginheight="0" marginwidth="0" topmargin="0"> <form id="frmTestCreateButton" method="post" runat="server"> <table width="100%" cellpadding ="0" cellspacing="0" border="0"> <tr> <td align="center"> <img src="images/ASPNETCookbookHeading_blue.gif"> </td> </tr> <tr> <td class="dividerLine"> <img src="images/spacer.gif" height="6" border="0"></td> </tr> </table> <table width="90%" align="center" border="0"> <tr> <td align="center">&nbsp;</td> </tr> <tr> <td align="center" class="PageHeading"> Test Dynamic Button Creation (VB) </td> </tr> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <tr> <td align="center"> <table width="50%"> <tr> <td class="LabelText">Text For Button: </td> <td> <asp:TextBox ID="txtButtonText" Runat="server" /> </td> <td> <input id="btnCreate" runat="server" type="button" value="Create" name="btnCreate"> </td> </tr> <tr> <td id="tdCreatedButton" runat="server" colspan="3" align="center" class="LabelText"><br /> Last Button Created - <img id="imgButton" runat ="server" border="0" align="middle"> </td> </tr> </table> </td> </tr> </table> </form> </body> </html> 

Example 12-5. Using the dynamically created images code-behind (.vb)
 Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: CH12TestCreateButtonVB.aspx.vb ' ' Description: This module provides the code behind for the ' CH12TestCreateButtonVB.aspx page. ' '***************************************************************************** Namespace ASPNetCookbook.VBExamples Public Class CH12TestCreateButtonVB Inherits System.Web.UI.Page 'controls on the form Protected txtButtonText As System.Web.UI.WebControls.TextBox Protected WithEvents btnCreate As System.Web.UI.HtmlControls.HtmlInputButton Protected tdCreatedButton As System.Web.UI.HtmlControls.HtmlTableCell Protected imgButton As System.Web.UI.HtmlControls.HtmlImage '*************************************************************************** ' ' ROUTINE: Page_Load ' ' DESCRIPTION: This routine provides the event handler for the page load ' event. It is responsible for initializing the controls ' on the page. '--------------------------------------------------------------------------- Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load If (Not Page.IsPostBack) Then 'make image button table cell invisible initially tdCreatedButton.Visible = False End If End Sub 'Page_Load '*************************************************************************** ' ' ROUTINE: btnCreate_Click ' ' DESCRIPTION: 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. '---------------------------------------------------------------------------  Private Sub btnCreate_ServerClick(ByVal sender As Object, _   ByVal e As System.EventArgs) _   Handles btnCreate.ServerClick   '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 = CH12CreateButtonVB.PAGE_NAME & _   "?" & CH12CreateButtonVB.QS_BUTTON_TEXT & _   "=" & txtButtonText.Text   tdCreatedButton.Visible = True   End Sub 'btnCreate_Click  End Class 'CH12TestCreateButtonVB End Namespace 

Example 12-6. Using the dynamically created images code-behind (.cs)
 //---------------------------------------------------------------------------- // // Module Name: CH12TestCreateButtonCS.aspx.cs // // Description: This module provides the code behind for the // CH12TestCreateButtonCS.aspx page // //**************************************************************************** using System; namespace ASPNetCookbook.CSExamples { public class CH12TestCreateButtonCS : System.Web.UI.Page { // controls on the form protected System.Web.UI.WebControls.TextBox txtButtonText ; protected System.Web.UI.HtmlControls.HtmlInputButton btnCreate; protected System.Web.UI.HtmlControls.HtmlTableCell tdCreatedButton; protected System.Web.UI.HtmlControls.HtmlImage imgButton; //************************************************************************ // // ROUTINE: Page_Load // // DESCRIPTION: This routine provides the event handler for the page // load event. It is responsible for initializing the // controls on the page. //------------------------------------------------------------------------ private void Page_Load(object sender, System.EventArgs e) { // wire the create button click event this.btnCreate.ServerClick += new EventHandler(this.btnCreate_ServerClick); if (!Page.IsPostBack) { // make image button table cell invisible initially tdCreatedButton.Visible = false; } } // Page_Load //************************************************************************ // // ROUTINE: btnCreate_ServerClick // // DESCRIPTION: 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. //------------------------------------------------------------------------  private 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 = CH12CreateButtonCS.PAGE_NAME +   "?" + CH12CreateButtonCS.QS_BUTTON_TEXT +   "=" + txtButtonText.Text;   tdCreatedButton.Visible = true;   } // btnCreate_ServerClick  } // CH12TestCreateButtonCS } 



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

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