Export (0) Print
Expand All
Around the World with Visual Basic
Asynchronous Method Execution Using Delegates
Building a Progress Bar that Doesn't Progress
Calling All Operators
Create a Graphical Editor Using RichTextBox and GDI+
Creating A Breadcrumb Control
Creating a Five-Star Rating Control
Creating and Managing Secondary Threads
Data Binding Radio Buttons to a List
Deploying Assemblies
Designing With Custom Attributes
Digital Grandma
Doing Async the Easy Way
Extracting Data from .NET Assemblies
Implementing Callbacks with a Multicast Delegate
Naming and Building Assemblies in Visual Basic .NET
Programming Events of the Framework Class Libraries
Programming I/O with Streams in Visual Basic .NET
Reflection in Visual Basic .NET
Remembering User Information in Visual Basic .NET
Advanced Basics: Revisiting Operator Overloading
Scaling Up: The Very Busy Background Compiler
Synchronizing Multiple Windows Forms
Thread Synchronization
Updating the UI from a Secondary Thread
Using Inheritance in the .NET World
Using the ReaderWriterLock Class
Visual Basic: Simplify Common Tasks by Customizing the My Namespace
What's My IP Address?
Windows Forms Controls: Z-order and Copying Collections
Expand Minimize

Life Without Control Arrays in Visual Basic .NET

Visual Studio .NET 2003
 

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

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.

Show:
© 2015 Microsoft