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:
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:
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).
The differences between arrays of data and collection objects.
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.
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.
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:
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:
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.
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.
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.
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:
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:
The following code uses the collection colTextBoxes to clear all the text entered on the form:
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.
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
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):
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
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
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.
The Arrange method and the rest of the WINDOWS.VPJ application are described in detail later in this chapter.
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:
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:
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.
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 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 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
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
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
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 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
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
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
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
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
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 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
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 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
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
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
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 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
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
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
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 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
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:
This method isn�t as obvious as that of Windows.Add, so the first method is preferable.
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
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 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
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:
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.
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.