ControlFreak
LANGUAGES: VB.NET | C#
ASP.NET VERSIONS: 3.5
Generate Dynamic Buttons
Create Attractive Image Buttons Instantly at Run Time
By Steve C. Orr
Standard HTML buttons are sturdy and functional, yet their ugly gray interface renders them fairly useless for design-conscious Web sites. For this reason, most Web sites tend to use the ImageButton control much more often than the Button control. Of course, the main problem with ImageButtons is the need to create and maintain all those images. And if your customers are like most, they likely change their minds several times during the development process about exactly which colors they d like to see on their buttons. Such innocently na ve requests can generate lots of rework that tends to suck away time from other important development tasks. They don t understand how simply changing a color or two can take so much work and trying to explain it to them can end up sounding whiney and unprofessional.
By examining the dynamic button generation techniques detailed here, you can learn how to satisfy such customer requests easily and promptly. You ll see how with a little forethought button images can be changed, site-wide and instantaneously, with no more than a few keystrokes. As a result, you ll find yourself spending a lot less time mucking around in Photoshop so you can progress toward more important work.
Creating the Image
At the heart of every image button is an image. The DynaBtn class listed in Figure 1 can create just such an image dynamically at run time. Simply feed the GenButton function some basic parameters (for the text to be displayed, the colors to be used, and the desired margins) and it will return a bitmap that matches your specifications. This class could be compiled into its own DLL for optimal reuse, or it could simply be included in the App_Code folder of an ASP.NET Web application.
Imports System.Drawing
Imports System.Drawing.Imaging
Public Class DynaBtn
Public Shared Function GenButton( _
ByVal text As String, _
ByVal backColor As Color, _
ByVal foreColor As Color, _
ByVal font As Font, _
ByVal padH As Integer, _
ByVal padV As Integer) As Bitmap
'create a starter image
Dim bmp As New Bitmap(1, 1, _
PixelFormat.Format32bppRgb)
Dim g As Graphics = Graphics.FromImage(bmp)
'measure the size needed for the specified text & font
Dim size As SizeF = g.MeasureString(text, font)
'add some size for the margins
Dim width As Integer = Convert.ToInt32( _
size.Width + (PadH * 2))
Dim height As Integer = Convert.ToInt32( _
size.Height + (PadV * 2))
'now recreate the image at the correct size
bmp = New Bitmap(width, _
height, _
PixelFormat.Format32bppRgb)
g = Graphics.FromImage(bmp)
'fill it with the background color
Dim brush As New SolidBrush(backColor)
g.FillRectangle(brush, 0, 0, width - 1, height - 1)
'now draw the text with the specified forecolor
Dim brush2 As New SolidBrush(foreColor)
g.DrawString(text, font, brush2, PadH, PadV)
Return bmp
End Function
End Class
Figure 1: The DynaBtn class can be used to dynamically generate ImageButton images as needed at run time.
The first code block in Figure 1 creates a throw-away bitmap used primarily for initial measurement purposes. The second code block does the actual measuring; it determines how large the button will need to be in order to display the specified text with the specified font. The third code block adds a bit of padding onto that measurement for margins.
Now that all the required sizes have been calculated, the fourth code block in Figure 1 begins the creation of the real bitmap that will be displayed to the user. A standard 32-bit RGB bitmap is specified here, although many other (less common) options are available if you re feeling adventurous.
Finally, brushes are created to fill the button with the specified background color and draw the specified text onto the button. The resulting bitmap is returned by the function.
Figure 2 displays an optional overloaded version of the GenButton function. This overloaded GenButton function simply calls the original GenButton function (listed in Figure 1) after setting some hard-coded default parameters. Hard-coding defaults in this way can be acceptable in some situations, although later in this article we ll explore preferable techniques for configuring them.
Public Shared Function GenButton( _
Optional ByVal text As String = "Submit") As Bitmap
'create defaults
Dim PadH As Integer = 5
Dim PadV As Integer = 5
Dim font As Font = New Font("Arial", 10)
Dim BackColor As Color = Color.LightSkyBlue
Dim ForeColor As Color = Color.DarkBlue
Return GenButton( _
text, _
BackColor, _
ForeColor, _
font, _
PadH, _
PadV)
End Function
Figure 2: This overloaded version of the GenButton function simplifies the parameter list at the expense of some flexibility.
This GenButton function can be called with a simple line of code, such as:
Dim bmp As Bitmap = DynaBtn.GenButton("Log In")
A Windows Forms application could then directly display this bitmap in a PictureBox control, although Web developers have an extra step or two that must be dealt with to get the image displayed in the correct place at the correct time (see Figure 3).
Figure 3: This is a basic button
generated dynamically at run time by the GenButton function of the DynaBtn
class.
Displaying the Image
For an image to be displayed on a Web page, it must be referenced by that Web page. Images are referenced from Web pages with a standard HTML tag, which is often generated by ASP.NET controls such as the Image and ImageButton controls.
A Web page that contains an image requires at least two separate browser requests: one for the textual page content and one for the image. This necessitates a separate handler to deal with the separate image request. Standard Web forms (ASPX pages) and HTTP handlers are both reasonable solutions for responding to such requests. An HTTP handler can be slightly more efficient, because it avoids most of the superfluous HTML handling routines. However, the ASPX-based solution is simpler to implement, and, if done right, it too can evade almost as much of the unnecessary HTML handling routines. The DynaBtn class listed earlier can be used by either of these image-serving techniques.
An ASPX Image Server
To create an ASPX page to serve up the dynamic button images, first add a new Web form to an ASP.NET Web application. Choose a short unique filename such as DynBtn.aspx. You can remove all the HTML from this Web form because this special page will be emitting an image instead of HTML. All the real work will happen in the code-behind for this page.
It s best to render the image as early as possible in the page lifecycle to efficiently evade most of the Web page s superfluous HTML handling routines. In this case, the PreInit event is adequate for this goal (Figure 4 shows how it s done).
'Imports System.Drawing
Protected Sub Page_PreInit(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles Me.PreInit
Response.Clear() 'Delete any HTML buffered so far
Response.ContentType = "image/jpeg" 'output image instead
Dim Text As String = "Submit" 'default text
If Request.QueryString("Text") IsNot Nothing Then
'Retrieve button text from a QueryString parameter
Text = Server.UrlDecode(Request.QueryString("Text"))
End If
'Call the button image generator
Dim bmp As Bitmap = DynaBtn.GenButton(Text)
bmp.Save( _
Response.OutputStream, _
Imaging.ImageFormat.Jpeg)
Response.End()
End Sub
Figure 4: The PreInit method of a standard ASPX page is suitable for rendering the dynamic button image.
The first line of Figure 4 ensures any HTML-related gunk is removed from the response stream. The second line specifies that this page will instead output a jpeg image.
The second block of code gets the button text from a QueryString parameter. If the Text parameter is not found in the QueryString, a default of Submit will be displayed as the button text.
The third block of code retrieves the button image from the GenButton method listed in Figure 2, which in turn calls the GenButton method of Figure 1. The final line (Response.End) aborts the rest of the normal page lifecycle because it s not needed.
The final piece of the puzzle is a reference to this special image-generating page from a content page (such as default.aspx; refer back to Figure 3 for the resulting image):
ID="ImageButton1" runat="server" ImageUrl="~/DynBtn.aspx?Text=Log+In" /> Now what s going to happen when the customer
requests that the button colors be changed? Well, you could simply change the
hard-coded button defaults in Figure 2. Then you ll have to recompile and
redeploy the application. In simple situations this can be a reasonable
approach, but some software systems in some companies require more laborious
deployment scenarios. This could be further complicated if you choose to deploy
the DynaBtn class in its own assembly, which would improve cross-project reuse,
but potentially adds an extra compilation and deployment step, as well. Generally speaking, a better approach would be to ditch
the code in Figure 2 and instead refactor those button defaults into the image
generator page listed in Figure 4. To take it one step further, it would be
better yet for the code to pull those defaults from the web.config file so that
updates can be deployed without needing to recompile. Let s take a closer look
at this approach. First, add some custom default button values to the
web.config: Then modify the code DynBtn.aspx.vb code from Figure 4 to
retrieve these default button values from the web.config file. Figure 5 shows
how this is done. You might optionally choose to enhance this image server page
even further by allowing custom button values to be passed via a QueryString
(as is done with the button text). 'Imports System.Drawing 'Imports System.Web.Configuration.WebConfigurationManager Protected Sub Page_PreInit(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles Me.PreInit Response.Clear() Response.ContentType =
"image/jpeg" Dim Text =
"Submit" 'default text 'retrieve button text
from QueryString parameter If
Request.QueryString("Text") IsNot Nothing Then Text =
Server.UrlDecode(Request.QueryString("Text")) End If 'retrieve default button
values from web.config Dim sBackClr As String =
AppSettings("DynaBtn_BackColor") Dim sForeClr As String =
AppSettings("DynaBtn_ForeColor") Dim BackColor As Color =
Color.FromName(sBackClr) Dim ForeColor As Color =
Color.FromName(sForeClr) Dim PadH As Integer =
AppSettings("DynaBtn_PadH") Dim PadV As Integer =
AppSettings("DynaBtn_PadV") Dim FontName As String =
AppSettings("DynaBtn_FontName") Dim FontSize As Integer =
AppSettings("DynaBtn_FontSize") Dim font As Font = New
Font(FontName, FontSize) 'Call the button image
generator Dim bmp As Bitmap
= DynaBtn.GenButton( _ Text, _ BackColor, _ ForeColor, _ font, _ PadH, _ PadV) bmp.Save( _ Response.OutputStream,
_ Imaging.ImageFormat.Jpeg) Response.End() End Sub Figure 5: This
code is more configurable than the code in Figure 4 because it pulls default
values from the web.config file instead of hard-coding them. Now the colors, fonts, and padding of all buttons
site-wide can be changed instantly from one central location (the web.config
file) without needing to recompile anything (see Figure 6). The code accompanying this article can be quite useful
as-is, but it should also be considered a great starting point for an even more
powerful button-generation system. I can envision many potential enhancements
to this system, such as adding colorful gradient backgrounds and fancy borders
with 3-D effects. Further enhancements could include the ability to specify
different categories of buttons, such as primary and secondary buttons with
individually adjustable colors and shading. The only real limits are those of
your imagination, so let your mind wander and see what you come up with. I d
love to hear about any interesting enhancements you add. C# and VB.NET source code accompanying this article is
available for download. Steve C. Orr is an
ASPInsider, MCSD, Certified ScrumMaster, Microsoft MVP in ASP.NET,
and author of Beginning ASP.NET 2.0 AJAX
by Wrox. He s been developing software solutions for leading companies in the Seattle
area for more than a decade. When he s not busy designing software systems or
writing about them, he can often be found loitering at local user groups and
habitually lurking in the ASP.NET newsgroup.
Find out more about him at http://SteveOrr.net
or e-mail him at mailto:[email protected]. Enhancing Configurability
Figure 6: You can optionally apply
different color schemes to different buttons. Conclusion