12.1. 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 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.

Creating a button image on the fly

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

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:

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

Discussion
  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.

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

Discussion
  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.

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

Discussion
  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.

Tip

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.

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

Discussion
  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.

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

Discussion
  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.

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

Discussion
  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.)

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

Discussion
  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.

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

Discussion
  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.

Tip

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.

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
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset