ControlFreak
LANGUAGES:C# | VB.NET
ASP.NETVERSIONS: 1.x
Beautiful Buttons Abound
Create Attractive Buttons with the NiftyButton Control
By Steve C. Orr
The standard Web button control is boring, pedestrian, andugly. That?s why few attractive Web sites use them. Instead, most use images. Thedrawback is that it takes a significant amount of time to create enough imagesto fill all the image buttons of an entire Web site. It also takes time tomanage them and redo them all the next time the design of the site changes. Wouldn?tit be nice to have a better button control that could radically change its ownappearance just by setting a few properties? Well now you?ll have one.
The NiftyButton control we?ll create in this article canbe used to create strikingly attractive buttons that can be created andconfigured dynamically, with no images required. This control builds on myDirectX column EyeCandy by utilizing filters to do the heavy lifting.
To create such a control, you might first envision acontrol that generates its own images by using the System.Drawing namespace; formore on the use of the System.Drawing namespace, see ImproveYour Image(s). This could potentially save some image management effort andcreate attractive results. However, Internet Explorer dictates that images mustultimately be generated via their own URL, which means that any kind of imagebutton must rely on code that doesn?t exist in the current page. Therefore, youmust have another page to generate the image or an HTTP handler to interceptsuch requests and do the work. This makes deployment and configuration ahassle. Wouldn?t it be better to have a button control that can change itsappearance without relying on images? Well, now you?ll have one.
Figure 1: This button was createdentirely with HTML. No images were required. The results look great in InternetExplorer, and the special effects are ignored by other browsers (which end updisplaying fairly standard looking buttons).
If you recall from EyeCandy, filters can be applied to standard buttons to improve their look. Thefollowing snippet transforms an ugly gray square button into the fuzzy blueoval 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 buttonaccepts up to seven parameters. In addition, you can combine multiple filtersfor a seemingly infinite number of effects. You?ve probably noticed this HTMLdefinition is rather nonstandard looking. It?s an odd way to define parametersand the resulting HTML can get quite complex when combining multiple filters. Itcan also end up being quite a maintenance chore to keep all of thesecomplicated definitions consistent across a large Web site. As if that weren?tenough complexity, Internet Explorer is very picky about line breaks and spacesin filter definitions, and Visual Studio 2003 will happily (and often) reformatthe HTML so that it changes just enough to break the code, but not enough tomake the cause of the problem obvious. Wouldn?t it be nice to have a buttoncontrol that encapsulated all this complexity by exposing valid filter optionsand parameters through a few well designed properties?
A Nifty Button Control
Like all custom controls, the NiftyButton control is a DLLthat you can add to your Visual Studio toolbox (by right clicking on thetoolbox, choosing ?Add/Remove Items? and browsing to the DLL). Then you candrag the control onto any Web form and set its properties to change how it willlook at run time.
Using the NiftyButton control, a button identical to thatshown 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 intuitivethan the previous example. Of course, you need not muck with such definitionsat all because the designer and properties window in Visual Studio can manageall of this for you.
The NiftyButton control inherits from the standard button Webcontrol, so it gets all of that functionality automatically. The NiftyButtoncontrol adds properties to manage parameters for the nine different filtersthat it supports. It then concatenates the required style strings and outputsthem via the OnPreRender event listed in Figure 2.
protected override void OnPreRender(EventArgs e)
{
//remove the oldfilter string (if any)
this.Style.Remove("filter");
//Build the newfilter 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 thesubclasses to add their
// own filter stylestrings
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 thecompleted style string to the control
if (sb.Length>0)this.Style.Add("filter",
System.Environment.NewLine+ sb.ToString());
}
Figure 2: TheOnPreRender event of the NiftyButton is where the majority of the control?sfunctionality is orchestrated.
First, any preexisting filters (that may have been placedthere on a previous postback) are removed so that the current settings will berendered instead.
In the second code block, a few of the more simple(paramaterless) filters are generated and added to the StringBuilder buffer. Thethird code block calls the GetStyle method implemented in the subclasses thatrepresent the more complex filters. Each of these subclasses will generatetheir own output and add it to the StringBuilder buffer. These subclasses willbe examined in more detail later in this article.
The final line in Figure 2 flushes the StringBuilderbuffer into the page by adding the resulting string to the Style collection ofthe control.
X-Ray Vision
XRay is a simple filter that takes no parameters. Whenenabled, it averages the red and green values to create a grayscale output thatcan look rather alluring, especially when combined with other filters. Thisfilter is exposed through a simple Boolean property of the NiftyButton control:
[Bindable(true), Category("Filter"),
Description("Grayscale, averages red & greenvalues.")]
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 propertywill appear under the ?Filter? category of the properties window when thecontrol 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 isselected. The get and set methods use ViewState to store the property value soit 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. TheNiftyButton control contains several other similar properties that can beexamined by downloading the source code (see end of article for details). SeeFigure 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: Theseare a few of Microsoft?s many DirectX filters that are exposed for Web pageelements to consume. These filters (and others) are managed automatically bythe NiftyButton control so you needn?t be worried about the somewhat arcanesyntax.
A Glowing Review
The Glow filter is more complex than the XRay filter. TheGlow filter accepts several parameters, which are exposed via NiftyButtonproperties. The Glow property is read-only, and is publicly exposed with thefollowing code:
[DesignerSerializationVisibility
(DesignerSerializationVisibility.Content),
NotifyParentProperty(true), Category("Filter")]
public glow Glow
{
get{return _Glow;}
}
The get method simply returns an instance of the Glowclass, which is encapsulated within the NiftyButton class. More interesting inthis code snippet, however, are the attributes. As mentioned previously, theCategory attribute specifies that this property will show up in the ?Filter?section of the property window. The other two attributes ensure that thissubclass will persist its properties. The NotifyParentProperty attributeensures that any changes to the properties in this subclass will bubble up tothe containing NiftyButton object so that they will manage state in unison. TheDesignerSerializationVisibility attribute ensures that changes made in theproperties window of Visual Studio will be persisted to the HTML definition inthe ASPX page.
The source code for the Glow class is shown in Listing One. The Glow class is a subclass of theNiftyButton control. It encapsulates the complexities involved with managingand displaying the Glow filter. The class is defined using the TypeConverterattribute, which is given the ExpandableObjectConverter as a parameter. Thisensures that the properties of this class will appear in the properties windowin an expandable way. That is, a ?+? sign will appear next to the property tolet it expand and show all its properties at design time, as shown in Figure 4.
Figure 4: TheExpandableObjectConverter TypeConverter attribute allows subclasses to beexpanded to show their properties. In this example, the Glow class has beenexpanded (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 Oneaccepts a StateBag as a parameter, so it can accept ViewState as a parameterfrom the containing NiftyButton control. The Visible property (which is Falseby default) uses ViewState to retain its value between page postbacks. TheColor property is similar, with a default value of Red. The Strength propertyadds some basic bounds checking to ensure the value is not zero.
The ToString property is overridden so a value can bedisplayed directly next to the Glow property in the properties window (as illustratedin Figure 4). In this case, it merely displays ?Visible? or ?Not Visible?, but itcould easily display a more complex representation of the data contained withinthe object.
The final piece of code in the Glow class is the GetStylefunction. It concatenates the Glow filter?s Style string together according tothe property values. It inserts spaces and new line characters where necessaryto ensure proper display in the browser.
The Glow class is one of many subclasses in theNiftyButton control. The rest of the subclasses have the same basic design andcan be downloaded for a detailed review.
More Niftiness?
Standard buttons are boring, Image buttons are amaintenance hassle, and filters (by themselves) use an overly complicatedsyntax. I hope you agree that the NiftyButton control is a nifty solution forall these problems (see Figure 5).
Figure 5: The NiftyButton controlcan display in a nearly infinite number of ways by simply adjusting someproperties.
After examining the NiftyButton source code you shouldhave a good understanding of how to build custom controls, inherit and extendexisting Web controls, and use filters to visually enhance controls. You shouldalso have a good feel for creating controls that provide a quality design-timeexperience by using attributes to expose complex public property trees.
Give the NiftyButton a whirl and let me know what youthink of it. Feel free to tinker with the code, expand upon it, and learn fromit. Drop me an e-mail if you come across any interesting new revelations on thesubject; 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 inthis article is available for download.
Steve C. Orr is anMCSD and a Microsoft MVP in ASP.NET. He?s been developing software solutionsfor leading companies in the Seattlearea for more than a decade. When he?s not busy designing software systems orwriting about them, he can often be found loitering at local user groups andhabitually lurking in the ASP.NET newsgroup. Find out more about him at http://SteveOrr.netor e-mail him at mailto:Steve@Orr.net.
Begin Listing One
[TypeConverter(typeof(ExpandableObjectConverter))
Description("Adds radiance around the buttonedges.")]
public class glow {
private StateBag _ViewState;
public glow(StateBag ViewState) //Constructor
{
_ViewState=ViewState;
}
[NotifyParentProperty(true),
Description("Should the glow be visible atruntime?")]
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 thebutton.")]
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 effectextends.")]
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 appearancesin the property window
if (this.Visible)return "Visible" ;
else return"Not Visible";
}
public StringGetStyle()
{
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);
returnsb.ToString();
}
else
{
returnstring.Empty;
}
}
}
End Listing One