Creating a ComboBox

Don’t Let HTML Limitations Stand in Your Way

Control Freak

LANGUAGES: VB.NET | SQL

ASP.NET VERSIONS: 1.0 | 1.1

 

Creating a ComboBox

Don't Let HTML Limitations Stand in Your Way

 

By Steve C. Orr

 

HTML supports TextBox controls, ListBox controls, and dropdown lists. Inexplicably however, there is no ComboBox control. As all Windows forms programmers know, a ComboBox control lets the user type in a value or select a value from a dropdown list. This functionality can be quite handy in a variety of situations. This article will use composition to create a ComboBox control that you can use in your Web applications.

 

Composite controls were discussed briefly in the last "Control Freak," but the details of such a design were postponed until now. While rendering generally produces the most CPU-efficient code, "composition" is usually more efficient with your development and testing time. We all appreciate efficiently performing code, but you must weigh this against the clich that time is money. If you're able to cut development time and still provide high-quality code, then you're the type of programmer most managers value.

 

E Pluribus Unum

A composite control encapsulates the functionality of two or more existing controls, resulting in one super control. Figure 1 demonstrates how several standard Web controls will be assembled into one ComboBox control. The primary sub-controls will be a TextBox, a ListBox, and a Label control. The TextBox will allow the user to type in a value, and it will display any value that the user might select from the dropdown list. This dropdown list will really be a standard ListBox control that will be shown and hidden when the arrow to the right of the TextBox is clicked. This arrow will be a character from the Webdings font displayed in a standard Label control. Finally, all of these will be inside a Panel control to help keep everything grouped properly. By taking advantage of the rich functionality that these well-tested controls provide, development and testing time will be saved by not "reinventing the wheel."

 


Figure 1: By combining several existing controls, a brand new ComboBox control is born.

 

To determine the best design for the functionality of the control, a basic use case should be examined:

1)        The user clicks the down arrow (which is really a Label) to drop down the item list.

2)        The item list (which is really a ListBox) is made visible.

3)        The user selects an item from the list.

4)        The item list is made invisible, and the selected item's text is displayed in the text portion of the control (which is really a TextBox).

 

What's the best way to accomplish this functionality? The easiest way would be to do a postback between each action so the text and visible properties of the child controls can be set appropriately from server side code. This would work. However, it would also be very slow and inefficient to do this many postbacks to achieve such basic functionality. The bar will be set higher for this ComboBox control. Client-side JavaScript will handle these tasks so that no postbacks will be necessary. This will make the control much more responsive and professional.

 

For example, using the "visibility" style of the ListBox control, it can be shown and hidden on command from client-side code. The client-side OnClick event of the arrow Label control will invoke JavaScript that looks a lot like that shown in Figure 2. Of course, not every detail of the control can or should be done on the client side. After all, this is a server control that's being created.

 

if (MyListBox.style.visibility=='visible')

{

  MyListBox.style.visibility='hidden';

}

else

{

  MyListBox.style.visibility='visible';

}

Figure 2: This client-side JavaScript code will be executed by the user's browser. It will toggle the visibility of the ListBox control that simulates the dropdown portion of the ComboBox control.

 

Back to the Server

Because composition will be used (instead of rendering, the technique used for last month's bar graph control), the Render event won't be needed. Instead, the CreateChildControls method of the base System.Web.UI.Control class will be overridden, and the bulk of the work will be done there.

 

The skeleton of this composite control is listed in Figure 3. The code starts by importing some useful namespaces, then declaring the class name with some basic attributes that let it appear nicely in the toolbox at design time. Because this class is a control, it inherits from the standard Control class from which all Web controls are derived.

 

It also implements the INamingContainer interface. If you've ever examined the HTML output of your ASP.NET pages, you've probably noticed that the client-side names of the controls don't always match the names you've given them in the server code. This is because ASP.NET automatically renames them to keep things organized, and to ensure that no two controls have the same client-side ID. By implementing the INamingContainer interface, the custom control is identified as a container for the child controls it encapsulates. It also ensures that all the sub-controls are named similarly in the HTML output. This will be important for referencing the controls in client-side code.

 

Imports System.ComponentModel

Imports System.Web.UI

Imports System.Web.UI.WebControls

 

  runat=server>")> _

Public Class ComboBox

   Inherits System.Web.UI.Control

   Implements INamingContainer

  ' Declare private variables (and child controls) here.

   Private m_txt As New TextBox()

   Private m_btn As New Label()

   Private m_ddl As New ListBox()

   Private m_pnl As New Panel()

  ' Define public properties.

   Public Property [Text]() As String

     Get

       Return m_txt.Text

     End Get

     Set(ByVal Value As String)

      m_txt.Text = Value

     End Set

   End Property

  ' (Download the code to see all properties).

   Protected Overrides Sub CreateChildControls()

    ' Set the properties of your child controls here.

    '

   End Sub

End Class

Figure 3: The basic structure of a composite control is quite simple.

 

Some private variables are then defined. In this case, the TextBox, Label, ListBox, and Panel controls described earlier are declared. Then the public properties are defined. The beauty of composite controls is that, in many cases, these properties can be piped through to the appropriate child control, thereby escaping the complexity of (and potential bugs associated with) handling these properties manually. The child controls also handle their own viewstate, which saves the effort of having to store the properties between postbacks.

 

Download the full source code for this control to see that the Text and MaxLength properties are sent to the underlying textbox control, border-related properties are delegated to the Panel control, and data-binding properties are piped-through to the ListBox control (see end of article for download details).

 

The full code for the CreateChildControls method of the ComboBox control is listed in Figure 4. The first code block dynamically builds client-side script that is equivalent to the listing in Figure 2. This script will be assigned to some client-side events. The StringBuilder class is the most efficient way to concatenate strings.

 

Protected Overrides Sub CreateChildControls()

  ' Client-side script: toggle visibility of dropdown list.

   Dim jscript As New Text.StringBuilder()

  jscript.Append("if (")

  jscript.Append(Me.ID & _

    "_DDL.style.visibility=='visible')")

  jscript.Append("{" & Me.ID & _

    "_DDL.style.visibility='hidden';}")

  jscript.Append("else{" & Me.ID & _

    "_DDL.style.visibility='visible';}")

  ' Configure the textbox.

  m_txt.Attributes.Add("AUTOCOMPLETE", "off")

  m_pnl.Controls.Add(m_txt)

   AddHandler m_txt.TextChanged, _

     AddressOf Me.OnTextChanged

  m_txt.ID = "txt"

  ' Configure the arrow button (which is really a label).

  m_btn.Font.Name = "webdings"

  m_btn.Text = Chr(54)

  m_btn.BorderStyle = BorderStyle.Outset

  m_btn.Attributes.Add("OnClick", jscript.ToString)

  m_pnl.Controls.Add(m_btn)

  m_pnl.Controls.Add(New LiteralControl("
"))

  ' Configure the dropdownlist.

  m_ddl.ID = "DDL"

  m_ddl.Style.Add("visibility", "hidden")

  m_ddl.Attributes.Add("onclick", Me.ID + "_txt.value=" _

    + Me.ID + "_DDL.value;" + jscript.ToString)

  m_pnl.Controls.Add(m_ddl)

  Controls.Add(m_pnl)   ' Add the panel to the control tree.

End Sub

Figure 4: Composite controls encapsulate the functionality of two or more child controls. These child controls are configured in the CreateChildControls method inherited from the base Control class. By taking advantage of this technique, rich controls can be made quickly by farming out tedious chores to existing controls.

 

The next code block configures the textbox portion of the control. The first line turns off AutoComplete, a normally nifty feature of Internet Explorer that will only get in the way of the custom dropdown functionality. The TextBox is then added to the Controls collection of the Panel control that keeps the child controls nicely organized together. A server-side event is then associated with the TextBox control. It will be raised whenever the user changes the text of the control. (More on this later.)

 

The final line of the TextBox code block specifies that the client-side ID of the control will be named txt. Because this control lies within a naming container, the actual client-side ID of the control will be the ID of the TextBox control (txt) combined with the ID of the parent ComboBox control. In most cases, this will result in the client-side ID of the TextBox being ComboBox1_DDL. Of course, if there are two ComboBox controls on the form, the second one will have an ID of ComboBox2_DDL. As seen further down in the code, syntax like this can be used to determine the actual client-side ID of the control at run time:

 

Me.ID + "_" + m_txt.ID

 

The next code block configures the little arrow that sits to the right of the TextBox control. ASCII character number 54 of the Webdings font will work well for displaying the arrow. A border style is also set to make it look more like a little button, even though it's actually a Label control. (A button Web control could have been used instead, but these cause postbacks that are undesirable in this case.) Then the JavaScript that was defined in the first code block is assigned to the client-side OnClick event of the label. This will cause the visibility of the dropdown list to toggle whenever the arrow is clicked. Finally, this control is also added to the Controls collection of the Panel control, and a line break
is added to make sure the following ListBox control appears beneath the TextBox. The client-side ID isn't specified, because it doesn't need to be referenced from any client-side code; it doesn't really matter which ID ASP.NET decides to give the control.

 

The final code block configures the ListBox control, which will appear and disappear on command, simulating a dropdown list. The ID is set for the ListBox, and the visibility is initially set to hidden. A client-side OnClick event attribute is then specified. When a user clicks on an item in the ListBox, client-side code will copy the text of the clicked item into the TextBox, and the visibility of the dropdown is then toggled to hidden using the JavaScript defined in the first code block.

 

The only bit left to discuss is the TextChanged event. This event will be raised whenever the user types in a new value or selects one from the dropdown list. Such an event must be publicly declared so it can be referenced from the code-behind of Web forms that use the control:

 

Public Event TextChanged As EventHandler

 

The event can then be raised by the control's code when appropriate:

 

Protected Overridable Sub OnTextChanged( _

   ByVal source As Object, ByVal e As EventArgs)

 

   RaiseEvent TextChanged(Me, e)

End Sub

 

The CreateChildControls method defined that this sub should be called in response to the TextChanged event of the child TextBox control. The event is then bubbled up to the page that is hosting the ComboBox control.

 

Fire It Up

When all the previous code has been put into a Web control library project, it can be compiled into a DLL and added to the toolbox. Then it can be dropped onto a Web form in any ASP.NET Web application and used just like any other control in the toolbox. With a simple code-behind like that shown in Figure 5, output similar to that shown in Figure 6 can be achieved.

 

Private Sub Page_Load(ByVal sender As System.Object, _

   ByVal e As System.EventArgs) Handles MyBase.Load

 

  ComboBox1.MaxLength = 30

  ComboBox1.Width = Unit.Pixel(150)

  ComboBox1.DataSource = MyDataSource()

  ComboBox1.DataBind()

End Sub

 

Private Sub ComboBox1_TextChanged(ByVal sender As Object, _

   ByVal e As System.EventArgs) _

   Handles ComboBox1.TextChanged

 

  labelResult.Text = "You chose " & ComboBox1.Text

End Sub

Figure 5: The ComboBox is simple and intuitive to use from any Web form.

 


Figure 6: Output from running the code shown in Figure 5.

 

Conclusion

At this point you have a reasonably professional ComboBox control in your hands. You should also have a good understanding of composition and composite controls. You should be able to create nearly any custom Web server control that you can imagine by experimenting with the techniques outlined in this article. You can glue sets of controls together like Legos and create a consistent look and behavior throughout your Web site, no matter how large and complex it may be.

 

The sample code accompanying this article is available for download.

 

Steve C. Orr is an MCSD and a Microsoft MVP in ASP.NET. He's an independent consultant who develops software solutions for many leading companies in the Seattle area. When he's not busy designing software systems or writing about it, 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://Steve.Orr.net or e-mail him at mailto:[email protected].

 

Want More?

If you have the need for a ComboBox control such as this in your Web applications, I suggest you go to http://www.metabuilders.com and check out the free ComboBox control created by cohort Andy Smith. It builds on many of the techniques outlined in this article and takes them to the next level. Clearly he's worked hard on it, so if you find it useful consider sending a donation his way. There's also some intriguing C# ComboBox code available at http://www.codeproject.com/aspnet/combobox.asp.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish