You need to create a button image on the fly using text generated during the running of your application.
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:
Import the System.Drawing
and
System.Drawing.Imaging
namespaces.
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.
Create a MemoryStream
object and save the bitmap
in JPEG format (or other format) to the memory stream.
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 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:
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
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.
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.
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.
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);
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, 1), _ 0, _ 0, _ buttonWidth - 1, _ buttonHeight - 1) 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.
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 then 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
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
.
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 pagePublic 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 pagepublic 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"> </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 }