Jeff Webb, Mike McKelvy, Ronald Martinsen, Taylor Maxwell, Michael Regelski September 1995 Special Edition Using Visual Basic 4 - Chapter 19 1-56529-998-1 Computer Programming computer programming Visual Basic OLE database applications ODBC VB VBA API This book is an all-in-one reference that provides extensive coverage of every topic and technique for creating optimized and customized applications with Visual Basic.

Chapter 19

Creating Object Collections


Object libraries that contain multiple objects organize their objects by using collections. If you use these libraries or intend to create your own, you must understand how collections work and how to use them in your code.

In this chapter, you learn how to do the following:

What Is a Collection?

A collection is a group of objects that is itself a type of object. Visual Basic has many built-in collection objects: the Forms collection and the Controls collection. You can use these collections with the For Each...Next statement to perform actions on all the objects that they contain. For example, the following MinimizeAll procedure minimizes each loaded form in an application:

' Minimizes all loaded forms.
Sub MinimizeAll()
Dim frmElement As Form
' For each loaded form.
For Each frmElement In Forms
' Minimize the form.
frmElement.WindowState = vbMinimized
Next frmElement
End Sub

For Each...Next statements provide a way to repeat an action on each item in a collection. Unlike For...Next, For Each...Next doesn’t use an index or counter variable. Instead, a For Each...Next statement simply gets each object in a collection, one after the other. The collection determines the order in which the statement retrieves the objects. A collection does not store the objects in a fixed order; instead, the order can vary based on the order of the objects’ display or user access. This is the major difference between arrays of data and collections of object (see fig. 19.1).

Fig. 19.1

The differences between arrays of data and collection objects.

Why Aren’t Collections Stored in a Fixed Order?

Objects often have a visual interface and are subject to the whims of users. For instance, a user might unload a form in an application by closing it. This action removes the form from the application’s Forms collection and automatically reorders the collection.

This scenario doesn’t happen with arrays; to add or remove items from an array, you must use the Redim or Redim Preserve statement in the code. The indices of an array are always contiguous, so you cannot, for example, remove the third element in a seven-element array. Of course, you can set the third element to zero, but that element still exists.

Because objects consume much more memory than simple numeric variables, Visual Basic must recover the memory of removed objects. You can have an array of integers with a many unused elements, but a Forms collection with many unused forms would quickly exhaust your system resources.

Why Use a Collection?

Collections solve the following three problems, which most programmers face when working with objects:

The following sections describe each of these aspects of using collections when creating object-oriented applications in Visual Basic.

Standard Collection Properties and Methods

Collections share a common set of properties and methods. Some collections may have additional properties and methods, but all collections have at least the set described in table 19.1.

Table 19.1 Properties and Methods Common to All Collections

Item Use
Count property Finds the number of objects in a collection
Item method Gets single object from a collection
NewEnum property Iterates over the items in a Visual Basic collection from another language, such as C++

You can use Count and Item together to do the same sorts of tasks that you might perform with For...Each Next. The following is the MinimizeAll procedure using For...Next with Count and Item rather than For Each...Next, as shown earlier:

' Minimizes all loaded forms.
Sub MinimizeAll()
Dim iCount As Integer
' For each loaded form.
For iCount = 0 to Forms.Count - 1
' Minimize the form.
Forms.Item(iCount).WindowState = vbMinimized
Next iCount
End Sub

This MinimizeAll procedure and the earlier one do exactly the same thing. If you work on projects developed in Visual Basic 3.0, you probably will see such code as the preceding, because Visual Basic 4.0 introduces the For Each...Next statement.

The Item method is the default method for Visual Basic’s built-in collections. Therefore, you can omit the keyword Item when using the method. The following two lines are equivalent:

Forms.Item(iCount).WindowState = vbMinimized
Forms(iCount).WindowState = vbMinimized

The second version is more common because it is shorter. Be aware that when you see this form, the Item method is in use.

The NewEnum property is private, so you cannot use it directly in Visual Basic code. The property enables Visual Basic’s For Each...Next statement and programmers using other languages to iterate over the elements in collections. Under the OLE 2.0 standard, each collection provides its own function that For Each...Next statements use to iterate over the collection. NewEnum returns a handle to that function in the object.

Methods That Most Collections Provide

In addition to providing the preceding items, collections usually provide two more methods. The methods in table 19.2 are common to most collections.

Table 19.2 Methods Common to Most Collections

Method Use
Add Adds an object to a collection
Remove Deletes an object from a collection

Add and Remove provide programmers with a standard way to create and delete items in a collection. Visual Basic maintains the Visual Basic Forms and Controls collections, therefore you cannot add or remove them. Add and Remove are quite common in object libraries such as those provided by Microsoft Excel and Project.

Collection Inconsistencies

Some inconsistencies that Microsoft introduced in its own products undermine the standardization of collections somewhat. These differences prove that not all great minds think alike:

With both of these inconsistencies, the problem is not that one system is significantly better than the other, but rather that a diverse set of rules is simply more difficult to remember than a consistent one. In your own code, you can choose to be consistent—and should do so.

Creating a New Collection for Grouped Actions

You can create new collections to contain forms, controls, classes, and OLE Automation objects from other applications. Use the Collection object data type when creating a new collection. The following declaration creates a new collection, colSelected:

Dim colSelected As New Collection

Declaring a variable as a Collection object gives you five built-in properties and methods, which are listed in table 19.3.

Table 19.3 Collection Objects’ Built-In Properties and Methods

Item Use
Count property Returns the number of objects in the collection.
NewEnum property (Private) Supports For Each...Next with the collection. You cannot use this property directly.
Add method Adds an object to the collection.
Item method Gets a single object from the collection.
Remove method Deletes an object from the collection.

The following code creates a new collection, colTextBoxes, and adds all the text boxes on a form to the new collection:

Option Explicit
' Create a new collection to contain all the
' text boxes on a form
Dim colTextBoxes As New Collection
Private Sub Form_Initialize()
' Variable used in For Each to get controls.
Dim cntrlItem As Control
' Loop through the controls on the form.
For Each cntrlItem In Me.Controls
' If the control is a text box, add it to the
' collection of text boxes.
If TypeName(cntrlItem) = "TextBox" Then
colTextBoxes.Add cntrlItem
End If
Next cntrlItem
End Sub

The following code uses the collection colTextBoxes to clear all the text entered on the form:

Sub cmdClear_Click()
' Variable used in For Each to get controls.
Dim cntrlItem As Control
' Clear each of the text boxes in the collection.
For Each cntrlItem In colTextBoxes
cntrlItem.Text = ""
Next cntrlItem
End Sub

One problem with the colTextBoxes collection as used in the preceding code is that the collection can contain any type of object, not just text box controls. Using the Text property on each of the collection’s elements, as in the preceding example, isn’t really safe. If another procedure inadvertently adds a command button to the collection, the Text property assignment fails when the For Each loop encounters it.

To solve this problem, you must create a class to contain the collection and check the type of the object before you add it to the collection. This solution is called type-safe programming.

Creating Collections of a Single Object Type

Ideally, a collection should check whether items that you add to it are of the correct type. The Visual Basic Collection object doesn’t provide any built-in feature that does this. To create a type-safe collection, you must create a new class.

Listing 19.1 shows the code in a class module that defines a type-safe collection. The Add method mimics that of the Visual Basic’s Collection object, but adds a step for checking the type of object to add. If the type doesn’t match, Add triggers a “Type Mismatch” error.

Listing 19.1 A Class Module Defining a Type-Safe Collection

' Class TextBoxes -- TXTBOXES.CLS
' A type-safe collection for text box controls.
Option Explicit
' Private collection contains the objects --
' Add, Item, Remove, and Count members control
' access to this collection.
Private colTextBoxes As New Collection
Public Const PROJECTNAME = “TextBoxCollection”
' Modified Add method -- verifies object type
' before adding an object to the collection.
Sub Add(TextBox As Control, Optional Key, Optional Before, _
Optional After)
' If the object is a text box, add it to
' the collection.
If TypeName(TextBox) = "TextBox" Then
colTextBoxes.Add TextBox, Key, Before, After
' Cause a type mismatch error.
Else
Err.Raise 13, PROJECTNAME & "." _
& Typename(Me), “Object is not a text box.”
End If
End Sub

The Remove, Item, and Count members of the TextBoxes class simply delegate to the built-in properties and methods of the private Collection object colTextBoxes (to delegate is to use an object’s built-in functionality and then repackage it in a similar method):

' Standard Remove method.
Sub Remove(Index)
colTextBoxes.Remove Index
End Sub
' Standard Item method.
Function Item(Index) As Object
' Use the Set statement to return an object
' reference. Simple assignment would return
' the default property for the object
Set Item = colTextBoxes.Item(Index)
End Function
' Standard Count property.
Property Get Count() As Integer
Count = colTextBoxes.Count
End Property

Using a Type-Safe Collection

Using a type-safe collection seems to be the same as using any other type of collection. A version of the code in listing 19.2 appeared earlier in this chapter with the Visual Basic Collection object. To use the type-safe collection TextBoxes, you need only modify one word.

Listing 19.2 Using Type-Safe Collection TextBoxes

Option Explicit
' Create a new collection using the
' type-safe TextBoxes collection.
Dim colTextBoxes As New TextBoxes
' <- Changed “Collection” to “TextBoxes”
Private Sub Form_Initialize()
' Variable used in For Each to get controls.
Dim cntrlItem As Control
' Loop through the controls on the form.
For Each cntrlItem In Me.Controls
' If the control is a text box, add it to the
' collection of text boxes.
If TypeName(cntrlItem) = "TextBox" Then
colTextBoxes.Add cntrlItem
End If
Next cntrlItem
End Sub

Now if you try to add an object other than a text box object to the collection, you get a “Type Mismatch” error. To see the result, try entering the following line of code:

colTextBoxes.Add Form1

Reasons to Use Type-Safe Collections

Type-safe collections verify the type of objects that you are adding to a collection. This verification is a good first-line defense against bugs. But type-safe collections also enable you to extend the object by adding properties and methods that the Visual Basic Collection object does not provide.

The following code shows an Arrange method added to the Windows collection in the sample application WINDOWS.VPJ. Arrange is a logical extension of the Windows collection, because the method’s task is common to most multiple-document interface (MDI) applications.

' Arrange all of the child windows in the
' MDI parent.
Sub Arrange(arrangement As Integer)
' Delegate to the MDI window’s Arrange method.
mdiApplication.Arrange arrangement
End Sub

The Arrange method and the rest of the WINDOWS.VPJ application are described in detail later in this chapter.

Limitations of Type-Safe Collections

A big drawback of type-safe collections is that you cannot use them with the For Each...Next statement. Visual Basic and other applications provide a special, private NewEnum property for their collections. The NewEnum property returns a pointer to a function in the collection that supports the For Each...Next statement. You cannot add this capability to a class that you create in Visual Basic.

To repeat actions on items in a type-safe collection, use the For...Next statement rather than For Each...Next. The following code uses the collection colTextBoxes to clear all the text that the For...Next statement enters on the form:

Sub cmdClear_Click()
' Variable used in For Next as counter.
Dim Item As Integer
' Clear each of the text boxes in the collection.
' WARNING: Must start from 1 rather than 0 since this
' is really a Collection object rather than the Controls
' collection (which starts at 0).
For Index = 1 to colTextBoxes.Count
colTextBoxes.Item(Index).Text = ""
Next cntrlItem
End Sub

Another limitation is that you cannot define a default property or method in a Visual Basic class. Therefore, you must always specify the Item method explicitly when working with a type-safe collection. The following lines are not equivalent:

colTextBoxes.Item(iCount).Text = ""
colTextBoxes(iCount).WindowState = "" ' Causes error!

Using Collections to Organize Objects

Object hierarchies are necessary when an application defines many classes that relate to each other. The hierarchy defines an understandable way for users to choose from among the many objects. You use collections to create a hierarchical organization of classes. The Excel object library is a good example of a large class hierarchy, part of which is shown in figure 19.2.

Fig. 19.2

Excel uses collections to form a hierachy of objects.

In figure 19.2, you can use Excel collections to find individual objects. For instance, the following code line makes a cell in a worksheet bold:

Application.Workbooks(“stock.xls”).Sheets(“Portfolio). _

Range(1,1).Font = xlBold

The following table describes the action that each method or property takes in the preceding line of code:

Item Action
Application Returns the top-level object in Excel.
Workbooks The Application object’s Workbooks method returns the collection of all loaded workbooks in Excel.
(“stock.xls”) The default method for the Workbooks collection is the Item method, so “stock.xls” returns the STOCK.XLS Workbook object within the Workbooks collection.
Sheets The Workbook object’s Sheets method returns a collection of all the sheets in a workbook. These sheets include worksheets, dialog sheets, and chart sheets.
(“Portfolio”) Again, the implicit Item method returns a single Sheets object from within the Sheets collection.
Range The Worksheet object’s Range method returns the collection of cells on a worksheet.
(1,1) The Range object’s Item method returns a single cell from the Range collection.
Font = xlBold The Range object’s Font property sets the cell’s font to appear bold.

This example has a few noteworthy aspects:

The rest of this chapter shows you how to use collections to create a class hierarchy in Visual Basic. The example uses simple Window objects, based on forms.

The WINDOWS.VPJ sample defines three classes in its object hierarchy. You can create the top-level Application object from other applications. All classes are available to other applications through OLE Automation, as shown in table 19.4.

Table 19.4 WINDOWS.VPJ Class Module Property Settings

Class Name Creatable Public
Application True True
Windows False True
Window False True

The Application Object

The Application class (APPLICAT.CLS) defines the WINDOWS.VPJ Application object. This class controls an MDI form that contains all the child windows. The Application object has the following methods and properties:

Property/Method Description
ActiveWindow property Returns the Window object within the application that currently has focus. (Read-only.)
Windows method Returns the application’s Windows collection object. The Windows collection consists of all the child windows that the application displays.
Top and Left properties Set or return the application window’s position. These properties repackage the MDI form’s Top and Left properties. (Read/write.)
Height and Width properties Set or return the application window’s dimensions. These properties repackage the MDI form’s Height and Width properties. (Read/write.)

The ActiveWindow Property

The Application object’s ActiveWindow property uses the modDeclares.gActiveWindow variable to return the window that has focus within the application. By placing global variables in a code module, you make them available internally to the application, but not externally to other applications. ActiveWindow is a read-only property, so access to the variable gActiveWindow must be limited to the current application. Listing 19.3 defines the ActiveWindow property in the Application class.

Listing 19.3 Definition for the ActiveWindow Property in the Application Class

' Application ActiveWindow property (read only)
Public Property Get ActiveWindow() As Window
Set ActiveWindow = modDeclares.gActiveWindow
End Property

The Windows Method

The Application object’s Windows method uses the modDeclares.Windows variable to return the application’s Windows collection. You could just as easily define this method as a read-only property. There is no real difference between a read-only property and a method that takes no arguments. Listing 19.4 defines the Windows property in the Application class.

Listing 19.4 Definition of the Windows Property of the Application Class

' Application Windows method.
Public Function Windows() As Object
' Return the Windows variable from the
' modDeclares module.
Set Windows = modDeclares.Windows
End Function

The Height, Width, Top, and Left Properties

This section discusses the Application object’s Height, Width, Top, and Left properties together because they are all so similar. Each of these properties simply repackages one of the MDI form’s properties. To define a read/write property, you need a pair of Property procedures (Let and Get). Listing 19.5 defines the Windows property in the Application class.

Listing 19.5 Definition of the Windows Property in the Application Class

' Application window height property (read/write).
Public Property Let Height(iVal As Integer)
mdiApplication.Height = iVal
End Property
Public Property Get Height() As Integer
Height = mdiApplication.Height
End Property
' Application window width property (read/write).
Public Property Let Width(iVal As Integer)
mdiApplication.Width = iVal
End Property
Public Property Get Width() As Integer
Width = mdiApplication.Width
End Property
' Application window top property (read/write).
Public Property Let Top(iVal As Integer)
mdiApplication.Top = iVal
End Property
Public Property Get Top() As Integer
Top = mdiApplication.Top
End Property
' Application window left property (read/write).
Public Property Let Left(iVal As Integer)
mdiApplication.Left = iVal
End Property
Public Property Get Left() As Integer
Left = mdiApplication.Left
End Property

The Windows Collection

The Windows class (WINDOWS.CLS) defines WINDOWS.VPJ’s Windows collection object. The class maintains a type-safe collection that contains all the child windows. WINDOWS.VPJ creates its own Windows collection. The application could instead use its Forms collection, except that the Visual Basic Forms collection includes the MDI parent form as well as all the child forms. Working around this parent inclusion behavior is less than straightforward.

Creating a special Windows collection and a separate Windows class requires more code than using the built-in Forms collection, but makes the class hierarchy more easy to understand and modify. To compare this technique to using the Forms collection for a similar purpose, see the example WINALT.VPJ.

The Windows collection object in WINDOWS.VPJ has the following methods and properties:

Property/Method Description
Add method Adds a Window object to the collection.
Remove method Removes a Window object from the collection.
Item method Returns a single Window object from the collection by using a numeric or string index.
Count property Returns the number of child windows in the application. (Read-only.)
Arrange method Tiles the child windows within the MDI parent window.

You should include this table’s first four items in every collection that you create. By being consistent, you make it easier for your object library’s users to use your objects correctly.

The Add Method

The Windows object’s Add method checks the type of objects before adding them to the colWindows collection. You declare the collection colWindows as Private at the module level, as you should always use the Add method to add items. Enabling programmers to add items directly by making colWindows a Public variable could result in the collection storing the wrong object types.

The Add method also enables programmers to create an object simply by omitting the first argument. This is a common syntactic practice in the Excel object library. Listing 19.6 defines the Add method in the Windows class.

Listing 19.6 Definition for the Add Method of the Windows Class

' Create a Windows collection
Private colWindows As New Collection
' Windows Add method -- verifies object type
' before adding an object to the collection.
Sub Add(Optional winVal, Optional Key, Optional Before, _
Optional After)
' If no form is specified, create a new form.
If IsMissing(winVal) Then
' Create a new instance of the Window class.
Dim Window As New Window
' Tell the window to create itself and
' add it to the collection.
colWindows.Add _
Window.Create(SOURCE_INTERNAL), _
Window.Index
Exit Sub
End If
' If the object is a form, add it to
' the collection.
If TypeName(winVal) = "Window" Then
' Make sure object has been initialized.
If Len(winVal.Index) Then
colWindows.Add winVal, Key, Before, After
' If it hasn't, use the Create function to create it.
Else
colWindows.Add _
winVal.Create(SOURCE_INTERNAL), _
winVal.Index
End If
' Cause a type mismatch error.
Else
Err.Raise 13, PROJECTNAME & "." _
& Typename(Me), “Object is not a window.”
End If
End Sub

The Remove Method

The Windows object’s Remove method deletes a Window object and removes it from the Windows collection. If you don’t delete the object before removing it from the collection, you lose the reference to the object without recovering the memory that it consumes. In WINDOWS.VPJ, the Window object’s Delete method unloads the form that it displays. Listing 19.7 defines the Remove method in the Windows class.

Listing 19.7 Definition for the Remove Method of the Windows Class

' Standard Remove method.
Sub Remove(Index)
' Delete the form -- SOURCE_INTERNAL argument
' indicates that Delete is used internally, so
' Remove is not called to remove the Window from
' the collection (causing infinite loop).
colWindows.Item(Index).Delete SOURCE_INTERNAL
' Remove the object from the collection.
colWindows.Remove Index
End Sub

The Item Method

The Windows object’s Item method returns a Window object from the Windows collection. Because Item returns an object reference rather than a simple data type, you must use the Set statement when returning the object. Listing 19.8 defines the Item method in the Windows class.

Listing 19.8 Definition of the Item Method in the Windows Class

' Standard Item method.
Function Item(Index) As Object
' Use the Set statement to return an object
' reference. Simple assignment would return
' the default property for the object
Set Item = colWindows.Item(Index)
End Function

The Count Property

The Windows object’s Count property returns the number of Window objects in the Windows collection. Other procedures that must iterate over the collection use Count; you cannot use a For Each...Next statement when iterating over a type-safe collection. Listing 19.9 defines the Count property in the Windows class.

Listing 19.9 Definition of the Count Property in the Windows Class

' Standard Count property.
Property Get Count() As Integer
Count = colWindows.Count
End Property

The Arrange Method

The Windows object’s Arrange method tiles all the Window objects within the Application window. Arrange demonstrates how you can extend a collection object to include methods and properties other than the standard four: Add, Remove, Item, and Count.

Because the MDI form already provides an Arrange method, you can simply delegate to that method. To control access to an aspect of the application that should remain private (such as the MDI form), you should delegate, rather than expose, the MDI form directly. Listing 19.10 defines the Arrange method of the Windows object.

Listing 19.10 Definition of the Arrange Method in the Windows Object

' Arrange all of the child windows in the
' MDI parent.
Sub Arrange(arrangement As Integer)
' Delegate to the MDI window’s Arrange method.
mdiApplication.Arrange arrangement
End Sub

The Window Object

The Window class (WINDOW.CLS) defines Window object in WINDOWS.VPJ. The class creates and controls a child form (frmWindow) object. Objects that are part of a collection have these requirements that you must consider:

The Window object’s methods and properties include the following:

Property/Method Description
Create method Creates and displays a new child form. The Windows collection’s Add method uses Create, but the method is also available to other applications through OLE Automation.
Delete method Unloads a child form. The Windows collection’s Add method uses Delete, but the method is also available to other applications through OLE Automation.
Index property Returns the key value (in WINDOWS.VPJ, the form caption) that identifies the object in the Windows collection. (Read-only.)
Top and Left properties Set or return the position of the child form. These properties repackage the form’s Top and Left properties. (Read/write.)
Height and Width properties Set or return the dimensions of the child form. These properties repackage the form’s Height and Width properties. (Read/write.)

The Create Method

The Window object’s Create method creates a new child form and returns the Window object that controls that form. The Windows collection’s Add method uses Create, which leads to a problem: You can’t assume that the user knows to use the Add method when creating objects, and you can’t hide Create from other applications because it is a member of a Public class. Therefore, you must make Windows.Add and Window.Create essentially interchangable in code.

The Create method in listing 19.11 uses an internal flag, SOURCE_INTERNAL, to determine whether the Windows object’s Add method called Create. If Add did not call the Create method, Create calls Add anyway, to maintain the Windows collection correctly.

If the SOURCE_INTERNAL flag is set, Create assumes that the Add method called it and proceeds to create the new form, set its index, and return the created object. SOURCE_INTERNAL is hidden from other applications because it is defined in a code module rather than a class or form module. Therefore, the flag is like a private key that you can use to unlock behavior that you should restrict from the outside world.

Listing 19.11 defines the Create method in the Window class.

Listing 19.11 Definition of the Create Method in the Window Class

Private Window As New frmWindow
Private mstrIndex As String
Public Function Create(Optional iCode) As Object
If IsMissing(iCode) Then
Windows.Add Me
' If source was within this project, don't
' delegate to Windows.Remove.
ElseIf iCode = SOURCE_INTERNAL Then
' Keep track of the total number of windows
' ever created for Window Index property.
' Can 't use Windows.Count, since deleting
' windows yields a nonunique number.
giWindowsCount = giWindowsCount + 1
mstrIndex = "Window" & giWindowsCount
Window.Caption = mstrIndex
' Register as the active window
Set modDeclares.gActiveWindow = Me
' Return this object as the result of Create.
Set Create = Me
' Otherwise, an invalid argument was used.
Else
' Invalid procedure call error.
Err.Raise 5, PROJECTNAME & "." _
& Typename(Me), “Invalid argument.”
End If
End Function

If you encounter a “Duplicate Definition” error when trying to add an object to a collection, make sure that the key argument is unique within the collection

The Delete Method

The Window object’s Delete method unloads the child form that the Window object controls. Delete, the companion to the Create method, is available for internal use by the Windows collection and for external use by other applications.

The Windows object’s Remove method uses Delete, which uses the SOURCE_INTERNAL internal flag to determine whether the Windows object’s Delete method called it. If the Delete method did not call Remove, Delete simply calls Remove anyway to maintain the Windows collection correctly. If the SOURCE_INTERNAL flag is set, Delete assumes that the Remove method called it and unloads the child form.

Listing 19.12 defines the Delete method in the Window class.

Listing 19.12 Definition of the Delete Method in the Window Class

' Window Delete method, unloads the form.
Public Function Delete(Optional iCode)
' Check source of Delete call -- if omitted,
' delegate to Windows.Remove.
If IsMissing(iCode) Then
Windows.Remove Me
' If source was within this project, don't
' delegate to Windows.Remove.
ElseIf iCode = SOURCE_INTERNAL Then
Unload Window
' Otherwise, an invalid argument was used.
Else
' Invalid procedure call error.
Err.Raise 13, PROJECTNAME & "." _
& Typename(Me), “Invalid argument.”
End If
End Function

The Index Property

The Window object’s Index property returns the key name that you use to store the Window object in the Windows collection. In WINDOWS.VPJ, the key name is the caption that the form displays. You should maintain the Index property internally and make it available for read-only access. If you change the Index property outside of the application, you lose the object in its collection. Listing 19.13 defines the Count property in the Windows class.

Listing 19.13 Definition of the Count Property in the Windows Class

' Windows index property (read only).
Public Property Get Index() As String
Index = mstrIndex
End Property

The Height, Width, Top, and Left Properties

This section discusses the Window object’s Height, Width, Top, and Left properties together because they are all so similar. Each of these properties simply repackages one of the child form’s properties. To define a read/write property, you need a pair of Property procedures (Let and Get). Listing 19.14 defines these properties for the Window class.

Listing 19.14 Definition of the Height, Width, Top, and Left Properties in the Window Class

' Window Height property (read/write).
Public Property Let Height(iVal As Integer)
Window.Height = iVal
End Property
Public Property Get Height() As Integer
Height = Window.Height
End Property
' Window Width property (read/write).
Public Property Let Width(iVal As Integer)
Window.Width = iVal
End Property
Public Property Get Width() As Integer
Width = Window.Width
End Property
' Window Top property (read/write).
Public Property Let Top(iVal As Integer)
Window.Top = iVal
End Property
Public Property Get Top() As Integer
Width = Window.Top
End Property
' Window Left property (read/write).
Public Property Let Left(iVal As Integer)
Window.Left = iVal
End Property
Public Property Get Left() As Integer
Width = Window.Left
End Property

The frmWindow Form

You must ensure that your system of objects reflect user actions correctly, particularly when you are maintaining a collection. The frmWindow form (FRMWIN.FRM) in WINDOWS.VPJ contains two event procedures that affect the objects defined in the application:

User Action Event Procedure Description
Switch focus between windows Form_GotFocus Updates the Application object’s ActiveWindow property to reflect the window that has focus
Close a window by using the form’s Control menu Form_QueryUnload Remove the associated Window object from the Windows collection

The Form_GotFocus Event Procedure

The frmWindow forms’s Form_GotFocus event procedure is triggered when the form receives focus. Because the Application object provides a property that returns the currently active form, you must maintain that property setting from within Form_GotFocus.

The Application object’s ActiveWindow property is read-only externally, and uses the internal variable gActiveWindow to maintain the active Window object internally. Form_GotFocus sets this variable by using its Caption property to retrieve its controlling object from within the Windows collection.

You should return object references for properties with object-related names, such as ActiveWindow. By returning such names, you create a more natural syntax, such as ActiveWindow.Print or ActiveWindow.Delete.

Listing 19.15 defines the Form_GotFocus event procedure in the frmWindow form.

Listing 19.15 Definition of the Form_GotFocus Event Procedure in the frmWindow Form

' Register that this is the active window.
Private Sub Form_GotFocus()
Set modDeclares.gActiveWindow = Windows.Item(Me.Caption)
End Sub

The Form_QueryUnload Event Procedure

The frmWindow form’s Form_QueryUnload event procedure is triggered just before the form unloads, while form properties are still available. Because the Windows collection object contains all the Window objects displayed on screen, you must remove the form’s controlling Window object from its collection when the user closes the form. Listing 19.16 defines the Form_QueryUnload event procedure in the frmWindow form.

Listing 19.16 Definition for the Form_QueryUnload Event Procedure in the frmWindow Form

Private Sub Form_QueryUnload(Cancel As Integer, _
UnloadMode As Integer)
' If the user closes the form manually,
' be sure to remove it from the collection.
If UnloadMode = vbFormControlMenu Then
Windows.Remove Me.Caption
End If
End Sub

The modDeclares Module

The modDeclares module (DECLARES.BAS) in WINDOWS.VPJ contains Public variables that you use throughout the application. The modDeclares module also defines a counter, giWindowsCount, that the Window class uses to create a unique title and key value that you use when storing the Window object in the Windows collection.

Code modules are not available to other applications through OLE Automation, so programmers often use them to declare application-wide internal variables that Public class modules use. Visual Basic provides no other way to share variables between classes without also making them Public to other applications. The variables and the constant that the modDeclares module defines are as follows:

Item Description
Application The instance of the Application class used throughout the application.
Windows The instance of the Windows class used throughout the application.
giWindowsCount A counter that creates unique key names and form captions in the Windows class’ Add method.
gActiveWindow An internal variable that tracks the currently active window. You can set this variable within the application, but you expose it to other applications as the Application object’s read-only ActiveWindow property.
SOURCE_INTERNAL An internal flag that the Windows class’ Add and Remove methods use to call the Window class’ Create and Delete methods. This flag prevents other applications from calling Create or Delete without first calling Add or Remove.

Listing 19.17 shows the modDeclares module’s declarations.

Listing 19.17 The modDeclares Module’s Declarations

' Classes used througout the application.
Public Application As New Application
Public Windows As New Windows
' Unique number to use in Window Index and caption.
Public giWindowsCount As Integer
' Internal variable that tracks active window.
Public gActiveWindow As Window
' Internal flag used when calling the Delete and
' Create Window methods.
Public Const SOURCE_INTERNAL = &h20

The mdiApplication Form

The mdiApplication form (MDIWIN.FRM) in WINDOWS.VPJ contains menu event procedures that demonstrate how to use the application’s objects in code. The mdiApplication form responds to the following user actions:

User Action Event Procedure Description
Choose Window, Add mnuAdd_Click Invokes the Add method on application’s Windows collection
Choose Window, Arrange mnuArrange_Click Invokes the Arrange method on the application’s Windows collection
Choose Window, Delete mnuDelete_Click Retrieves the Application object’s ActiveWindow property, then invokes the Delete method on the returned object

The mnuAdd_Click Event Procedure

The mdiApplication forms’s mnuAdd_Click event procedure adds a window to the application. Listing 19.18 defines the mnuAdd_Click event procedure in the mdiApplication form.

Listing 19.18 Definition of the mnuAdd_Click Event Procedure in the mdiApplication Form

Private Sub mnuAdd_Click()
Windows.Add
End Sub

This procedure could just as easily use the Window object’s Create method, but it must first create a new instance of the object, as follows:

Private Sub mnuAdd_Click()
Dim Window As New Window
Window.Create
End Sub

This method isn’t as obvious as that of Windows.Add, so the first method is preferable.

The mnuArrange_Click Event Procedure

The mdiApplication forms’s mnuArrange_Click event procedure tiles the child windows in the application window. Listing 19.19 defines the mnuArrange_Click event procedure in the mdiApplication form.

Listing 19.19 Definition of the mnuArrange_Click Event Procedure in the mdiApplication Form

Private Sub mnuArrange_Click()
Windows.Arrange
End Sub

This procedure demonstrates the essence of object-oriented programming: Objects know how to do what they are told. You consign the complexities of arranging the windows on screen to the Windows object itself. The object’s users don’t have to worry about the details.

The mnuDelete_Click Event Procedure

The mdiApplication forms’s mnuDelete_Click event procedure deletes the active child window. Listing 19.20 defines the mnuDelete_Click event procedure in the mdiApplication form.

Listing 19.20 Definition of the mnuDelete_Click Event Procedure in the mdiApplication Form

Private Sub mnuDelete_Click()
Application.ActiveWindow.Delete
End Sub

The following code uses the Windows collection’s Remove method to do the same thing. Because Remove takes the Window object’s index as an argument, this code is a little less obvious than the preceding version:

Private Sub mnuDelete_Click()
Windows.Remove Application.ActiveWindow.Index
End Sub

As you can see from the mnuDelete_Click and mnuAdd_Click event procedures, creating objects from the collection object and deleting objects from the individual object are usually more convenient. This practice is exhibited throughout the Excel object library—the collection object’s Add method creates new objects, and the individual object’s Delete method removes them.

From Here...

In this chapter, you learned about object collections and how to manipulate them. For more information on creating and using objects, see the following chapters:


© 1996, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company.