Introducing the RoundedCorner Web Control
By Scott Mitchell
More Information About this Control! |
---|
A new article has been written with more information about this Web control. Once you have read this article, be sure to read Improving the RoundedCorners Web Control. |
Introduction
Many professionally done sites group related options into aesthetically-pleasing "boxes". On Amazon.com, for example, the left-hand column contains a number of these boxes: Search, Browse, and so on. Each box has a bordered outline, with a title at the top. To gussy things up a bit, the title bar at the top of the box has rounded corners, as opposed to just square corners.
![]() | ![]() |
|
This is an example of text inside of a RoundedCorners control that has a width of 150 pixels. Neat, eh? | ||
![]() | ![]() |
To overcome this loathsome activity, I decided to create a custom ASP.NET server control that would utilize the dynamic image-creation capabilities of GDI+ to create the corner images for me. The result is an ASP.NET Web control, which I call RoundedCorners, that you can drop on an ASP.NET Web page, set a few properties, and be presented with a nifty box with rounded corners, as shown on the right. In this article we'll examine how to use the RoundedCorners Web control in a page, as well as take a peek under the covers at the control's source code.
Techniques for Creating Boxes with Rounded Corners
Before we delve into how to use the RoundedCorners control, let's first discuss some of the techniques that can be employed to create a box with rounded corners:
- Create the box, rounded corners, and text, all as a single image.
- Use a three-column, three-row HTML
<table>
. With this approach, the left-most and right-most columns in the top and bottom rows each contain one of the four corners. A variant of this approach is to create a one-column, three-row table, where the top and bottom rows are complete images themselves. (That is, rather than having to create just the corner, you create the entire bottom or top row as an image.) - Use CSS-related techniques. Typically a few nested
<div>
tags are used with some relative positioning for the corners. For more information on using CSS simply Google CSS rounded corners, and you'll find a wealth of information.
With all of these approaches, however, one or more images are needed. (CSS3 offers the ability to create rounded corners and drop shadows without the use of images; unfortunately, CSS3 is still in the formation stages, and while some of its features are supported by some browsers, it's not anywhere close to being uniformly supported.)
The RoundedCorners Web control uses a 3x3 HTML <table>
to create the rounded corners. For those
CSS purists out there... well... sorry. The source code for this control is available at the end of this article,
so you are more than welcome to modify the control to emit CSS syntax. (If you do enhance the control, though, please send me
the updated version, as I can share it here with others.)
Using the RoundedCorners Web Control in an ASP.NET Web Page
At the end of this article you will find the complete source code as well as an assembly (a
.dll
file) compiled for the .NET Framework
1.1. If you are using Visual Studio .NET 2003, you can simply add the RoundedCorners control to your Toolbox by
right-clicking on the VS.NET Toolbox, choosing Add/Remove Items, and browsing to the RoundedCorners assembly. (If you
are using Visual Studio .NET 2002, you'll need to compile the provided source code and use that compiled assembly.)
Once you have added the control to the Toolbox, adding it to an ASP.NET Web page is as simple as dragging the control
from the Toolbox and dropping it onto the page's Designer.
Not an Ideal Design-Time Experience |
---|
One of the nice things about Visual Studio .NET is the design-time experience for ASP.NET page developers. When you add a control to the page's Designer, the Designer renders the control as you would see it in the browser. Change the control's properties, and its appearance in the Designer changes automatically, too. RoundedCorners, however, does not display the rounded corners in the Designer - instead there are broken image links. |
Once you add the RoundedCorners control to an ASP.NET Web page, you'll be able to configure it's properties via the Properties pane. The germane properties for this control are:
BackColor
- specifies the background color for the main area in the box.BorderColor
/BorderStyle
/BorderWidth
- specifies the border color, style, and width for the box.CornerHeight
/CornerWidth
- specifies the height and width for the corner images. These default to 13 pixels, but you can tweak the size if needed. (If you have a large title row, you might need to make the corners higher than 13 pixels.)Font
- specifies the font style information for the box's text. (Font name, size, bold, italic, etc.)ForeColor
- specifies the foreground color of the box's text.HorizontalAlign
- indicates how the box is horizontally aligned.ImageDirectory
- the RoundedCorners control only creates the corner images on a per-demand basis. After a corner image is created, it is saved to the Web server's file system, so that the image need not be recreated for each page load from each visitor. TheImageDirectory
property specifies the virtual directory where the images should go (i.e.,/Images/
).Padding
- the padding around the box's text.RoundedBottom
- determines whether or not rounded corners are also displayed at the bottom of the box. Defaults to True, meaning that rounded corners are placed on both the bottom and top.TextHorizontalAlign
- indicates how the text within the box is horizontally aligned.Title
- specifies the title for the box. If a title is specified, it is placed in the middle column of the<table>
's top row.TitleStyle
- provides style information for the title row, such as background color, font information, and so on.Width
- indicates the width of the box. This is optional, but oftentimes you'll want to give an absolute width for the box, such as150px
.
You can configure all of the properties for RoundedCorners through the Visual Studio .NET Properties pane and Designer, or you can declaratively specify the property values. I have a series of live demos available where you can see the resulting rounded corners box and the declarative markup that goes with it.
Looking Under the Hood at the RoundedCorners Control's Source Code
The code for the RoundedCorners server control is fairly straightforward. RoundedCorners is implemented as a composite control derived from the
WebControl
class. In the overridden
CreateChildControls()
method a Table
is created with three TableRow
s, each
with three TableCell
s. The upper-left, upper-right, bottom-left, and bottom-right cells display an
appropriately rounded corner. The upper-middle cell displays the title, if specified.
The rounded corners are displayed by adding an Image
Web control to the appropriate cells. This
Image
Web control is returned by a call to the method CreateCorner()
. If one were to
naively implement CreateCorner()
, they'd have it use GDI+ to dynamically generate a rounded corner image
each time is was called, but this is quite wasteful. Imagine a Web page has a box with rounded corners.
When it is visited for the first time, these four corner images will be dynamically created. Now, if another user
visits this same page, he'll need the same images; we could recreate them on the fly, but why not save the images from
the first time they were created? The purpose of CreateCorner()
, then, is to:
- Check to see if an image file exists on the file system for the rounded corner.
- If the file does not exist, dynamically create it and save it.
- Return an
Image
Web control whoseImageUrl
property equals the appropriate filename.
This is a pretty sensible approach, but there is one wrinkle. The rounded corner images are specific to a number of parameters, including:
- Which of the four corners is being created,
- The background color of the box,
- The box's border color, style, and width, and
- The specified height and width of the corner images.
For example, imagine that you have a box with a light blue background and a solid navy border with 1px width. When the page containing this rounded corner box is visited, the four corner images will be generated dynamically, and then saved to the file system. When subsequent visitors come, they'll be served the corner images that were saved to disk. Great. But now imagine that our page developer changes the background color to white. If he doesn't delete the associated corner images, visitors will now see a box with a white background, but whose corners have a light blue background!
To overcome this problem, we can give each corner a filename that spells out its defining characteristics. This is
precisely what CreateCorner()
does, as can be seen by the following code snippet. (Note that
CreateCorner()
takes in a parameter of type Corners
- this is an enumeration with the four
possible corner positions: UpperLeft
, UpperRight
, BottomLeft
, and
BottomRight
.)
|
The filename is composed of two characters specifying what corner the image is for (upper-left, upper-right, ...),
the HTML color value of its background color, the HTML color value of its border color, its border style, the
height and width of the corner image, and the border width. For example, the image for a 13px by 13px
upper-left corner with a blue background and a solid, black, 1px border would be:
ul.0000ff.000000.Solid.13-13.1px.gif
. A long filename, yes, but it uniquely identifies the corner
image by utilizing its defining characteristics. This way, if the page developer changes the background color to
white and the border width to 2px, the filename will change appropriately to: ul.000000.000000.Solid.13-13.2px.gif
.
(The filename is prepended with the value of the ImageDirectory
property as well, as you can see in
the initialization of the StringBuilder
in the first line of the above method.)
After the filename has been determined, CreateCorner()
checks to see if the file exists. If not,
it calls CreateRoundedCorner()
, which dynamically creates the image and saves it to the specified
file path. Finally, a new Image
Web control is instantiated and its ImageUrl
property is
set to the appropriate file path.
Dynamically Generating the Rounded Corner Image
Creating images of the fly in an ASP.NET Web page is not terribly difficult thanks to the GDI+ library. GDI+ is the term used to describe a set of classes in the .NET Framework for creating and manipulating images. Past articles on 4Guys - such as True Image Resizing, A Robust Image Gallery for ASP.NET, and Creating Snazzy Web Charts and Graphics On the Fly with ASP.NET - have shown how to utilize the GDI+ libraries to accomplish common image-related tasks.
The main challenge in creating or manipulating images with GDI+ is making those images look good. Since the corner images created for RoundedCorners are GIFs, I'll focus on the GDI+ issues with GIF files. When you create or open an image with the GDI+ classes, the classes use 32 bits per pixel, When you save a GIF file with GDI+, GDI+ dithers the image, reducing the palette to 256 Web-safe colors. The problem is that not all colors in the GIF will be accurately represented, which leads to a grainy looking image. The solution to overcoming grainy GIF images is to use quantization, which is the process of adjusting the GIF's palette so that it more closely matches the image's true colors, thereby resulting in a less grainy image.
Admittedly, I am no expert on image file formats and related algorithms, but fortunately there's a good article
on MSDN by Morgan Skinner called Optimizing
Color Quantization for ASP.NET Images, which looks at the problem in more details and provides a set of classes
for performing quantization. The RoundedCorners Web control utilizes Morgan's Palette-based Quantization algorithm
to "clean up" the graininess of the GIF image. Too, I modified the algorithm slightly so that the first palette
color is the transparent color. This transparent color is used as the background of the corner images, so that
if the rounded corner box is placed on a colored background, the background color shows through the area outside
of the border of the rounded corner. (Specifically, in the GetPalette()
method in PaletteQuantizer.cs
I added palette.Entries[0] = Color.FromArgb(0, _colors[0]);
, which sets the first palette item to
transparent (that's what the 0 passed into FromArgb()
indicates). In CreateRoundedCorner()
I define the palette's entries, and make sure to cover the image with the first palette entry color initially, before
adding the curve of the rounded corner.)
Conclusion
In this article we looked at RoundedCorners, a custom Web control I created to display a box with rounded corners without having to create any corner image files. RoundedCorners works by generating a 3x3 HTML
<table>
, with
rounded corner images in each corner. These images are created on the fly, if necessary, and then saved to the
Web server's file system so that they can be reused without having to be regenerated. To improve the quality of the
image, I utilize Morgan Skinner's quantization routines, and tweaked them ever so slightly to make the first palette
color the transparent one. The complete source code is downloadable at the end of this article.
Happy Programming!
Attachments:
The impetus for this control is due in part to two individuals: Paul Murphy, who on a listserv I'm on asked about if there were any way to create a rounded corners box without having to manually create a file, and then suggesting that a Web control be created that could do as such; and Pierre Huguet, who gave a talk on July 20, 2004 at the San Diego ASP.NET SIG about GDI+.
More Information About this Control! |
---|
A new article has been written with more information about this Web control. Once you have read this article, be sure to read Improving the RoundedCorners Web Control. |