Download as pdf or txt
Download as pdf or txt
You are on page 1of 7

The World Has Changed: Life Without

Control Arrays in Visual Basic .NET


Deborah Kurata
InStep Technologies, Inc
July 11, 2003
Summary: Control arrays were the best way to manage the controls on your Visual Basic
forms, but there are no control arrays in Visual Basic .NET. Deborah Kurata describes
how to use new Visual Basic .NET features to obtain control array functionality without
the need for a control array. (8 printed pages)
Applies to:
Microsoft Visual Basic® .NET 2003

Introduction
"I amar prestar aen-the world has changed." You may recognize this as the first words
from the movie The Lord of the Rings: The Fellowship of the Ring. These words are just
as true in the world of Visual Basic development as they were in Middle Earth. Our
world has changed. This is the first in a series of articles that describe the fundamental
changes in Microsoft Visual Basic® and how to do today with Visual Basic .NET what
you used to do with earlier versions of Visual Basic.
The original versions of Visual Basic provided control arrays for managing the controls
on your forms. Control arrays had several benefits. They allowed you to share event
procedures for a set of controls. For example, one GotFocus event procedure could
handle the focus event for all of the text boxes on your forms. Control arrays provided a
mechanism for iterating through a set of controls and for adding controls at runtime. And
if you were concerned about system resources, using a control array counted as one
control, regardless of the number of controls in the control array.
There were limits to control arrays as well. You could only put controls of the same type
in a control array. If one of your input boxes was a masked edit control, it could not be in
the same control array as your text boxes.
Control arrays were not consistent in the language. They were not quite collections and
not quite arrays. This is why they are not provided in Visual Basic .NET. Instead, Visual
Basic .NET has a rich set of features that provide all of the benefits of control arrays
without the limitations.
Note Actually, Visual Basic .NET does support control arrays through the
Microsoft Visual Basic .NET Compatibility library. This library allows you to
retain some of the Visual Basic 6.0 features in Visual Basic .NET to simplify the
migration process. Features of this library should be used only for migration.

Sharing Event Procedures


Control arrays in prior versions of Visual Basic allowed you to define one set of event
procedures for all of the controls in the control array. For example, you could use a
control array for all of the text boxes on your form. In the single GotFocus event
procedure for the control array, you could change the background color of the text box.
When any of the text boxes in the control array got focus, the background color would
change. This minimized the amount of code you needed to write and ensured that the
controls in your control array had consistent behavior.
How do you get that same functionality in Visual Basic .NET? The answer is in the new
.NET event procedures, which are now called event handlers.
The event handler syntax in Visual Basic .NET makes it easier for a set of controls to
share event handlers without the need for control arrays. An event handler looks like the
following:
Private Sub txtName_Enter(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txtName.Enter
End Sub
This event handler manages the Enter event for the text box named txtName. Notice that
the generated name of the event handler is similar to the name given to event procedures
in Visual Basic 6.0. However, in Visual Basic 6.0, the name of the event procedure
defined the control and event that the procedure handled. In .NET, the event handler
name has no intrinsic meaning. The above event handler could be rewritten as:
Private Sub ProcessEnter(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txtName.Enter
End Sub
The .NET event handler uses the Handles keyword to define which event the handler
will manage. The ProcessEnter event handler will automatically handle the Enter event
for the textbox named txtName.
You can share an event handler by simply adding another event to the Handles clause like
this:
Private Sub ProcessEnter(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txtName.Enter, txtAddress.Enter
End Sub
The ProcessEnter event handler now handles the Enter events for both the txtName and
txtAddress textboxes.
With the Handles clause, .NET takes event sharing to a new level because it allows
sharing of event handlers for controls of different types and for different events (as long
as the event handler signature is the same). This allows you to use one event handler for
all of your text boxes, masked input controls, and any other .NET or third-party control.
Let’s take a quick look at the other parameters of the event handler. The first parameter
defines the object that generated the event:
ByVal sender As Object
This returns a generic sender object so that the same event handler can be used for many
different types of controls. To access the properties or methods of the sender object, you
need to cast (or convert) the sender variable from the generic object type to a specific
control type. You can do this with the DirectCast function:
Private Sub ProcessEnter(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txtName.Enter, txtAddress.Enter
DirectCast(sender, TextBox).BackColor = Color.Wheat
End Sub
If your code needs to perform differently based on the control that generated the event,
you can look at the Name property of the sender object. For example:
Private Sub ProcessEnter(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txtName.Enter, txtAddress.Enter
If DirectCast(sender, TextBox).Name = "txtName" Then
' Use one color for required fields
DirectCast(sender, TextBox).BackColor = Color.Wheat
Else
' Use a different color for optional fields
DirectCast(sender, TextBox).BackColor = Color.LightYellow
End If
End Sub
The second parameter of the event handler is the set of event arguments:
ByVal e As System.EventArgs
This parameter is used by events that allow you to access event arguments. For example,
the event arguments for the Closing event allow you to cancel the event and stop the
closing. This is useful if you want to prevent the user from exiting a form without saving.

Iterating Through Controls


Control arrays also made it easy to perform operations on a set of controls. For example,
you could clear the text for all of the controls in a control array by simply looping
through the control array.
In .NET, controls are automatically added to a Controls collection. However, this
collection is hierarchical, which makes it a little more difficult to work with. The first
level of the Controls collection hierarchy includes only the controls directly on the form.
The second level of the hierarchy includes the controls that are contained in any of the
controls that are directly on the form.
This is easier to understand with an example. Say you have a form with two panels as
shown in Figure 1.

Figure 1. Visual Basic .NET form with two panels


In pnlSelection you have a combo box that allows the user to select an entry. In
pnlInformation you have a tab control. The tab control contains two tab pages, and on
each tab page you have sets of controls. If you looked at the Controls collection for the
form you would see that the collection only contains two controls:
• pnlSelection
• pnlInformation
These are the only two controls in this example that are directly on the form.
To access the controls contained in pnlSelection, you have to access the Controls
collection for pnlSelection. To access the controls on the tab control on pnlInformation,
you have to access the Controls collection for pnlInformation (which contains the tab
control), the Controls collection for the tab control (which contains the tab pages), and
then the Controls collection of each of the tab pages (which contains the text boxes).
To write a function that accesses all of the controls on the form in a generic fashion, you
need to write a function that is recursive, meaning that it calls itself. The function iterates
through the Controls collection and, if it finds a control in the Controls collection that
contains other controls (referred to as child controls), the function will call itself to iterate
through the control’s Controls collection.
Private Sub ClearForm(ByVal ctrlParent As Control)
Dim ctrl As Control
For Each ctrl In ctrlParent.Controls
If TypeOf ctrl Is TextBox Then
ctrl.Text = ""
End If
' If the control has children,
' recursively call this function
If ctrl.HasChildren Then
ClearForm(ctrl)
End If
Next
End Sub
This routine clears all of the text box fields of the form, even if the text box is contained
within another control.

Adding Controls at Runtime


Control arrays provided an easy way to add controls at runtime. You could create the
control array at design time and then add controls of the same type to the array at
runtime. Again, this was limited to adding controls of the same type.
In Visual Basic .NET, you can use the Controls collection to add controls of any type at
runtime. You could, for example, add a text box and a label to the Contact Information
tab of the form as shown in Figure 2.
Figure 2. Adding an Email label and associated control at runtime
The following routine adds the Email label and associated text box at run time.
Private Sub AddEmailAddress()
Dim txtEmail As New TextBox
Dim lblEmail As New Label
' Set the desired properties
txtEmail.Top = txtAddress.Top + txtAddress.Height + 10
txtEmail.Left = txtAddress.Left
lblEmail.Text = "Email"
lblEmail.Location = New Point(lblAddress.Location.X, _
txtEmail.Location.Y)
' Add to the collection
tpAddress.Controls.Add(txtEmail)
tpAddress.Controls.Add(lblEmail)
End Sub
This code first creates instances of the TextBox and Label controls by using the New
keyword on the Dim statement. Any desired properties of the controls are then set. Notice
that the location of a control can be set using the Top and Left properties of the control,
as with the txtEmail TextBox in the example code. This is similar to setting the location
of controls in Visual Basic 6.0. Alternatively, you can set the location of the control by
creating a new point on the form and assigning that point to the Location property of the
control. This is shown in the example code with the lblEmail label control.
Finally, the new controls are added to the Controls collection for the tab page. This is
needed to ensure the controls appear on the tab and not directly on the form. If the
controls do not need to be on a tab, panel, or other container, you can use the Controls
collection of the form instead.

Adding Event Handlers at Runtime


If you added a control to a control array at runtime, the control’s events were
automatically handled by the events for the control array. With Visual Basic .NET, you
need to define the event handler for the control.
You can define the event handler for a control at runtime by using the AddHandler
statement.
' Add the event handler
AddHandler txtEmail.Enter, AddressOf ProcessEnter
The first parameter of the AddHandler statement defines the event to be handled; in this
case the Enter event of txtEmail. The second parameter defines the address of the event
handler that will handle the event. In this case, the ProcessEnter event handler described
earlier in this article will be reused.
The AddHandler statement can be used to wire up event handlers for controls added to
the form at design time or at runtime. You can later remove event handlers for a
particular control event using the RemoveHandler statement.
You can use AddHandler to write generic code that automatically connects up events for
your controls. For example:
Private Sub AddEvents(ByVal ctrlParent As Control)
Dim ctrl As Control
For Each ctrl In ctrlParent.Controls
If TypeOf ctrl Is TextBox Then
AddHandler ctrl.Leave, AddressOf ProcessLeave
End If
' If the control has children,
' recursively call this function
If ctrl.HasChildren Then
AddEvents(ctrl)
End If
Next
End Sub
This example is similar to the code that cleared the form, but this code adds an event
handler for every TextBox on the form. The ProcessLeave event handler referenced in
the above example is as follows:
Private Sub ProcessLeave(ByVal sender As Object, _
ByVal e As System.EventArgs)
DirectCast(sender, TextBox).BackColor = _
Color.FromKnownColor(KnownColor.Window)
End Sub
This event handler has no Handles clause because the events are wired up using the
AddHandler statement. The AddHandler statement defines that this ProcessLeave
event handler should be used whenever the Leave event is generated for a TextBox on
the form.
The result is that when the user enters a text box, the background color is changed to a
wheat color (light tan) allowing the user to easily see which control has focus. When the
user leaves the text box, the background color is changed back to the user’s default
window color.
Using the AddHandler statement in generic code such as this example simplifies
maintenance and improves your productivity because, as text boxes are added to the form
over time, they will automatically have all of the appropriate event behavior.

Conclusion
The world of Visual Basic development has changed, but it has only gotten better. The
hard part is learning all of the differences and how best to take advantage of the many
new features.
Control arrays are gone, but with the new event handlers and controls collection, we can
achieve the same functionality with less code, easier maintenance, and no limitations.

You might also like