LANGUAGES: C#
ASP.NET VERSIONS: 1.x | 2.x
Beautiful Buttons Abound
The free NiftyButton control detailed in this article can be used to
create strikingly attractive buttons that can be created and configured dynamically
– no images required. This control
builds on my DirectX article
by utilizing Filters to do the heavy lifting.
The standard Web button control is boring, pedestrian, and ugly. That’s why few
attractive Web sites use them. Instead, most use images. The drawback is that it
takes a significant amount of time to create enough images to fill all the image
buttons of an entire Web site. It also takes time to manage them and redo them all
the next time the design of the site changes. Wouldn’t it be nice to have a better
button control that could radically change its own appearance just by setting a
few properties? Well now you’ll have one.
To create such a control, you might first envision a control that generates its
own images by using the System.Drawing namespace; for more on the use of the System.Drawing
namespace, see Improve Your Image(s).
This could potentially save some image management effort and create attractive results.
However, Internet Explorer dictates that images must ultimately be generated via
their own URL, which means that any kind of image button must rely on code that
doesn’t exist in the current page. Therefore, you must have another page to generate
the image or an HTTP handler to intercept such requests and do the work. This makes
deployment and configuration a hassle. Wouldn’t it be better to have a button control
that can change its appearance without relying on images? Well, now you’ll have
one.

Figure 1: This button was created entirely with HTML. No images
were required. The results look great in Internet Explorer, and the special effects
are ignored by other browsers (which end up displaying fairly standard looking buttons).
If you recall from my DirectX
article, filters can be applied to standard buttons to improve their look. The following
snippet transforms an ugly gray square button into the fuzzy blue oval button shown
in Figure 1:
<input
type="submit"
name="NiftyButton10"
value="Oval
Shaped"
id="NiftyButton10"
style="FILTER:
progid:DXImageTransform.Microsoft.Alpha(style=2,startX=0,
startY=0,finishX=100,finishY=100,opacity=100,
finishOpacity=0);
COLOR:deepskyblue;BACKGROUND-COLOR:darkblue">
The Alpha filter defined inside the style for this button accepts up to seven parameters.
In addition, you can combine multiple filters for a seemingly infinite number of
effects. You’ve probably noticed this HTML definition is rather nonstandard looking.
It’s an odd way to define parameters and the resulting HTML can get quite complex
when combining multiple filters. It can also end up being quite a maintenance chore
to keep all of these complicated definitions consistent across a large Web site.
As if that weren’t enough complexity, Internet Explorer is very picky about line
breaks and spaces in filter definitions, and Visual Studio 2003 will happily (and
often) reformat the HTML so that it changes just enough to break the code, but not
enough to make the cause of the problem obvious. Wouldn’t it be nice to have a button
control that encapsulated all this complexity by exposing valid filter options and
parameters through a few well designed properties?
A Nifty Button Control
Like all custom controls, the NiftyButton control is a DLL that you can add to your
Visual Studio toolbox (by right clicking on the toolbox, choosing “Add/Remove Items”
and browsing to the DLL). Then you can drag the control onto any Web form and set
its properties to change how it will look at run time.
Using the NiftyButton control, a button identical to that shown in Figure 1 can
be created with the following definition:
<cc1:NiftyButton id="NiftyButton10" runat="server" Text="Oval Shaped" BackColor="DarkBlue" Alpha-Visible="True"
Alpha-StartX="0"
Alpha-StartY="0"
Alpha-Style="Radial"
Alpha-FinishOpacity="0"
Alpha-FinishY="100"
Alpha-FinishX="100"
Alpha-Opacity="100"
ForeColor="DeepSkyBlue"></cc1:NiftyButton>
This HTML snippet looks much cleaner and more intuitive than the previous
example. Of course, you need not muck with such definitions at all because the designer
and properties window in Visual Studio can manage all of this for you.
The NiftyButton control inherits from the standard button Web control,
so it gets all of that functionality automatically. The NiftyButton control adds
properties to manage parameters for the nine different filters that it supports.
It then concatenates the required style strings and outputs them via the OnPreRender
event listed in Figure 2.
protected override
void OnPreRender(EventArgs e)
{
//remove the old filter
string (if any)
this.Style.Remove("filter");
//Build the new filter
style string
StringBuilder sb = new
StringBuilder();
if (this.FlipHorizontal)
sb.Append("fliph ");
if (this.FlipVertical)
sb.Append("flipv ");
if (this.XRay)
sb.Append("xray ");
// delegate the subclasses
to add their
// own filter style strings
sb.Append(_Alpha.GetStyle());
sb.Append(_Blur.GetStyle());
sb.Append(_DropShadow.GetStyle());
sb.Append(_Emboss.GetStyle());
sb.Append(_Engrave.GetStyle());
sb.Append(_Glow.GetStyle());
//Output the completed
style string to the control
if (sb.Length>0)
this.Style.Add("filter",
System.Environment.NewLine
+ sb.ToString());
}
Figure 2: The OnPreRender event of the NiftyButton is where
the majority of the control’s functionality is orchestrated.
First, any preexisting filters (that may have been placed
there on a previous postback) are removed so that the current settings will be rendered
instead.
In the second code block, a few of the more simple (paramaterless) filters
are generated and added to the StringBuilder buffer. The third code block calls
the GetStyle method implemented in the subclasses that represent the more complex
filters. Each of these subclasses will generate their own output and add it to the
StringBuilder buffer. These subclasses will be examined in more detail later in
this article.
The final line in Figure 2 flushes the StringBuilder
buffer into the page by adding the resulting string to the Style collection of the
control.
(advertisement)
X-Ray Vision
XRay is a simple filter that takes no parameters. When enabled, it averages
the red and green values to create a grayscale output that can look rather alluring,
especially when combined with other filters. This filter is exposed through a simple
Boolean property of the NiftyButton control:
[Bindable(true),
Category("Filter"),
Description("Grayscale, averages
red & green values.")]
public bool
XRay
{
get
{
return((ViewState["xray"]
== null) ? false
:
(bool)ViewState["xray"]);
}
set
{ViewState["xray"] = value;}
}
The property has three attributes. The first attribute
(Bindable) permits data binding. The second attribute (Category) ensures this property
will appear under the “Filter” category of the properties window when the control
is compiled and placed on a Web form. The third attribute (Description) will appear
in the bottom portion of the properties window when the property is selected. The
get and set methods use ViewState to store the property value so it will be persisted
between page postbacks. If no value is found in ViewState, the default of False
will be used, which will turn off the XRay effect. The NiftyButton control contains
several other similar properties that can be examined by downloading the source
code (see end of article for details). See Figure 3 for a partial
list of filters managed by the NiftyButton control.
|
Filter
|
Description
|
Example Syntax:
|
|
Alpha
|
Adjusts the opacity of the object.
|
progid:DXImageTransform.Microsoft.Alpha(style=2,
startX=0, startY=0, finishX=100, finishY=100, opacity=100, finishOpacity=0)
|
|
DropShadow
|
Creates a solid silhouette of the object,
offset in the specified direction.
|
progid:DXImageTransform.Microsoft.DropShadow(color=red,
offX=2, offY=2, positive=true)
|
|
Emboss
|
Displays the object as embossed texture
with grayscale.
|
progid:DXImageTransform.Microsoft.Emboss(Bias=50)
|
|
Engrave
|
Displays the object as engraved texture
with grayscale.
|
progid:DXImageTransform.Microsoft.Engrave(Bias=50)
|
|
Glow
|
Adds radiance around the edges of the object.
|
progid:DXImageTransform.Microsoft.Glow(color=red,
strength=6)
|
|
MotionBlur
|
Causes the object to appear to be in motion.
|
progid:DXImageTransform.Microsoft.MotionBlur(add=true,
direction=180, strength=6)
|
Figure 3: These are a few of Microsoft’s many DirectX filters that are exposed
for Web page elements to consume. These filters (and others) are managed automatically
by the NiftyButton control so you needn’t be worried about the somewhat arcane syntax.
A Glowing Review
The Glow filter is more complex than the XRay filter. The Glow filter
accepts several parameters, which are exposed via NiftyButton properties. The Glow
property is read-only, and is publicly exposed with the following code:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
NotifyParentProperty(true), Category("Filter")]
public glow Glow
{get{return
_Glow;}}
The get method simply returns an instance of the Glow class, which is
encapsulated within the NiftyButton class. More interesting in this code snippet,
however, are the attributes. As mentioned previously, the Category attribute specifies
that this property will show up in the “Filter” section of the property window.
The other two attributes ensure that this subclass will persist its properties.
The NotifyParentProperty attribute ensures that any changes to the properties in
this subclass will bubble up to the containing NiftyButton object so that they will
manage state in unison. The DesignerSerializationVisibility attribute ensures that
changes made in the properties window of Visual Studio will be persisted to the
HTML definition in the ASPX page.
The source code for the Glow class is shown in
Listing One at the end of this article. The Glow
class is a subclass of the NiftyButton control. It encapsulates the complexities
involved with managing and displaying the Glow filter. The class is defined using
the TypeConverter attribute, which is given the ExpandableObjectConverter as a parameter.
This ensures that the properties of this class will appear in the properties window
in an expandable way. That is, a “+” sign will appear next to the property to let
it expand and show all its properties at design time, as shown in Figure 4.

Figure 4: The ExpandableObjectConverter TypeConverter attribute allows subclasses
to be expanded to show their properties. In this example, the Glow class has been
expanded (via the plus signs along the left) to show its three properties: Color,
Strength, and Visible.
The constructor of the Glow class shown in Listing One
accepts a StateBag as a parameter, so it can accept ViewState as a parameter from
the containing NiftyButton control. The Visible property (which is False by default)
uses ViewState to retain its value between page postbacks. The Color property is
similar, with a default value of Red. The Strength property adds some basic bounds
checking to ensure the value is not zero.
The ToString property is overridden so a value can be displayed directly
next to the Glow property in the properties window (as illustrated in Figure
4). In this case, it merely displays “Visible” or “Not Visible”, but
it could easily display a more complex representation of the data contained within
the object.
The final piece of code in the Glow class is the GetStyle function. It
concatenates the Glow filter’s Style string together according to the property values.
It inserts spaces and new line characters where necessary to ensure proper display
in the browser.
The Glow class is one of many subclasses in the NiftyButton control.
The rest of the subclasses have the same basic design and can be downloaded for
a detailed review.
More Niftiness?
Standard buttons are boring, Image buttons are a maintenance hassle,
and filters (by themselves) use an overly complicated syntax. I hope you agree that
the NiftyButton control is a nifty solution for all these problems (see Figure
5).

Figure 5: The NiftyButton control can display in a nearly infinite number
of ways by simply adjusting some properties.
After examining the NiftyButton source code you should have a good understanding
of how to build custom controls, inherit and extend existing Web controls, and use
filters to visually enhance controls. You should also have a good feel for creating
controls that provide a quality design-time experience by using attributes to expose
complex public property trees.
Give the NiftyButton a whirl and let me know what you think of it. Feel
free to tinker with the code, expand upon it, and learn from it. Drop me an e-mail
if you come across any interesting new revelations on the subject; I’d love to hear
about them.
References:
Filter References:
http://www.w3schools.com/dhtml/dhtml_css.asp
http://msdn.microsoft.com/workshop/author/filter/reference/reference.asp
ExpandableObjectConverter:
http://www.bluevisionsoftware.com/WebSite/TipsAndTricksDetails.aspx?Name=ExpandableObjectConverter
The sample code in this article is available for
download.
This article was originally published in
ASP.NET Pro Magazine.
Listing One:
[TypeConverter(typeof(ExpandableObjectConverter))
Description("Adds radiance
around the button edges.")]
public class
glow {
private StateBag _ViewState;
public glow(StateBag ViewState) //Constructor
{
_ViewState=ViewState;
}
[NotifyParentProperty(true),
Description("Should the glow
be visible at runtime?")]
public bool
Visible
{
get
{
return((_ViewState["glowVisible"]
== null) ? false
:
(bool)_ViewState["glowVisible"]);
}
set
{_ViewState["glowVisible"] = value;}
}
[NotifyParentProperty(true),
Description("The color of the
glow around the button.")]
public Color Color
{
get
{
return((_ViewState["GlowColor"]
== null) ? Color.Red :
(Color)_ViewState["GlowColor"]);
}
set
{_ViewState["GlowColor"] =
value;}
}
[NotifyParentProperty(true),
Description("Number of pixels
that the effect extends.")]
public byte
Strength
{
get
{
return((_ViewState["GlowStrength"]
== null) ? (byte)6
:
(byte)_ViewState["GlowStrength"]);
}
set
{
if
(value<1) value=1;
_ViewState["GlowStrength"]
= value;
}
}
public override
string ToString()
{
//For appearances in the
property window
if (this.Visible)
return "Visible" ;
else return
"Not Visible";
}
public
String GetStyle()
{
StringBuilder sb = new
StringBuilder();
if (this.Visible)
{
sb.Append(" progid:DXImageTransform.Microsoft.Glow(");
sb.Append("color="+ this.Color.ToKnownColor() + ",");
sb.Append("strength="+ this.Strength.ToString() + ") ");
sb.Append(System.Environment.NewLine);
return
sb.ToString();
}
else
{
return
string.Empty;
}
}
}
End of Listing One