Building applications with objects differs significantly from the structured programming techniques that you have probably already mastered. Creating a library of object requires planning and design work to determine how the objects relate to each other.
In this chapter, you learn how to do the following:
The two basic ways to organize the objects that an application provides are the following:
Microsoft Word and Microsoft Excel are two extreme examples of both object library organizations. The broad Word object library provides one creatable object, WordBasic, and hundreds of properties and methods. The deep Excel object library provides three creatable objects and almost a hundred subordinate objects. Figures 20.1 and 20.2 illustrates the two approaches to designing object libraries.
The Word object library is so broad that it is flat.
The Excel object library is as deep as an ocean.
Offhand, broad organizations like Word's might seem poorly planned. In fact, Word's object library is what Microsoft calls "opportunistic." Microsoft added the library to Word 6.0 near the end of the development cycle by using the OLE 2.0 CreateDispatch helper functions.
In practice, however, the Word object library is quite useful and understandable. This is because the Word object library accurately reproduces the WordBasic language, which itself closely matches the Word user interface. WordBasic has the advantage of an existing user base and some familiarity among Visual Basic programmers. It is already BASIC-like and thus fits well in Visual Basic code.
To create WordBasic code, you use the Word menu, dialog box, and field names. If you want to open a Word document by using Word's object library, you use the FileOpen method; to change the style of a paragraph, you use the FormatStyle method; and so on. These names are obvious to users, because users see the names every day in the user interface, and the WordBasic programming documentation has already documented the names.
Broad organizations can accurately reproduce an existing interface, which enables you to create objects quickly. Also, broad organizations are simple and thus easy to understand.
The disadvantage of linking an object library to names in the user interface is that you must change your object library when you change names in your user interface. Word has already faced this problem with two version changes of WordBasic.
When updating Word 1.0 to Word 2.0, Microsoft added a macro translation utility that converted Word 1.0 macros to Word 2.0 macros. However, this utility didn't work well within the WordBasic programming environment, and would not work at all now that other programming environments, such as Visual Basic and Visual C++, can use WordBasic code.
To accommodate changes from Word 2.0, Microsoft Word 6.0 includes support for all the old Word 2.0 language. Therefore, two programmers can be doing the same thing in the same environment while using completely different languages—which is not a good idea.
Another disadvantage of the Word object library is that all methods and properties operate on the currently active selection or document rather than an actual object. Unfortunately, the selection can easily change because of user actions. To avoid this problem, you can use objects, because the object variable to which you apply methods and properties always points to the same object. Figure 20.3 shows how WordBasic methods act on the currently active document rather than a document object.
Changing the active document affects code written with the Word object library.
Changing the active document affects code written with the Word object library.
Summary
The following lists summarize the features, advantages, and disadvantages of a broad organization. The features include the following:
The advantages of a broad organization include the following:
Disadvantages of a broad organization include the following:
Deep object libraries, such as Excel's, require much more planning than broad organizations. A team of product managers at Microsoft wrote hundreds of pages of specifications before Microsoft wrote any code to implement the Excel objects. By the end of the process, the team used an Access database to keep the specifications up to date.
Excel is about as complex as the human brain. Excel's full potential, like that of the human brain, is seldom tapped; 90 percent of Excel's users never use more than 10 percent of the program's capabilities. Still, the Excel object library manages to include every feature and capability of Excel, and makes them all available from Visual Basic.
In all, the Excel object library defines about 4,200 items, including 1,943 properties, 1,385 methods, 756 constants, and 130 objects. This is much too large a chunk of knowledge for anyone to swallow whole, so Excel's object hierarchy breaks it down into smaller bites. The 130 objects include both individual objects and their collections. If you count objects and collections together, you have only 80 unique items. If you learn the purposes of these 80 objects, you can use the Excel object library effectively.
For example, performing calculations on a range of cells in Excel involves only four objects: Application, Workbook, Worksheet, and Range. Understanding this, you can focus on the relatively smaller number of properties and methods that those objects provide. Deep organizations of objects break the complexity of large object libraries into manageable pieces.
However, deep organizations can get too deep. Some Excel objects are nine levels down in the hierarchy. For example, the following code line changes the marker in the legend of a chart embedded on an Excel worksheet:
The preceding line requires nine object references to navigate down to the object to act on. Excel ameliorates this problem slightly by providing Application object properties such as ActiveWorkbook, ActiveSheet, and Selection, which act as shortcuts through the object hierarchy.
Another disadvantage to deep organizations of objects is that users might have trouble finding the object that they want to use for a specific task. You can address this problem by documenting your object library adequately; you not only must define what your objects are, you also must tell users how to use them.
Making objects easy to find and understand is not much of a problem for broad organizations, in which the method and property names mirror the user interface. Because of this mirroring, users can simply mimic their user-interface actions in code.
Summary
The following lists summarize the features, advantages, and disadvantages of a deep organization. The features include the following:
Here are the advantages of a deep organization:
Disadvantages of a deep organization include the following:
You must amortize the time that you spend planning the organization of your objects against the lifetime of your application. Creating a broad organization is faster and requires less planning, but misses out on the advantages of a well-designed deep organization.
Use broad organizations when implementing OLE Automation objects on top of an existing, widely installed application that is stable. Don't use a broad organization for an application that you often revise, particularly if you expect the user interface to change.
Use deep organizations of objects for new applications that you are building from the ground up, particularly if they are complex applications that you are building with object-oriented programming techniques.
Combine the two approaches when you want to compromise between effort and value. Not all features offer equal value to your users, so you should concentrate your design effort on the most-used features in your application. Don't use this as an excuse to be messy or inconsistent; just know where the value of your application lies.
In an object hierarchy, superordinate objects provide methods that return subordinate objects. This enables users to create a top-level object, then navigate down to the object that they need, as shown in figure 20.4.
Each object provides methods that return the next object down in the hierarchy.
If an object supports multiple instances of a subordinate object, the method returns a collection object. In figure 20.4, the Excel Application object supports multiple open files, represented by the Workbooks collection.
Not all subordinate objects have multiple instances. In Excel, charts can have only one legend, so the Chart object's Legend method returns a singular Legend object.
Subordinate objects should provide a way to navigate back up the object hierarchy. Microsoft decrees that all objects should have a Parent property that returns the next object up in the hierarchy, and an Application property that returns the top-level object in the hierarchy. Figure 20.5 shows how you use the Application and Parent property to navigate up an object hierarchy.
The Application property jumps to the top of the hierarchy; Parent moves up one step.
The Application and Parent properties skip over collections. For instance, Pane.Parent returns the Window object, not the Panes collection. Similarly, the Panes.Parent returns the Window object.
Because a single object can belong to many different collections, Visual Basic provides no standard method for returning an object's collection. You usually can get the object's collection from its parent object. The following code shows how to get a Worksheets collection from a Worksheet object in Excel:
The preceding code requires that you know that a Worksheets method for the Worksheet object's parent exists. Parent objects usually include a method for the child object's collection, but you should check the object library's documentation to make sure.
Applications usually expose only a few objects that CreateObject or GetObject can create. This limits the number of external entry points into the application and ensures that objects are correctly initialized before access.
Navigating downward through objects enables users to access the objects that are subordinate to those objects that CreateObject or GetObject can create. The methods that provide this downward mobility can create the object, initialize data, or lock access to data depending on the object's needs. In other words, navigation controls the flow through your application's objects.
Upward navigation is important to procedures that receive objects. You often have to know not only the type of an object, but which instance of another object owns it. Parent and Application properties help identify objects wherever they may roam.
Used together, upward and downward navigation enables you to move between any two objects in an object library, as shown in figure 20.6.
Using navigation to move around in an object library.
The method name and the name of the object that the method returns should be the same. This blurs the distinction between the method and the object, but makes code easier to read and understand. The following code uses the Application object's Windows method to display the number of Window objects in an application:
MsgBox Application.Windows.Count
You could just as easily define the Application object's Windows method as a read-only property. In this case, choosing between a method and a property is a matter of taste.
The following code shows the definition for the Application object's Windows method. The variable that stores the application-wide Windows collection is defined in a code module so that other applications cannot modify the variable's setting.
You also should provide Application object properties to return currently active objects in the application. This provides a shortcut to the object that has focus. Active object properties should be read-only. If you want to control focus from outside an application, provide an Activate method for the object.
The following code shows the definition for the Application object's ActiveWindow property. Because a code module defines the variable that stores the Window object that has focus, other applications cannot modify the variable's setting.
The following code shows the definition for the Window object's Activate method. The method sets the focus of a form within the application, and the form's GotFocus event procedure updates the internal variable (gActiveWindow) that stores the currently active Window object.
All objects in the hierarchy should define Application and Parent properties. Microsoft states this as a rule in the OLE Programmer's Reference, Volume 2 (Microsoft Press). The Application property returns the top-level object in the object library's hierarchy. The Parent property returns the object that "owns" the current object. Both properties are read-only.
The Application and Parent properties for the Application object return the Application object itself. The following code defines the Application and Parent properties for an Application object:
The following code shows the definition for the Window object's Parent property. Because a code module defines the variable that stores the Application object, other applications cannot modify the variable's setting.
The Parent property of each object reflects the hierarchy of objects in the library. For each object, the property should return the object that created the object or otherwise owns control of the object. For objects that only one type of object can create, the object to return is easy to determine. For objects that many other objects can create, determining the object that Parent should return is more difficult.
Listing 20.1 shows how an object that many other objects can create and use maintains the Parent property. The DialogBox object's Create method displays the DialogBox object and registers the object that created the object as the newly created object's parent by using a Private variable, miParent. The object's Parent property returns a reference to that object.
Listing 20.1 Use a Private Object Variable to Keep Track of an Object's Parent
The Parent property of an object should stay within the bounds of the application that provides the object. In other words, if another application uses CreateObject to create an object, that object's Parent is not the calling application. The Parent and Application properties are intended for navigation within a single object library.
Collections organize multiple instances of objects within your application. Collections can be homogeneous—made up of objects of the same type—or heterogeneous—made up of objects of different types.
Homogeneous collections help you control multiple instances of objects that the application creates. For instance, an application that supports multiple open files might want to provide access to those files through a Documents collection. Each time that the user opens a new file, the application adds to the Documents collection the object that controls that file. When the application closes, it can check each document to see whether the application needs to save it, by using code similar to the following:
Without a container for all the Document objects, you cannot easily check which files need to be saved.
Heterogeneous collections provide a way to create multiple selections and to group diverse objects as a single unit for general operations, such as moving or sizing. For example, a Document object might provide a Selection method that returns a collection of all the objects currently selected in the document. The objects that the user can select provide the Selected property to add them to the collection.
A Select method might seem like a more natural way to add objects to a Selection collection, but Visual Basic reserves the keyword Select, so you cannot use it as a method name.
Listing 20.2 shows the Selected property that selects an object. This read/write property should exist for each object that the user can select. A code module, modDeclares, stores the actual collection, so outside applications cannot change the property's setting directly.
Listing 20.2 The Selected Property Should Exist for Each Object That the User Can Select
The Document object provides a Selection method that returns the collection of objects stored in modDeclares.Selection. This provides a way to get the objects that the user selects, but prevents direct changes to the collection. The following is the Selection method:
Like all symbols in your code, objects, properties, and methods should have names that clearly describe what they are or what they do. Objects, properties, and methods that are visible outside your application should have English-like, readable names. Therefore, OLE Automation objects might not follow the same naming conventions that you use for other symbols in your application. Table 20.1 shows the names in an application that appear to other applications.
Table 20.1 Application Names and Where They Appear
Item | Location of Appearance |
---|---|
Project name | First part of the programmatic ID in the system registration database, and the first part of the Libraries/Projects name in the Object Browser |
Project description | The library description in the References dialog box and the second part of the Libraries/Projects name in the Object Browser |
Class module name for system-creatable objects | Second part of the programmatic ID in the registration database |
Class module name for Public objects | Name in the Classes/Modules list of the Object Browser |
Class module name for Private objects | Nowhere outside the application |
Form module name | Nowhere outside the application |
Code module name | Nowhere outside the application |
Public procedure and variable names in a Public class module | Methods/Properties names in the Object Browser |
Private procedure and variable names in a Public class module | Nowhere outside the application |
A class modules with a Creatable property set to True has a programmatic ID. Visual Basic creates the programmatic ID by combining of the project name and the class name. The programmatic ID is registered in the system registration database and used in the CreateObject or GetObject function to create the object from another application. For example, the following line of code starts the VB Terminal application and returns a reference to its Application object:
Set objVBTerm = CreateObject("vbterminal.application")
Project names should not include any of the following special characters:
• ~!@#$%^&*()_+`-=<>?/.,
Visual Basic removes these characters when creating the programmatic ID. For example, the project name My_Project becomes MyProject in the registration database.
Project names should not start with a number. Visual Basic appends vb4 when creating class names that begin with a numeric digit. For example, the project name 1234 becomes vb41234 in the registration database.
The application's project name also appears in the Object Browser along with the project description, as shown in figure 20.7.
The application's description appears in the References dialog box, as shown in figure 20.8. Including a description for your application is very important if it provides Public classes; otherwise, the References dialog box simply displays the application name.
Applications that don't have project descriptions appear as "mystery" applications in the References dialog box.
You set project names and descriptions in the Project page of the Options property pages (fig. 20.8).
Choose Tools, Options to display the Options property pages.
A class module is visible from other applications if you set its Public property to True. All the procedures and variables declared as Public in that module are also visible from other applications.
The name of Public class modules appears in the Classes/Modules list of the Object Browser. The Public procedures and variables appear in the Methods/Properties list, as shown in figure 20.09
The Object Browser displays all the Public procedures and variables in a Public class module.
Inside the application that defines them, Public variables don't appear in the Object Browser. Outside the application, however, you can view them in the Object Browser's Methods/Properties list.
The names of Public classes, procedures, and variables should follow the rules listed in table 20.2.
Table 20.2 Class, Procedure, and Variable Naming Rules
Rule | Use | Don't Use |
---|---|---|
Use entire words or syllables whenever possible. | Application Window Document Selection | App Wnd NewDoc SelObjects |
Use mixed case to separate words. | ActiveWindow BasedOn IsDirty | activewindow based_on Is_Dirty |
Use the same word as you use in the application's interface. | Toolbar ProjectOptions SelectAll | ToolsCollection UserPrefs HighlightAll |
Use the plural of the singular object for collection names. | Windows TextBoxes Axis | colWindows TextBoxCollection Axes |
For plurals that are the same as the singular form, append Collection. | SheepCollection SeriesCollection | Sheeps CollectionSeries |
When creating an object library, you must be as consistent as possible with similar object libraries. That is, you must use the same or similar names for like objects, properties, and methods. If you maintain such consistency, you save users from having to relearn names for common objects, properties, and methods.
You do not have to be so slavishly consistent that you can't take advantage of the unique capabilities that your application provides. The guidelines in this section should help you create object libraries that enable users to become quickly familiar with its objects, properties, and methods; the guidelines should not limit what your application can do.
As this chapter previously mentioned, all objects should provide Application and Parent properties. These properties return values of the Object data type, as shown in table 20.3.
Table 20.3 Properties and Methods That All Collections Should Provide
Property | Return Type | Description |
Application | Object | Returns the top-level object in the object hierarchy. (Read-only.) |
Parent | Object | Returns the object that created or owns the object. (Read-only.) |
All collection objects should provide additional properties and methods that enable users to add, remove, and manipulate the objects contained in the collection. Table 20.4 lists the objects that are required for collection objects as well as those that are recommended.
Table 20.4 Properties That All Collections Should Provide
Item | Return Type | Description |
---|---|---|
Required Properties | ||
Count property | Long | Returns the number of objects in the collection. (Read-only.) |
Item method | Object | Returns one object from the collection. Item might require a numeric index or a string argument to identify the object to retrieve from the collection. |
Recommended Properties | ||
Add method | Object | Adds an object to the collection. |
Remove method | None | Removes an item from the collection. Remove might require a numeric index or a string argument to identify the object to remove from the collection. |
According to the OLE 2 Programmer's Reference, Volume 2 (Microsoft Press), applications that create documents should provide the following standard objects:
Microsoft identifies these objects as "standard" because they were the ones that the Microsoft Excel and Microsoft Word product groups could agree on before Microsoft released OLE 2.0. Although these objects might not apply to your application, they are useful to examine for their approach to some common properties and methods, such as Left, Top, and Quit. The following sections describe the properties and methods for these standard objects.
The Application object should have the following properties and methods. Table 20.5 shows items that are required for the Application object as well as those that are recommended.
Table 20.5 Application Object Properties and Methods
Item | Return Type | Description |
---|---|---|
Required Properties | ||
Application property | Object | Returns the Application object. (Read-only.) |
Name property | String | Returns the name of the application, such as Microsoft Excel. (Read-only.) |
Parent property | Object | Returns the Application object. (Read-only.) |
Quit method | None | Exits the application and closes all open documents. |
Recommended Properties | ||
Active Document property | Object | Returns the active document object. In this property name, you should use the name of the document object. For example, ActiveWindow or ActiveWorkbook are appropriate names. (Read-only.) |
Caption property | String | Sets or returns the title of the application window. (Read/write.) |
DefaultFilePath property | String | Sets or returns the default path specification that the application uses for opening files. (Read/write.) |
Documents property | Object | Returns a collection object for the open documents. The name of this property should match the name that you use for your Documents collection. (Read-only.) |
FullName property | String | Returns the file specification for the application, including the path. Example: C:\DRAWDIR\SCRIBBLE.EXE. (Read-only.) |
Height property | Single | Sets or returns the distance between the top and bottom edge of the main application window. (Read/write.) |
Help method | None | Displays online Help for the application. |
Interactive property | Boolean | Sets or returns whether the application accepts actions from the user. (Read/write.) |
Left property | Single | Sets or returns the distance between the left edge of the physical screen and the main application window. (Read/write.) |
Path property | String | Returns the path specification for the application's executable file. Example: C:\DRAWDIR if the executable is C:\DRAWDIR\SCRIBBLE.EXE. (Read-only.) |
Repeat method | None | Repeats the previous action in the user interface. |
StatusBar property | String | Sets or returns the text displayed in the status bar. (Read/write.) |
Top property | Single | Sets or returns the distance between the top edge of the physical screen and main application window. (Read/write.) |
Undo method | None | Reverses the previous action in the user interface. |
Visible property | Boolean | Sets or returns whether the application is visible to the user. (Read/write.) |
Width property | Single | Sets or returns the distance between the left and right edges of the main application window. (Read/write.) |
If your application is document-based, you should provide a Document object. You should use the name Document for this object unless you have used a different name, such as Drawing, within your application. Table 20.6 lists items that are required for Document object as well as those that are recommended.
Table 20.6 Document Object Properties and Methods
Item | Return Type | Description |
---|---|---|
Required Properties | ||
Application property | Object | Returns the Application object. (Read-only.) |
Close method | None | Closes all windows associated with a given document and removes the document from the Documents collection. |
Name property | String | Returns the file name of the document, not including the file's path specification. (Read-only.) |
Parent property | Object | Returns the parent of the Document object. (Read-only.) |
Recommended Properties | ||
Activate method | None | Activates the first window associated with a given document. |
Author property | String | Sets or returns summary information about the document's author. (Read/write.) |
Comments property | String | Sets or returns summary information comments for the document. (Read/write.) |
FullName property | String | Returns the file specification of the document, including the path. (Read/write.) |
Keywords property | String | Sets or returns summary information keywords associated with the document. (Read/write.) |
NewWindow method | None | Creates a new window for the given document. |
Path property | String | Returns the path specification for the document, not including the file name or file name extension. (Read-only.) |
PrintOut method | None | Prints the document. |
PrintPreview method | None | Previews the pages and page breaks of the document. Equivalent to choosing File, Print Preview. |
ReadOnly property | Boolean | Returns True if the file is read-only and False otherwise. (Read-only.) |
RevertToSaved method | None | Reverts to the last saved copy of the documnet, discarding any changes. |
Save method | None | Saves changes to the file specified in the document's FullName property. |
Saved property | Boolean | Returns True if the document has never been saved or changed since its creation. Returns True if the document was saved and has not changed since the last save. Returns False if the document was never saved but was changed since its creation, or if the document was saved but has changed since the last save. (Read-only.) |
SaveAs method | None | Saves changes to a file. Takes one optional argument, filename (VT_BSTR). The filename argument can include a path specification. |
Subject property | String | Sets or returns the summary information about the subject of the document. (Read/write.) |
Title property | String | Sets or returns summary information about the document's title. (Read/write.) |
If your application is document-based, you should provide a Documents collection object. You should use the name Documents for this collection unless that name is inappropriate for your application. Table 20.7 lists the items required for Documents collection as well as those that are recommended.
Table 20.7 Documents Collection Properties and Methods
Item | Return Type | Description |
---|---|---|
Required Properties | ||
Add method | Object | Creates a new document and adds it to the collection. Returns the document that the method created. |
Application property | Object | Returns the Application object. (Read-only.) |
Count property | Long | Returns the number of items in the collection. (Read-only.) |
Item method | Object | Returns a Document object from the collection. Takes an optional argument, index, which can take a String indicating the document's Name property, an Integer indicating the ordered position within the collection, or either (Variant). |
Parent property | Object | Returns the parent of the Documents collection object. (Read-only.) |
Recommended Properties | ||
Close method | None | Closes all documents in the collection. |
Open method | Object | Opens an existing document and adds it to the collection. Returns the document that the method opened or Empty if the method could not open the object. |
Table 20.8 lists the items required for Font object as well as those that are recommended.
Table 20.8 Font Object Properties and Methods
Item | Return Type | Description |
---|---|---|
Required Properties | ||
Application property | Object | Returns the Application object. (Read-only.) |
Name property | String | Returns the name of the font. (Read-only.) |
Parent property | Object | Returns the parent of the Font object. (Read-only.) |
Recommended Properties | ||
Bold property | Boolean | Sets or returns whether the font is boldface. (Read/write.) |
Color property | Long | Sets or returns the RGB color of the font. (Read/write.) |
Italic property | Boolean | Sets or returns whether the font is italic. (Read/write.) |
OutlineFont property | Boolean | Sets or returns whether the font is scaleable. For example, bitmapped fonts are not scaleable, but TrueType fonts are. (Read/write.) |
Shadow property | Boolean | Sets or returns whether the font appears with a shadow. (Read/write.) |
Size property | Single | Sets or returns the size of the font in points. (Read/write.) |
Strikethrough property | Boolean | Sets or returns whether the font appears with a line running through it. (Read/write.) |
Subscript property | Boolean | Sets or returns whether the font is subscripted. (Read/write.) |
Superscript property | Boolean | Sets or returns whether the font is superscripted. (Read/write.) |
For more information on the following topics, see the indicated chapters:
© 1996, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company.