ControlFreak
LANGUAGES:
VB.NET | C#
ASP.NET
VERSIONS: 2.x
Hot Menu Options
Make Your Sites Sizzle with a Custom HoverList Control
By Steve C. Orr
You can never have too many menu controls. Ever. But more
is not always good enough they have to be unique, as well. A unique menu
control can really differentiate a Web site, making it stand out from the
information overload that is the World Wide Web. To help you get moving in this
distinctive direction, try using the HoverList custom control provided with this
article to spice up your site.
The HoverList control contains configurable menu items
that highlight as the mouse is moved over them at run time. The list can be
specified at design time or run time, and may optionally be data bound. Each
list item consists of a string value and a formatted text item that may
optionally include HTML. A configurable header area is also optionally included
(see Figure 1).
Figure 1: The HoverList control
contains a configurable list of menu items that can be highlighted with various
color effects as the mouse hovers over them.
To use this control, download the sample code and add the
included HoverList.dll to your Visual Studio 2005 toolbox (see end of article
for download details). Then drag it onto any WebForm to get started using it. The
ASPX declaration looks roughly like this (depending on how the control s
properties have been configured):
<cc1:hoverlist id="HoverList1"
runat="server"
borderstyle="Solid" borderwidth="1px"
datasourceid="SiteMapDataSource1"
datatextfield="Title"
datavaluefield="Url"
hoverbackcolor="Blue" hoverforecolor="LightCyan"
padding="0.2em"
width="200px">
</cc1:hoverlist>
The design-time experience is very much like working with
a ListBox because the ListBox and the HoverList control both inherit from
ListControl (as explained in more detail later). The HoverList s most
noteworthy members are listed in the table in Figure 2. There are several
properties for configuring the appearance of the header area of the control,
and a couple important properties for configuring the color changes that happen
when the mouse is hovered over each menu item.
|
Noteworthy
HoverList Members
|
Description
|
|
OnSelectedIndexChanged event
|
Raised whenever the user clicks on one of the menu items.
|
|
ShowHeader property
|
Specifies the visibility of the header area. Default: True
|
|
HeaderText property
|
Specifies the text displayed in the header area.
|
|
HeaderBackColor property
|
Specifies the background color of the header area. Default:
LightBlue
|
|
HeaderForeColor property
|
Specifies the color of the header text. Default: Black
|
|
HeaderFont property
|
Specifies the font of the header text.
|
|
HoverBackColor property
|
Specifies the background color of the menu when the
mouse hovers over it. Default: Blue
|
|
HoverForeColor property
|
Specifies the text color of the menu item when the mouse
hovers over it. Default: LightCyan
|
|
Padding property
|
Specifies the padding around the edge of the control. Default:
0.2em
|
|
HorizontalAlign property
|
Specifies the alignment of the menu items. Default: Center
|
|
AppendDataBoundItems property
|
Appends data-bound items to statically declared list
items. Default: False
|
Figure 2: The
HoverList control provides several useful members in addition to those that the
base ListControl already provides.
The HoverList control s menu items can be configured at
design time via the standard ListItem Collection Editor shown in Figure 3. Alternatively,
items can be added programmatically at run time or standard data-binding
techniques can be applied. You can even combine these techniques, thanks to the
AppendDataBoundItems property.
Figure 3: The HoverList control
supports standard data-binding techniques, or items can be added at design time
through the ListItem Collection Editor dialog box.
AppendDataBoundItems is a new property of the base
ListControl class (as of ASP.NET version 2.0). It lets you declare one or more
list items at design time, and then also add data-bound list items at run time.
This is especially useful for controls such as DropDownList, because you can
configure the first item to say Choose an item from the list and then add the
rest of the list via standard data binding. This was not possible in ASP.NET 1;
instead, you either needed to ditch data binding or add a dummy record to the
data source to achieve such functionality.
When the user clicks on one of the HoverList s menu items
at run time, the page posts back and the OnSelectedIndexChanged event is
raised, as demonstrated here:
Protected Sub HoverList1_SelectedIndexChanged(ByVal _
sender As Object, ByVal e
As System.EventArgs) _
Handles
HoverList1.SelectedIndexChanged
Response.Write("You
Clicked: " & HoverList1.SelectedValue)
End Sub
That covers most of what you need to know to use the
HoverList control. If you re the intellectually curious type (which I assume
most of you are), keep reading to tour the source code and find out about the
details of its inner workings.
Viscera
One of the most fundamental pieces of information about
the HoverList control is that it inherits from ListControl. ListControl is an
abstract base class provided by the .NET Framework from which many common
controls inherit. A few of the standard ASP.NET controls that extend the
ListControl class are ListBox, DropDownList, and CheckBoxList. Like these
controls, HoverList doesn t need to deal with trivia, such as data binding,
because that is all handled by the base ListControl class. This greatly
simplifies the code, reducing it to little more than an exercise in HTML
rendering. Essentially, the HoverList control consumes the collection of
ListItems that is managed by the ListControl class, and renders custom HTML
from that data. The general structure of the control is shown here:
<DefaultProperty("HeaderText"), _
ToolboxData("<{0}:HovrList runat=server></{0}:HovrList>")>
_
Public Class HoverList
Inherits ListControl
Implements
IPostBackEventHandler
'TODO: constructor
'TODO: public
properties
'TODO: Rendering
'TODO: Postback
handling
End Class
Another important detail is that the control implements
the IPostBackHandler interface. This enables the control to handle postbacks in
a standard way. The associated code is located in the RaisePostBackEvent
routine. The logic is simple for the HoverList control: Unselect any previously
selected item and ensure that the item the user clicked is now the selected
item. Then hand off control to the base class which will raise the standard
OnSelectedIndexChanged event to the page:
Public Sub RaisePostBackEvent(ByVal _
eventArgument As
String) Implements _
System.Web.UI.IPostBackEventHandler.RaisePostBackEvent
If SelectedItem IsNot
Nothing Then
SelectedItem.Selected = False
End If
Dim li As ListItem = _
Items.FindByValue(eventArgument)
If li IsNot Nothing
Then li.Selected = True
MyBase.OnSelectedIndexChanged(Nothing)
End Sub
You ll notice references to the SelectedItem property and
the Items collection, but you ll find no such properties in the HoverList
source code. Rather, these properties are inherently available from the base
ListControl class. Therefore, the HoverList control only needs to define the
unique properties it requires for its customizable rendering chores. While most
of the properties are utilitarian in nature, a couple of them are shown in
Figure 4 to give you a taste.
<Bindable(True), Category("Appearance"), _
Description("Sets or gets the font color of the
header.")> _
Public Property HeaderForeColor() As System.Drawing.Color
Get
If
ViewState("HeaderForeColor") IsNot Nothing Then
Return
CType(ViewState("HeaderForeColor"), Color)
Else
Return Color.Black
'default
End If
End Get
Set(ByVal value As Color)
ViewState("HeaderForeColor") = value
End Set
End Property
Private Const Content As _
System.ComponentModel.DesignerSerializationVisibility = _
DesignerSerializationVisibility.Content
Private _lblHeader As Label = New Label
<Bindable(True), Category("Appearance"), _
Description("The font applied to the header area."), _
NotifyParentProperty(True), _
DesignerSerializationVisibility(Content)> _
Public Property HeaderFont() As FontInfo
Get
If
ViewState("HeaderFont") IsNot Nothing Then
Return
CType(ViewState("HeaderFont"), FontInfo)
Else
Return
_lblHeader.Font
End If
End Get
Set(ByVal value As
FontInfo)
ViewState("HeaderFont") = value
End Set
End Property
Figure 4: Because
the concept of a header is foreign to the base ListControl class, the HoverList
control must explicitly define header-related properties.
The HeaderForeColor property is needed so the application
developer may specify which color they would like the text of the header area
to be. The property stores the property in ViewState, and uses Black by
default. The HeaderFont property is a little more complicated because of the
fact that the Font property is complex and contains many sub-properties. Most
of the details related to this are managed by .NET automatically if you tell
it to. That s what the extra attributes are for NotifyParentProperty and
DesignerSerializationVisibility are critical for ensuring the properties are
persisted properly, especially at design time.
Of course, all these properties are pointless until they
are used by the rendering code to output the customized HTML that makes up the
HoverList control. The overridden Render event is listed in Figure 5.
Protected Overrides Sub Render(ByVal tw As HtmlTextWriter)
'create a container
Dim pnl As Panel =
CreateContainer()
'create the header
If Me.ShowHeader Then
CreateHeader(pnl)
'create each list item
For Each li As ListItem
In Me.Items
CreateItem(pnl, li)
Next
pnl.RenderControl(tw)
End Sub
Figure 5: The
overridden Render event delegates rendering tasks to several other internal
functions.
As you can see, each piece of the control is rendered by
custom functions: CreateContainer, CreateHeader, and CreateItem. The
CreateContainer function listed in Figure 6 is a fairly simple function that
does little more than instantiate a Panel control and set its properties to
some proper defaults. This panel will act as the container for the other
elements of the HoverList control. Many of the HoverList s properties are piped
through to be managed by the Panel control.
Private Function CreateContainer() As Panel
Dim pnl As New Panel
pnl.ID = Me.ID
pnl.Style(HtmlTextWriterStyle.Padding) = _
Padding.ToString
pnl.HorizontalAlign =
Me.HorizontalAlign
pnl.BorderColor =
Me.BorderColor
pnl.BorderStyle = Me.BorderStyle
pnl.BorderWidth =
Me.BorderWidth
pnl.Width = Me.Width
pnl.Height = Me.Height
pnl.Visible =
Me.Visible
pnl.Enabled =
Me.Enabled
pnl.ToolTip =
Me.ToolTip
Return pnl
End Function
Figure 6: The
CreateContainer function instantiates a Panel control to contain the child
controls that make up the HoverList control.
If the application developer has set the ShowHeader
property of the HoverList control to True, then the CreateHeader function is
called to generate the header area of the control. As demonstrated here, the
header area is nothing more than a Label control:
Private Sub CreateHeader(ByVal pnl As Panel)
Dim lbl As Label =
_lblHeader
lbl.Text =
Me.HeaderText & "<br />" 'For Firefox
lbl.Width = New
Unit(100, UnitType.Percentage)
lbl.BackColor =
Me.HeaderBackColor
lbl.Font.CopyFrom(Me.HeaderFont)
lbl.ForeColor =
Me.HeaderForeColor
pnl.Controls.Add(lbl)
End Sub
The For loop shown in Figure 5 loops through each ListItem
managed by the base ListControl class and renders each item individually by
invoking the CreateItem subroutine shown in Figure 7. This is what makes up the
bulk of the control s final rendered appearance.
Private Sub CreateItem(ByVal pnl As Panel, _
ByVal li As ListItem)
Dim lbl As New Label
lbl.Text = li.Text &
"<br />" 'For Firefox
lbl.Width = New Unit(100,
UnitType.Percentage)
lbl.BackColor =
Me.BackColor
lbl.ForeColor =
Me.ForeColor
If li.Enabled Then
lbl.Style(HtmlTextWriterStyle.Cursor) = "hand"
lbl.Attributes("onMouseOver")
= _
"this.style.backgroundColor='" & _
ColorTranslator.ToHtml(Me.HoverBackColor) _
&
"';" & "this.style.color='" & _
ColorTranslator.ToHtml(Me.HoverForeColor) _
&
"';"
lbl.Attributes("onMouseOut")
= _
"this.style.backgroundColor='" & _
ColorTranslator.ToHtml(Me.BackColor) _
&
"';" & "this.style.color='" & _
ColorTranslator.ToHtml(Me.ForeColor) & "';"
lbl.Attributes("onClick") = _
Page.ClientScript.GetPostBackClientHyperlink(Me, _
li.Value)
End If
lbl.Font.CopyFrom(Me.Font)
pnl.Controls.Add(lbl)
End Sub
Figure 7: The
CreateItem subroutine is invoked once for every ListItem that s managed by the
underlying ListControl class. It instantiates and configures a Label control to
represent each ListItem.
Like the header area, each ListItem will be represented by
a Label control. The CreateItem subroutine instantiates and configures a Label
control to represent the ListItem parameter. Because the ASP.NET 2.0 version of
the standard ListIem class now includes an Enabled property, it is used here to
represent a richer rendering for enabled items. For example, the Label s Cursor
style is set to hand so that the mouse cursor will appear as a hand icon when
moved over enabled items at run time (as shown in Figure 1).
The CreateItem subroutine shown in Figure 7 continues by
adding client-side onMouseOver and onMouseOut events to the Label control to
implement the unique color changes that occur when the mouse moves over the
item at run time. These client-side events use JavaScript to change the text
color and the background color of the label as the mouse enters and leaves its
boundaries.
The final client-side event to be wired up is the onClick
event. When the user clicks on the label, the page will post back and,
ultimately, the OnSelectedItemChanged event will fire, allowing the page to
respond to the user s command. ASP.NET s GetPostBackClientHyperlink method
dynamically generates the code that invokes the post back. This method uses
ASP.NET s adaptive rendering engine to generate proper PostBack code, no matter
which browser or device is being utilized by the end user.
Finally, the selected font is applied to the label, and
the label is added to the containing panel s control collection. Then the panel s
complete control tree is finally output when execution returns to the last line
of the render function of Figure 5.
Conclusion
The HoverList is a nice control to have around to add a
quick and functional menu of options to the user. Its visual interactivity adds
a bit of spice to a Web site without being overly flashy. The design-time
experience is quite similar to that of a ListBox, so the learning curve should
be small.
By inheriting from ListControl, rich functionality can be
taken advantage of with little or no code necessary such as data binding and
practical design-time features. Sprinkle in a touch of rendering code with a
dash of client-side JavaScript code and a useful little control starts to
emerge. Using the techniques demonstrated in this article, you can extend the
HoverList control with enhanced functionality, or use the knowledge to create a
custom control that lives up to your own dreams or at least your manager s
requirements.
The source code for
the HoverList control (in both VB and C#) is available for download.
Steve C. Orr is an
MCSD and a Microsoft MVP in ASP.NET. 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:Steve@Orr.net.