Improving the RoundedCorners Web ControlBy Scott Mitchell
A couple weeks ago I created a custom ASP.NET server control for displaying some text within a box with rounded corners. This control, which I called RoundedCorner, was discussed in detail in the article Introducing the RoundedCorners Web Control. Ideally, such rounded corner boxes are created by having a graphics designer create the necessary rounded corner images using Photoshop or some other graphics editing program. The HTML designer, then, can create the necessary markup (HTML and/or CSS) to display a box using these rounded corner images. While this is indeed the "best practices" approach for creating a box with rounded corners, it is not always feasible for those of us who are developer teams of one, and whose artistic skills match those of a brain-damaged poodle. The RoundedCorners Web control aids those of us, like me, who fall in this boat by dynamically creating the corner images using GDI+.
Since the publication of the original RoundedCorners article, I have received a lot of great feedback on suggestions for improvements. The main two suggestions were:
- Add the ability to put anything inside a box with rounded corners, not just some text message. That is, I should be able to have a DataGrid, some HTML markup, and a TextBox Web control all within a RoundedCorners Web control instance.
- Use anti-aliasing when generating the images, to help smooth out the rounded corners.
Opening Up the RoundedCorners Control
The previous incarnation of RoundedCorners had a string
Textproperty. The value of this property was the text (or HTML markup) displayed within the box with rounded corners. Clearly a page developer would have a lot more freedom in displaying content within a box with rounded corners if, rather than having to specify a string property, she could simply drag and drop controls into the box, much like page developers can add Web controls to the Panel Web control. To allow for this, I did the following three things:
- Set the
ParseChildrenattributes of the RoundedCorners Web control to the appropriate values.
- Turn RoundedCorners into a rendered Web control from a composite control.
- Created a design-time class that derived from
All ASP.NET server controls can utilize a number of attributes to specify metadata about the control. Commonly this metadata is used by Visual Studio .NET to enhance the design-time experience, but some attributes are used for other purposes. Two important attributes are
ParseChildren, which dictate how a Web control reacts to any child content within its tags. Child content is the markup that appears between the starting and closing tags for a control's declarative syntax:
The child content can be interpreted in two ways:
- As property values - many built-in ASP.NET Web controls specify property values as child content. For example,
the DataGrid class will specify style information, as well as column information as child content. This actual content
maps to properties in the DataGrid class.
<asp:DataGrid runat="server" AutoGenerateColumns="False" ...> <ItemStyle BackColor="Peach" Font-Name="Verdana" ...></ItemStyle> <Columns> <asp:BoundColumn ... /> ... </Columns> </asp:DataGrid>
- As children controls - the child content can be interpreted as controls that should be added to the
Web control's control hierarchy. The Panel Web control, for example, has the HTML and Web controls that appear
within it specified in its child content area:
<asp:Panel runat="server" ...> What is your name?<br /> <asp:TextBox runat="server" ... /> </asp:Panel>
ParseChildrenattributes are used. By default, the
ParseChildrenhave values of False and True. The
PersistChildrenattribute specifies whether Visual Studio .NET should persist the child content as children controls. The
ParseChildrenattribute indicates whether or not the child content should be parsed as properties. So, when
PersistChildrenis False and
ParseChildrenis True, the behavior is that inner XML content is treated as property values (again, this is the default behavior). If you want to child content to be treated as child controls in the control hierarchy, you need to explicitly set
PersistChildrento True and
ParseChildrenis False, like so:
For more information on these two attributes, and how they affect how the child content in the declarative syntax of a
Web control is interpreted, be sure to read: Using
Converting from a Composite Control to a Rendered Control
Previously RoundedCorners was a composite control, meaning that I overrided the
CreateChildControls()method and programmatically constructed a child control hierarchy for the control. This control hierarchy contained the necessary controls to display a box with rounded corners, with the specified
Textinside. While composite controls have their place, they aren't suited for a control where the page developer can specify the control hierarchy through the declarative syntax. In such a case, it's better to go with a rendered control. A rendered control is one that emits the correct markup explicitly for the control during the Render stage. The markup is emitted by calls to the
HtmlTextWriterinstance passed into the control's
Rendered controls and composite controls are two different ways to generate the appropriate markup for a custom server control. A thorough discussion of these two techniques is beyond the scope of this article, but you can learn more at: Composition vs. Rendering. The key thing to understand is that for a control that can have its control hierarchy specified declaratively by the page developer, a rendered control likely makes more sense. Hence, I refactored RoundedCorners to use the rendering technique as opposed to the composition technique.
Creating a Design-Time Class
With the two changes above, the RoundedCorners control allows for arbitrary markup in the child content region of the control's declarative syntax. That is, you could create the RoundedControl with a DataGrid in it using declarative syntax like:
However, the design-time experience is woefully lacking. What we'd like to be able to do is have the design-time experience
mimic that of the Panel Web control, enabling page developers to simply drag and drop controls from the Toolbox into the
RoundedCorners control. To accomplish this we need to create a custom designer class that derives from
ReadWriteControlDesigner provides a simple HTML interface in the designer into which a page developer and
drag-and-drop controls. Sadly we, the control designer, have very little control over the actual HTML used in the VS.NET designer.
ReadWriteControlDesigner has a
GetDesignTimeHtml() method, it's never called. All we
can do is specify the style properties for the design-time interface.
The good news is that the design-time experience of RoundedCorners allows for drag-and-drop, and a somewhat WYSIWYG editing interface. The bad news is that that editing interface is anything but ideal. Since the custom designer class cannot richly specify the HTML that should be rendered in Visual Studio .NET's Design view, the RoundedCorners can show only the "body" of the box with rounded corners. That is, in the Designer you won't see any rounded corners, or even the title of the box (if specified). But these will appear when viewed through a Web browser. The screenshot to the right shows a view of the RoundedCorners control in the VS.NET Designer; note that the rounded corners are nowhere to be found, and the title is not shown.
Using Anti-Aliased Rounded Corners
Another suggestion I received was to anti-alias the rounded corner images. Anti-aliasing is the process by which the rough edges of a curve are smoothed. Anti-aliasing works by taking the colors at the edge of a line and smoothing them with an intermediate color that's inbetween the color of the line and the background. For example, if you had a black, curved line, ant-aliasing would add some grey pixels along the edge of the line so that, from a human's perspective, the line looked smooth and not jagged. An example of two lines - the top one anti-aliased, the bottom one not - can be seen on the left.
Fortunately anti-aliasing with GDI+ is fairly simple. The
Graphics class has a
that can be assigned one of the following values from the
To overcome this, I added an optional property to the RoundedCorners Web control called
BackgroundBackColor (a bit of
a tongue-twister). If this property is omitted, then the rounded corner image has its exterior painted with the transparent color,
so that it doesn't matter the color of the background you add the RoundedCorners control to. If, however, you explicitly provide
BackgroundBackColor, the exterior of the rounded corner will be filled with the specified color and the
image will be anti-aliased. The downside of providing a
BackgroundBackColor is that you must know the color behind
the rounded corner box, and if that color changes, users will see rounded corners with a background that doesn't match the color behind it.
(Special thanks to Nick Gilbert for helping with some anti-aliasing issues and
doing a great job in explaining the concepts to me...)
|Using the |
Avid 4Guys reader Mel G. writes in to share:
I've made a small improvement that I'd like to share with you. By adding a call to
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
.dllfile) 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.
Thanks for many great suggestions, the RoundedCorners control has been improved in two ways: by allowing page developers to add any content into the box with rounded corners, and allowing anti-aliasing of the auto-generated GIF images.