C H A P T E R    1 Microsoft Office 97/Visual Basic Programmer's Guide

Programming Basics


Contents

This chapter introduces you to the fundamentals of the Visual Basic for Applications programming language: how to get to the Visual Basic programming environment and how to write, edit, store, and run code in that environment. This chapter also briefly discusses the control structures, data types, and built­in constants available to Visual Basic programmers.

Note   The information in this chapter applies to the integrated development environment of Visual Basic for Applications in Microsoft Excel 97, Word 97, and PowerPoint 97. For information about writing Visual Basic code in Microsoft Access 97, see Building Applications with Microsoft Access 97, available in Microsoft Access 97 and Microsoft Office 97, Developer Edition. An online version of Building Applications with Microsoft Access 97 is available in the Value Pack on CD­ROM in Microsoft Access 97 and Microsoft Office 97, Professional Edition. For information about writing VBScript code in Microsoft Outlook 97, see Chapter 5, "Microsoft Outlook Objects," and Building Microsoft Outlook 97 Applications by Peter Krebs, available from Microsoft Press (ISBN 1-57231-5736-9).

Writing, Editing, and Running Code in the Visual Basic Editor

Microsoft Excel 97, Word 97, and PowerPoint 97 come equipped with a full­featured development environment called the Visual Basic Editor. Using the Visual Basic Editor, you can create, edit, debug, and run code associated with Microsoft Office documents. To open the Visual Basic Editor, click the Visual Basic Editor button on the Visual Basic toolbar.

A First Look at the Visual Basic Editor

If you're used to writing, editing, and debugging code in a macro­editing window within the Word application window, on an XLM macro sheet, or on a module in a Microsoft Excel workbook, the Visual Basic Editor may seem complex to you the first time you open it, with many windows and buttons you aren't familiar with. This section explains some of these features of the Visual Basic Editor.

For information about a particular window in the Visual Basic Editor, click in the window and then press F1 to open the appropriate Help topic. To see the Help topic for any other element of the Visual Basic Editor, such as a particular toolbar button, search Help for the name of the element.

The Properties Window

A property is a characteristic of an object, such as the object's color or caption. You set a property to specify a characteristic or behavior of an object. For example, you can set the ShowSpellingErrors property of a Word document to True to show spelling errors in the document.

You can use the Properties window to set the properties of an object at design time. The Properties window is very useful when you're working with custom dialog boxes and ActiveX controls. For more information about using the Properties window to set properties of dialog boxes and controls, see Chapter 12, "ActiveX Controls and Dialog Boxes." For most objects, however, it's easier to set these properties at design time by using familiar commands in the user interface. For example, you can set the ShowSpellingErrors property of a Word document to True by selecting the Hide spelling errors in this document check box on the Spelling & Grammar tab in the Options dialog box (Tools menu).

If you don't think you'll be using the Properties window right now, you can close it to simplify your work space a little. You can open it again at any time by clicking Properties Window on the View menu.

The Project Explorer

All the code associated with a workbook, document, template, or presentation is stored in a project that's automatically stored and saved with the workbook, document, template, or presentation. In the Project Explorer of the Visual Basic Editor, you can view, modify, and navigate the projects for every open or referenced workbook, document, template, or presentation. You can resize the Project Explorer and either dock it to or undock it from any of the sides of the Visual Basic Editor window to make it easier to use.

Note   In Word, because the Normal template is available from every Word document, there's always a project for Normal in the Project Explorer.

Within a project, there can be application objects that have events associated with them, custom dialog boxes (called forms in the Project Explorer), standard modules, class modules, and references.

Tip   Folders in the Project Explorer divide project elements into categories. If you don't see any folders, click the Toggle Folders button at the top of the Project Explorer.

In the Project Explorer, there's one project for each open or referenced workbook, document, template, or presentation. In each project, you may find objects (such as Document objects, Workbook objects, and Worksheet objects) that recognize events; forms (also called UserForms), which are custom dialog box interfaces and the code that controls how the user interacts with a particular dialog box; standard modules, which contain code that isn't associated with a particular object or form; class modules, which contain information about a custom object type; and references to other projects. To see the code in a module or the code associated with an object or form, click the element in the Project Explorer, and then click the View Code button at the top of the Project Explorer. To see the user interface for a particular object or form, click the object or form in the Project Explorer, and then click the View Object button at the top of the Project Explorer.

The Code Window

To view the code in a project, go to the Project Explorer, click the element that contains the code, and then click the View Code button at the top of the Project Explorer.

Tip   If you want to be able to see more than one procedure in the code window at a time, select the Default to Full Module View check box on the Editor tab in the Options dialog box (Tools menu). To view just one procedure at a time, clear this check box.

You can navigate the Code window by using the items listed in the Object and Procedure boxes at the top of the window. In the Object box, click (General), and then click a procedure name in the Procedure box to see a procedure that isn't associated with a specific event. In the Object box, click an object, and then click an event in the Procedure box to see the code that runs when a specific event occurs.

Making Room in the Visual Basic Editor

If all you want to do is write a simple procedure or edit a macro you've recorded, you may want to forego some of the advanced features of the Visual Basic Editor in the interest of a simpler workspace. Here are a few ways you can simplify your coding environment:

  • Close the Properties window. If you aren't working with custom dialog boxes or ActiveX controls, the Properties window probably won't be of much use to you. To reopen Properties window, just click Properties Window on the View menu.

  • Hide any toolbars you aren't currently using. To redisplay the Debug, Edit, Standard, or UserForm toolbar, right­click the Visual Basic Editor menu bar, and then click the name of the toolbar you want to display.

  • If you're only working with code in a standard module and you don't need to navigate to other code in the project or to code in other projects, consider closing the Project Explorer. To reopen the Project Explorer, just click Project Explorer on the View menu.

Recording a Macro

You can use the macro recorder to translate user­interface actions into Visual Basic code. Recording a simple macro can give you a jump start on creating a more complex macro, and can help you become familiar with the objects, properties, and methods of an application.

To record a macro

  1. To display the Visual Basic toolbar, point to Toolbars on the View menu in your application window (not in the Visual Basic Editor), and then click Visual Basic if it isn't already selected.

  2. On the Visual Basic toolbar, click the Record Macro button.

  3. In the Record Macro dialog box, replace the default macro name in the Macro name box if you want, and click OK.

    You can use the Store macro in box to choose where your macro will be stored. For now, click This Workbook in Microsoft Excel, All Documents (Normal.dot) in Word, or the name of the active presentation in PowerPoint.

  4. Perform the actions for which you want to generate Visual Basic code.

  5. On the Stop Recording toolbar, click the Stop Recording button.

Your macro has been recorded. To look at the macro code, point to Macro on the Tools menu, and then click Macros. In the Macros dialog box, select the appropriate macro name, and then click Edit.

Getting Around in Your Projects

You use the Project Explorer to navigate to any procedure in any open project. Start by finding the object that contains your macro. Most general procedures, including recorded macros, are stored in a standard module. If you have folders displayed in the Project Explorer, standard modules are located in the Modules folder.

Tip   If you don't see folders in the Project Explorer, click the Toggle Folders button to display them.

After you locate the object that contains your code, double­click the object to view the procedures it contains. You can use this method to get to either procedures you've written from scratch or macros you've recorded.

Where a recorded macro is stored depends on what location you specified in the Store macro in box in the Record Macro dialog box when you recorded your macro. In Microsoft Excel, if you clicked This Workbook in the Store macro in box when you recorded your macro, your macro will be stored in Module1 in the Modules folder of the project for the workbook you recorded the macro in. In Word, if you clicked All documents (Normal.dot) in the Store macro in box when you recorded your macro, your macro will be stored in the NewMacros module in the Modules folder of the Normal project. In PowerPoint, if you clicked the name of the active presentation in the Store macro in box when you recorded your macro, your macro will be stored in Module1 in the Modules folder of the project for the presentation you recorded the macro in.

Tip   If you want to be able to see more than one procedure in the code window at a time, make sure that the Default to Full Module View check box is selected on the Editor tab in the Options dialog box (Tools menu). Otherwise, you have to use the Procedure box in the code window to move from one procedure to another.

Writing a New Procedure

If you want to write code that isn't associated with a specific object or event, you can create a procedure in a standard module in the Visual Basic Editor. A procedure is a unit of code enclosed either between the Sub and End Sub statements or between the Function and End Function statements.

To create a blank standard module, go to the Project Explorer, click anywhere in the project you want to add the module to, and then click Module on the Insert menu.

To open an existing standard module, select the module in the Project Explorer, and then click the View Code button in the Project Explorer.

To add a procedure to a module, select the module in the Project Explorer, click Procedure on the Insert menu, select whatever options you want in the Add Procedure dialog box, and then click OK. For more information about the options in the dialog box, press F1 while the dialog box is displayed. For example, in the dialog box, type Test1 in the Name box, click Sub under Type, click Public under Scope, and then click OK. The procedure that appears in your module should look like the following example.

Public Sub Test1()

End Sub

After you've added a procedure to a module, you can add code to the procedure. The following example adds to the preceding code a line that displays a message box.

Public Sub Test1()
    MsgBox "This is the Test1 procedure running"
End Sub

If you want to write code that runs automatically when a certain event occurs — for instance, when a document is opened, a worksheet is calculated, or a button in a custom dialog box is clicked — you should write a procedure associated with the event for the object or form. For general information about writing event procedures, see "Writing Code to Respond to Events" later in this chapter. For specific information about writing event procedures for custom dialog boxes and ActiveX controls, see Chapter 12, "ActiveX Controls and Dialog Boxes."

What's the Difference Between a Macro and a Procedure?

Although the terms macro and procedure are sometimes used interchangeably, they actually have distinct meanings. Procedure is the broader term; it applies to any unit of code enclosed either between the Sub and End Sub statements or between the Function and End Function statements. Macro is a specific term that applies only to public Sub procedures that take no arguments. All macros are procedures, but not all procedures are macros. All procedures you generate with the macro recorder and all procedures you can run from the Macros dialog box in the Office application are macros.

Sub Procedures vs. Function Procedures

With Visual Basic, you can create two types of procedures: Sub procedures and Function procedures.

A Sub procedure is a unit of code enclosed between the Sub and End Sub statements that performs a task but doesn't return a value. The following example is a Sub procedure.

Sub DisplayWelcome()
    MsgBox "Welcome"
End Sub

A Function procedure is a unit of code enclosed between the Function and End Function statements. Like a Sub procedure, a Function procedure performs a specific task. Unlike a Sub procedure, however, a Function procedure also returns a value. The following example is a Function procedure.

Function AddThree(OriginalValue As Long)
    AddThree = OriginalValue + 3
End Function

Public Procedures vs. Private Procedures

You can call a public procedure, declared with the Public keyword, from any procedure in any module in your application. You can call a private procedure, declared with the Private keyword, only from other procedures in the same module. Both Sub procedures and Function procedures can be either public or private. The following are examples of private procedures.

Private Sub Test1()
    MsgBox "This is the Test1 procedure running"
End Sub

Private Function AddThree(OriginalValue As Long)
    AddThree = OriginalValue + 3
End Function

The following are examples of public procedures.

Public Sub Test1()
    MsgBox "This is the Test1 procedure running"
End Sub

Public Function AddThree(OriginalValue As Long)
    AddThree = OriginalValue + 3
End Function

If you don't use either the Public or Private keyword to declare a procedure, the procedure will be public by default. Therefore, the following are also examples of public procedures.

Sub Test1()
    MsgBox "This is the Test1 procedure running"
End Sub

Function AddThree(OriginalValue As Long)
    AddThree = OriginalValue + 3
End Function

Although it's not necessary to use the Public keyword when creating a public procedure, including it in procedure declarations makes it easier to see at a glance which procedures are public and which are private. For more information, see "Public" or "Private" in Help.

Using the Value Returned from a Function

For a function to return a value, it must include a function assignment statement that assigns a value to the name of the function. In the following example, the value assigned to ConeSurface will be the value returned by the function.

Function ConeSurface(radius, height)
    Const Pi = 3.14159
    coneBase = Pi * radius ^ 2
    coneCirc = 2 * Pi * radius
    coneSide = Sqr(radius ^ 2 + height ^ 2) * coneCirc / 2
    ConeSurface = coneBase + coneSide
End Function

The information that must be supplied to a Sub procedure or Function procedure for it to perform its task (radius and height in the preceding example) is passed in the form of arguments. For more information about arguments, see "Passing Arguments to a Procedure" later in this chapter.

When the Function procedure returns a value, this value can then become part of a larger expression. For example, the following statement in another procedure incorporates the return value of the ConeSurface and ScoopSurface functions in its calculations.

totalSurface = ConeSurface(3, 11) + 2 * ScoopSurface(3)


Running a Sub Procedure

You can have a Sub procedure run in response to a specific event, you can run it from the Visual Basic Editor or your application window, or you can call it from another procedure.

  • If you want a Sub procedure to run automatically every time a specific event occurs, you should add the code to the event procedure for the event. For more information, see "Writing Code to Respond to Events" later in this chapter.

  • To run a Sub procedure from the Visual Basic Editor, position the insertion point anywhere in the procedure, and then either press F1 or click the Run Sub/UserForm button on the Standard or Debug toolbar.

  • To run a Sub procedure that's a macro (see "What's the Difference Between a Macro and a Procedure?" earlier in this chapter), select the macro name in the Macros dialog box in the application, and then click Run.

  • To call a Sub procedure from another procedure, name it in your code, just as you do with built­in keywords. The procedure in the following example calls the DisplayWelcome procedure.

    Sub TestCall()
       DisplayWelcome
    End Sub
    
    

You cannot call a procedure you've declared as private from any procedure outside the module in which the private procedure resides. However, you can call a public procedure from outside the module in which it resides. For an explanation of the terms "public" and "private" in this context, see "Sub Procedures vs. Function Procedures" earlier in this chapter.

When you call a public procedure that isn't located in the current module, Visual Basic searches other modules and runs the first public procedure it finds that has the name you called. If the name of a public procedure isn't unique, you can specify the module it's located in when you call the procedure. The following example runs a Sub procedure named "DisplayWelcome" that's stored in a module named "TestTools."

TestTools.DisplayWelcome


If necessary, you can also specify the project that the procedure resides in. The following example runs a Sub procedure named "DisplayWelcome" that's stored in a module named "TestTools" in a project named "TestDocument."

TestDocument.TestTools.DisplayWelcome


Note that the name of the project you specify is the project's code name, not the name of the document the project is associated with. You can check and modify the project's code name in the space to the right of (Name) in the Properties window for the project. To see the Properties window, select the project in the Project Explorer, and then click Properties Window on the View menu. You can also change the code name of a project by typing a new name in the Project Name box on the General tab in the Project Properties dialog box. You display this dialog box by right­clicking the project in the Project Explorer, and then clicking Properties on the shortcut menu (the command appears on the shortcut menu preceded by the current name of the project). For more information about the options in the Project Properties dialog box, click a tab and press F1.

Tip   If you want to be able to call a procedure from other modules in the same project but not from other projects, declare the procedure as public, but make the module private to the project by adding the Option Private Module statement to the (Declarations) section of the module.

If you want to be able to call procedures in one project from another project, there must be a reference from the project containing the calling code to the project containing the called code. To create a reference to a project, use the References dialog box (Tools menu).

Note   If you get an error when you try to create a reference from one project to another one, make sure that the project you're trying to reference doesn't have the same code name as the other project. (Multiple projects in an application may be given the same default code name, such as "Project" in Word or "VBAProject" in Microsoft Excel.) To check a project's code name, click the project name in the Project Explorer, and then click and then click Properties Window on the View menu. The text to the right of (Name) in the Properties window is the project's code name. To change the code name for a project, select the current code name and then type a new one. Keep in mind that you cannot have circular references — that is, if you have a reference to project A from project B, you cannot have a reference from project B to project A.

Passing Arguments to a Procedure

If your procedure needs information to perform its task that it cannot get from the context in which it's being run, you can pass that information to the procedure in the form of arguments. To indicate that a given procedure takes arguments, include an argument list between the parentheses that follow the procedure name in the procedure declaration. The argument list can contain multiple argument declarations, separated by commas.

When you declare an argument, you can specify the data type of the argument by using the As keyword (whether or not the procedure can change the argument's value by using the ByVal and ByRef keywords), and you can specify whether the argument is required or optional by using the Optional keyword. For more information about a specific keyword, see the appropriate topic in Help. For more information about the available data types in Visual Basic, see "Visual Basic Data Types" later in this chapter.

The following example shows the declaration line of a Sub procedure that takes three arguments.

Sub UpdateRecord(ByVal custId As Long, ByRef custName As String, _
    Optional custRepeat As Boolean)

The first argument, custID, is a required argument that will be passed as a value of type Long and will be passed by value. If you pass an argument by value when calling a procedure, the called procedure receives only a copy of the variable passed from the calling procedure. If the called procedure changes the value, the change affects only the copy and not the variable in the calling procedure.

The second argument, custName, is a required argument that will be passed as a value of type String and will be passed by reference. If you pass an argument by reference when calling a procedure, the procedure has access to the actual variable in memory. As a result, the variable's value can be changed by the procedure.

The third argument, custRepeat, is an optional argument that will be passed as a value of type Boolean and will be passed by reference (passing by reference is the default).

The following example calls UpdateRecord.

Dim newId As Long
Dim newName As String
Dim newRepeat As Boolean

newId = 3452
newName = "Mary Boyd"
newRepeat = True
UpdateRecord newId, newName, newRepeat

Note that the name of the variable you pass from the calling procedure doesn't have to match the name of the argument declared in the called procedure.

Using Named Arguments

If either a procedure you create or a built­in function, statement, or method takes more than one optional argument, you may want to pass arguments to it by name rather than by position.

For example, the Open method of the Microsoft Excel Workbooks object, which opens a workbook, takes 13 arguments. If you want to write code that opens the workbook Book2.xls and adds it to the list of recently used files, you could write the code shown in the following example.

Workbooks.Open "book2.xls", , , , , , , , , , , , True


However, this code is difficult to write correctly without introducing bugs, because you have to count the number of commas to insert between the arguments. The code is also very difficult to read, and it gives no clues about what the arguments represent. The following example shows a better way to write this code.

Workbooks.Open FileName:="book2.xls", AddToMru:=True


Because every argument has a name, you can use the name and the := operator to assign a value to an argument. When you use named arguments, you don't have to remember the order of the arguments. For instance, the preceding code could have been written with the order of the arguments reversed, as in the following example.

Workbooks.Open AddToMru:=True, FileName:="book2.xls"


You can also use named arguments with the procedures you create. Visual Basic automatically associates argument names with their corresponding procedures. For instance, assume that you've created a FormatList procedure that takes two required arguments and two optional arguments, as shown in the following declaration.

Sub FormatList(startRow As Integer, startCol As Integer, _
	Optional redText, Optional sortList)

The DoList procedure in the following example uses named arguments to call the FormatList procedure.

Sub DoList()
	FormatList redText:=True, startCol:=2, startRow:=2
End Sub

The arguments are now out of order, and one of the optional arguments was omitted.

Note   Using named arguments doesn't negate the need to enter required arguments.

Writing Code to Respond to Events

Certain objects in the Office 97 applications recognize a predefined set of events, which can be triggered either by the system or by the user. Examples of events recognized by objects in Office include the Open and Close events for Word documents; the Open, BeforePrint, BeforeSave, and BeforeClose events for Microsoft Excel workbooks; the Calculate and SelectionChange events for Microsoft Excel worksheets; the Click, Initialize, and Terminate events for custom dialog boxes; and the Click, GotFocus, and LostFocus events for ActiveX controls. For detailed information about the events available in Microsoft Excel and Word, see Chapter 7, "Microsoft Word Objects," and Chapter 4, "Microsoft Excel Objects." For detailed information about using custom dialog boxes and ActiveX controls, see Chapter 12, "ActiveX Controls and Dialog Boxes."

You can control how your application responds to a recognized event by writing code in the Code window for the object. Every time an event occurs, the code, or event procedure, associated with that event runs. For instance, if you write a procedure that's associated with the Open event for a Word document, every time that document is opened, the procedure automatically runs.

Where Event Code Is Stored

An event procedure is stored in the document, workbook, worksheet, slide, or UserForm where the event can be triggered. For example, the procedure that runs when you calculate the worksheet named "Last Quarter" in the workbook named "Sales" would be stored in the Last Quarter worksheet in the project associated with the Sales workbook. To view the code in a document, workbook, worksheet, slide, or UserForm, click the object in the Project Explorer, and then click the View Code button to open the Code window.

Note   PowerPoint presentations and slides don't recognize events. Therefore, unless you can place ActiveX controls (which recognize events) on a PowerPoint slide, there can be no event procedures associated with the slide, and you won't see the slide in the Project Explorer. For more information about adding ActiveX controls to documents, see Chapter 12, "ActiveX Controls and Dialog Boxes."

How Event Procedures Are Named

The name of an event procedure is the name of the object that recognizes the event — such as "Document," "Worksheet," "UserForm," or "CommandButton1" — followed by an underscore (_), followed by the name of the event that the procedure runs in response to — such as "Open," "Calculate," or "Click." For example, the procedure that runs when you open a Word document is Document_Open.

Note   Whereas the name of an event procedure for most objects is linked to the class name (such as Document, Worksheet, or UserForm), the name of an event procedure for an ActiveX control is linked to the control's code name — either the default name or a name you assign. If you change the code name of a control after writing event procedures, you must rename your procedures to match; otherwise, they will never run in response to the events for that control.

To view an event procedure, open the Code window for the document, workbook, worksheet, slide, or UserForm where the event can be triggered; select the name of the object that recognizes the event (this can be either the object where the event can be triggered itself or an ActiveX control contained in the object) in the Object box; and then select the name of the specific event you want to respond to in the Procedure box.

Note   If you want a procedure to be associated with a specific document, workbook, worksheet, slide, or custom dialog box, but not with a specific event — for instance, if you want to be able to call the procedure from several different event procedures — store it in the (General) section of the document, workbook, worksheet, or slide module.

Timesaving Tools for Writing Code

Many keywords used in Visual Basic are extremely long and difficult to type without making mistakes. To reduce the time you spend typing and the number of typing errors in your code, Visual Basic includes tools that complete words and build expressions for you.

When you've typed enough letters for Visual Basic to recognize a word, press CTRL+SPACE or click the Complete Word button on the Edit toolbar to have Visual Basic automatically complete the word for you.

In the Options dialog box (Tools menu), you can turn on tools that automatically do the following after you enter a line of code: verify correct syntax, display information, and give you appropriate options to choose from at each stage of building your expression.

You can also use the List Properties/Methods, List Constants, Quick Info, Parameter Info, and Complete Word buttons on the Edit toolbar to get help completing a word or an expression at any time. For more information about using a specific tool to help you complete words and statements, see the Help topic for that button or option. For information about using these tools to build statements using Office properties and methods, see Chapter 2, "Understanding Object Models."

Writing Code That's Easy to Read and Navigate

There are many ways to make your Visual Basic code more readable, as described in the following paragraphs.

Add comments to your code by using an apostrophe ('). At run time, Visual Basic ignores everything between the apostrophe and the end of the line. Each line in the following example includes a comment.

'This procedure calculates the burdened cost
'of the specified employee
Dim baseSalary As Currency          'salary not including benefits or bonuses
baseSalary = employeeLevel * 2500   'employeeLevel passed as argument

To add the comment character to the beginning of each line in a selected block of code, click the Comment Block button on the Edit toolbar. To remove the comment character from the beginning of each line in a selected block of code, click the Uncomment Block button.

Break a long statement into multiple lines in the Code window by using the line­continuation character, which is a space followed by an underscore ( _). The following example shows the same statement expressed two different ways: on a single line, and continued over two lines:

Set myField = ActiveDocument.Fields.Add(Range:=Selection.Range, Type:=wdFieldDate)

Set myField = ActiveDocument.Fields.Add(Range:=Selection.Range, _
    Type:=wdFieldDate)

Note that you cannot use the line­continuation character in the middle of a literal string. If you have to break the line within a literal string, break the string with the concatenation character (&), as shown in the following example.

MsgBox "This is a string that I have to break up " & _
    "so that I can continue it on another line"

You cannot follow a line­continuation character with a comment on the same line.

Use indentation levels to show logical levels in your code. Press TAB or click the Indent button on the Edit toolbar to shift each line in a selected block of code one indentation level to the right. Lines within the selection retain their indentation levels relative to one another. Press SHIFT+TAB or click the Outdent button on the Edit toolbar to shift each line in a selected block of code one indentation level to the right.

Use bookmarks to mark key areas in your code that you want to be able to move between quickly without having to navigate manually. Add a bookmark to a line by clicking the Toggle Bookmark button on the Edit toolbar. A blue, rounded rectangle appears in the margin to indicate a bookmark. To navigate between bookmarks, click the Next Bookmark or Previous Bookmark button on the Edit toolbar.

For more information about a specific feature, see the appropriate topic in Help.

Document Projects vs. Template Projects

If you're writing procedures that are specifically designed to be run on a single document, workbook, or presentation, you can store the code in the project associated with that document, workbook, or presentation.

If, however, you want to be able to get to a procedure from more than one document, workbook, or presentation, you can store the code in the project associated with a particular template.

When you apply a template to a Word document, the template is attached to the Word document. All procedures in the attached template are available to the document. If you change the code in a template, the changed code is available for use in all documents based on that template. If you want a procedure to be available to all Word documents, regardless of which templates they're based on, store the procedure in Normal.dot, which is automatically referenced by all documents.

When you apply a template to a workbook or a presentation, any code in the template project is copied to the project for the workbook or presentation. Unlike Word, Microsoft Excel and PowerPoint don't attach the template to the workbook, so changes you make to the code in the template project won't be reflected in the workbook or presentation projects after the template has been applied. If you want a procedure to be available to all Microsoft Excel workbooks, regardless of which templates they're based on, store the procedure in Personal.xls.

Class Modules

You use class modules to create your own custom objects when you want to create encapsulated, reusable units of code. The Sub and Function procedures you define in a class module become methods of the custom object. The properties you define with the Property Get, Property Let, and Property Set statements become properties of the custom object. For more information about creating and using custom classes, see Mastering Office 97 Development, a CD­ROM product available from Microsoft.

If you've added class modules to your project, either by clicking Class Module on the Insert menu or by copying modules from another project, you'll see a Class Modules folder under the project name in the Project Explorer. You get to the code for a particular class by clicking the class name and then clicking the View Code button at the top of the Project Explorer.

Variables, Constants, and Data Types

In Visual Basic, as in all high­level programming languages, you use variables and constants to store values. Variables can contain data represented by any supported data type.

Visual Basic Data Types

The following table lists the fundamental data types that Visual Basic supports.

Data typeDescription Range
Byte 1-byte binary data 0 to 255.
Integer 2-byte integer – 32,768 to 32,767.
Long 4-byte integer – 2,147,483,648 to 2,147,483,647.
Single 4-byte floating-point number  – 3.402823E38 to  – 1.401298E – 45 (negative values).

1.401298E – 45 to 3.402823E38 (positive values).

Double 8-byte floating-point number  – 1.79769313486231E308 to  – 4.94065645841247E – 324 (negative values).

4.94065645841247E – 324 to 1.79769313486231E308 (positive values).

Currency 8-byte number with a fixed decimal point  – 922,337,203,685,477.5808 to 922,337,203,685,477.5807.
String String of characters Zero to approximately two billion characters.
Variant Date/time, floating-point number, integer, string, or object. 16 bytes, plus 1 byte for each character if the value is a string value. Date values: January 1, 100 to December 31, 9999.

Numeric values: same range as Double.

String values: same range as String.

Can also contain Error or Null values.

Boolean 2 bytesTrue or False.
Date 8-byte date/time value January 1, 100 to December 31, 9999.
Object 4 bytesAny object reference.

Declaring a Constant, Variable, or Array

You declare a constant for use in place of a literal value by using the Const statement. You can specify private or public scope, specify a data type, and assign a value to the constant, as shown in the following declarations.

Const MyVar = 459
Public Const MyString = "HELP"
Private Const MyInt As Integer = 5
Const MyStr = "Hello", MyDouble As Double = 3.4567

If you don't specify scope, the constant has private scope by default. If you don't explicitly specify a data type when you declare a constant, Visual Basic gives the constant the data type that best matches the expression assigned to the constant. For more information, see "Const Statement," "Public Statement," "Private Statement," and "As" in Help.

You declare a variable by using the Dim, Private, Public, or Static keyword. Use the As keyword to explicitly specify a data type for the variable, as shown in the following declarations.

Private I
Dim Amt
Static YourName As String
Public BillsPaid As Currency
Private YourName As String, BillsPaid As Currency
Private Test, Amount, J As Integer

If you don't declare a variable as static, when a procedure that contains it ends, the variable's value isn't preserved and the memory that the variable used is reclaimed. If you don't explicitly declare a data type, Visual Basic gives the variable the Variant data type by default.

Note   Not all variables in the same declaration statement have the same specified type. For example, the variables Test and Amount in the last line in the preceding example are of the Variant data type.

The steps you take to declare an array are very similar to the steps you take to declare a variable. You use the Private, Public, Dim, and Static keywords to declare the array, you use integer values to specify the upper and lower bounds for each dimension, and you use the As keyword to specify the data type for the array elements. You must explicitly declare an array before you can use it; you cannot implicitly declare an array.

When you declare an array, you specify the upper and lower bounds for each dimension within the parentheses following the array name. If you specify only one value for a dimension, Visual Basic interprets the value as the upper bound and supplies a default lower bound. The default lower bound is 0 (zero) unless you set it to 1 by using the Option Base statement. The following declarations declare one­dimensional arrays containing 15 and 21 elements, respectively.

Dim counters(14) As Integer
Dim sums(20) As Double

You can also specify the lower bound of a dimension explicitly. To do this, separate the lower and upper bounds with the To keyword, as in the following declarations.

Dim counters(1 To 15) As Integer
Dim sums(100 To 120) As String

In the preceding declarations, the index numbers of counters range from 1 to 15, and the index numbers of sums range from 100 to 120.

Tip   You can use the LBound and UBound functions to determine the existing lower and upper bounds of an array.

You can declare arrays of up to 60 dimensions. The following declaration creates an array with three dimensions, whose sizes are 4, 10, and 15. The total number of elements is the product of these three dimensions, or 600.

Dim multiD(4, 1 To 10, 1 To 15)


Tip   When you start adding dimensions to an array, the total amount of storage needed by the array increases dramatically, so use multidimensional arrays with care. Be especially careful with Variant arrays, because they're larger than arrays of other data types.

You declare a dynamic array just as you would declare a fixed­size array, but without specifying dimension sizes within the parentheses following the array name, as in the following declaration.

Dim dynArray() As Integer


Somewhere in a procedure, allocate the actual number of elements with a ReDim statement, as in the following example.

ReDim DynArray(X + 1)


Use the Preserve keyword to change the size of an array without losing the data in it. You can enlarge an array by one element without losing the values of the existing elements, as in the following example.

ReDim Preserve myArray(UBound(myArray) + 1)


For more information, see "ReDim Statement" in Help.

Setting an Object Variable

You declare an object variable by specifying for the data type either the generic Object type or a specific class name from a referenced object library. The following declaration declares an object variable of the generic type Object.

Dim mySheet As Object


When an object variable is declared as the generic type Object, Visual Basic doesn't know what type of object the variable will later be used with. Therefore, Visual Basic cannot verify at compile time that the object exists, cannot verify that any properties or methods used with the object are specified correctly, and cannot bind this information to the object variable — in other words, Visual Basic cannot early bind the object variable. Not until the code runs and actually assigns an object to the object variable can Visual Basic verify this information and late bind the object variable. Generic object variables are useful when you don't know the specific type of object that the variable will contain, or when the variable must at different times contain objects from several different classes. If possible, however, you should provide a specific class name when declaring an object variable, as shown in the following declarations.

Dim mySheet As Worksheet
Dim myPres As Presentation
Dim myRange As Range
Dim myApp As Application

In addition to providing a specific class name, you may want to qualify the object variable type with the name of the application that's supplying the object, as in the following declarations. This is useful if you write code using the objects from more than one library, especially if the different libraries contain objects with the same name.

Dim wndXL As Excel.Window
Dim wndWD As Word.Window
Dim appWD As Word.Application

To assign an object to an object variable, use the Set statement, as shown in the following example.

Dim myRange As Excel.Range
Set myRange = Worksheets("Sheet1").Range("A1")

If you don't explicitly declare an object variable and you forget the Set statement in your assignment, Visual Basic attempts to use the default property of the object to assign a value to the variable. The following example assigns to the variable myRange the value of the default property of the Range object (which is the Value property) rather than the Range object itself.

myRange = Worksheets("Sheet1").Range("A1")	' forgot the Set statement!

Using Built­in Constants

The object library in each Office 97 application provides a set of built­in constants, which you can use to set properties or pass arguments to properties or methods. An enumerated type is a set of built­in constants that represent the possible values that a specific property can be set to or that a specific property or method can accept as an argument. In the Object Browser, many properties or methods will display the name of an enumerated type for a return type or an argument type instead of displaying a basic data type. To open the Object Browser in the Visual Basic Editor, press F2. You can use the Object Browser to see which constants are included in an enumerated type and what literal value each constant represents. For example, click Application in the Classes box in the Object Browser, and click DisplayAlerts in the Members of box. In the pane at the bottom of the Object Browser, you see the following phrase:

Property DisplayAlerts As WdAlertLevel

WdAlertLevel is an enumerated type that contains a set of constants that represent all the valid values for the DisplayAlerts property. You can recognize an enumerated type name because it begins with a prefix that indicates the object library that supplied it — such as Mso, Wd, Xl, Ac, Pp, VB, or Fm — just as built­in constant names do. To see the constants included in this enumerated type, click WdAlertLevel. Built­in constant names begin with the same prefixes as enumerated types. The Classes box in the Object Browser will scroll to the WdAlertLevel enumerated type, and you'll see the constants of this type listed in the Members of box. If you click one of the constants, you'll see the literal value that it represents in the pane at the bottom of the Object Browser. For more information about using the Object Browser, see Chapter 2, "Understanding Object Models."

You use built­in constants to replace literal values in your code. The two lines of code in the following example, each of which sets Word to display all alerts and message boxes when it's running a procedure, are equivalent to one another.

Application.DisplayAlerts = -1
Application.DisplayAlerts = wdAlertsAll


Code that uses these constants instead of literal values is easier to read. In addition, code that uses built­in constants is less likely to need to be updated if values are remapped in future versions. That is, whereas the literal value  – 1 might not always represent the option of displaying all alerts and message boxes, the constant wdAlertsAll always will.

Control Structures

Using control structures, you can control the flow of your program's execution. If left unchecked by control­flow statements, a program's logic will flow through statements from left to right, and from top to bottom. Although you can write very simple programs with only this unidirectional flow, and although you can control a certain amount of flow by using operators to regulate precedence of operations, most of the power and utility of any programming language comes from its ability to change statement order with structures and loops.

Decision Structures

Visual Basic procedures can test conditions and then, depending on the results of that test, perform different operations. The Visual Basic decision structures are listed in the following table.

To testUse
A single condition and run a single statement or a block of statements If...Then
A single condition and choose between two statement blocks If...Then...Else
More than one condition and run one of several statement blocks If...Then...ElseIf
A single condition and run one of several statement blocks Select Case

If...Then

Use the If...Then statement to run one or more statements when the specified condition is True. You can use either a single­line syntax or a multiple­line "block" syntax. The following pair of examples illustrate the two types of syntax.

If thisVal < 0 Then thisVal = 0

If thisVal > 5 Then
	thatVal = thisVal + 25
	thisVal = 0
End If

Notice that the single­line form of the If...Then statement doesn't use an End If statement. If you want to run more than one line of code when the condition is True, you must use the multiple­line If...Then...End If syntax.

Note   When the condition you're evaluating contains two expressions joined by an Or operator — for example, If (thisVal > 5 Or thatVal < 9) — both expressions are tested, even if the first one is True. In rare circumstances, this behavior can affect the outcome of the statement; for example, it can cause a run­time error if a variable in the second expression contains an error value.

If...Then...Else

Use the If...Then...Else statement to define two blocks of statements, as in the following example. One of the statements runs when the specified condition is True, and the other one runs when the condition is False.

If age < 16 Then
	MsgBox "You are not old enough for a license."
Else
	MsgBox "You can be tested for a license."
End If

If...Then...ElseIf

You can add ElseIf statements to test additional conditions without using nested If...Then statements, thus making your code shorter and easier to read. For example, suppose that you need to calculate employee bonuses using bonus rates that vary according to job classification. The Function procedure in the following example uses a series of ElseIf statements to test the job classification before calculating the bonus.

Function Bonus(jobClass, salary, rating)
	If jobClass = 1 Then
		Bonus = salary * 0.1 * rating / 10
	ElseIf jobClass = 2 Then
		Bonus = salary * 0.09 * rating / 10
	ElseIf jobClass = 3 Then
		Bonus = salary * 0.07 * rating / 10
	Else
		Bonus = 0
	End If
End Function

The If...Then...ElseIf statement block is very flexible. You can start with a simple If...Then statement and add Else and ElseIf clauses as necessary. However, this approach is unnecessarily tedious if each ElseIf statement compares the same expression with a different value. For this situation, you can use the Select Case statement.

Select Case

You can use the Select Case statement instead of multiple ElseIf statements in an If...Then...ElseIf structure when you want to compare the same expression with several different values. A Select Case statement provides a decision­making capability similar to the If...Then...ElseIf statement; however, Select Case makes the code more efficient and readable.

For instance, to add several more job classifications to the example in the preceding section, you can add more ElseIf statements, or you can write the function using a Select Case statement, as in the following example.

Function Bonus(jobClass, salary, rating)
	Select Case jobClass
		Case 1
			Bonus = salary * 0.1 * rating / 10
		Case 2
			Bonus = salary * 0.09 * rating / 10
		Case 3
			Bonus = salary * 0.07 * rating / 10
		Case 4, 5	'The expression list can contain several values...
			Bonus = salary * 0.05 * rating / 5
		Case 6 To 8	'...or be a range of values
			Bonus = 150
		Case Is > 8	'...or be compared to other values
			Bonus = 100
		Case Else
			Bonus = 0
	End Select
End Function

Notice that the Select Case structure evaluates a single expression at the top of the structure. In contrast, the If...Then...ElseIf structure can evaluate a different expression for each ElseIf statement. You can replace an If...Then...ElseIf structure with a Select Case structure only if each ElseIf statement evaluates the same expression.

Looping Structures

You can use loop structures to repeatedly run a section of your procedure. The Visual Basic loop structures are listed in the following table.

ToUse
Test a condition at the start of the loop, run the loop only if the condition is True, and continue until the condition becomes False Do While...Loop
Test a condition at the start of the loop, run the loop only if the condition is False, and continue until the condition becomes True Do Until...Loop
Always run the loop once, test a condition at the end of the loop, continue while the condition is True, and stop when the condition becomes False Do...Loop While
Always run the loop once, test a condition at the end of the loop, continue while the condition is False, and stop when the condition becomes True Do...Loop Until
Run a loop a set number of times, using a loop counter that starts and ends at specified values and that changes value by a specified amount each time through the loop For...Next
Run a loop once for each object in a collection For Each...Next

Note   Visual Basic also includes the While…Wend statement, but it's a good idea to use the more flexible variations of the Do…Loop statement (such as Do While…Loop or Do…Loop While) instead.

Do...Loop

Use a Do...Loop statement to run a block of statements an indefinite number of times — that is, when you don't know how many times you need to run the statements in the loop. There are several variations of the Do...Loop statement, but each one evaluates a condition to determine whether or not to continue running. As with an If...Then statement, the condition must be a value or an expression that evaluates to either True or False. The different Do…Loop variations are described in this section. For more information about the Do...Loop statement, see "Do...Loop Statement" in Help.

Note   If you want to run a block of statements a specific number of times, use a For…Next loop.

Do While...Loop

Use the Do While...Loop statement when you want to test a condition before you run the loop and then continue to run the loop while the condition is True.

Note   The statements in a Do While…Loop structure must eventually cause the condition to become False, or the loop will run forever (this is called an infinite loop). To stop an infinite loop, press CTRL+BREAK.

The Function procedure in the following example counts the occurrences of a target string within another string by looping as long as the target string is found. Because the test is at the beginning of the loop, the loop runs only if the string contains the target string.

Function CountStrings(longstring, target)
	position = 1
	Do While InStr(position, longstring, target) 'Returns True/False
		position = InStr(position, longstring, target) + 1
		Count = Count + 1
	Loop
	CountStrings = Count
End Function

Do Until...Loop

Use the Do Until…Loop statement if you want to test the condition at the beginning of the loop and then run the loop until the test condition becomes True. If the condition is initially True, the statements inside the loop never run. With the test at the beginning of the loop in the following example, the loop won't run if Response is equal to vbNo.

Response = MsgBox("Do you want to process more data?", vbYesNo)
Do Until Response = vbNo
	ProcessUserData    'Call procedure to process data
	Response = MsgBox("Do you want to process more data?", vbYesNo)
Loop

Do...Loop While

When you want to make sure that the statements in a loop will run at least once, use Do…Loop While to put the test at the end of the loop . The statements will run as long as the condition is True. In the following Microsoft Excel example, the loop runs only if the Find method finds a cell that contains "test." If the text is found, the loop sets the color of the cell, and then searches for the next instance of "test." If no other instance is found, the loop ends.

Sub MakeBlue()
	Set rSearch = Worksheets("sheet1").Range("a1:a10")
	Set c = rSearch.Find("test")
	If Not c Is Nothing Then
		first = c.Address
		Do
			c.Font.ColorIndex = 5
			Set c = rSearch.FindNext(c)
		Loop While (Not c Is Nothing) And (c.Address <> first)
	Else
		MsgBox "not found"
	End If
End Sub

Do...Loop Until

With the Do…Loop Until statement, which puts the test at the end of the loop, the loop runs at least once and stops running when the condition becomes True, as shown in the following example.

Do 
	ProcessUserData    'Call procedure to process data
	response = MsgBox("Do you want to process more data?", vbYesNo)
Loop Until response = vbNo

For...Next

When you know that you must run the statements a specific number of times, use a For...Next loop. Unlike the many variations of Do…Loop, a For...Next loop uses a counter variable that increases or decreases in value during each repetition of the loop. Whereas the variations of Do…Loop end when a test condition becomes True or False, a For...Next loop ends when the counter variable reaches a specified value.

The Sub procedure in the following example sounds a tone however many times you specify.

Sub BeepSeveral()
	numBeeps = InputBox("How many beeps?")
	For counter = 1 To numBeeps
		Beep
	Next counter
End Sub

Because you didn't specify otherwise, the counter variable in the preceding example increases by 1 each time the loop repeats. You can use the Step keyword to specify a different increment for the counter variable (if you specify a negative number, the counter variable decreases by the specified value each time through the loop). In the following Sub procedure, which replaces every other value in an array with 0 (zero), the counter variable increases by 2 each time the loop repeats.

Sub ClearArray(ByRef ArrayToClear())
	For i = LBound(ArrayToClear) To UBound(ArrayToClear) Step 2
		ArrayToClear(i) = 0
	Next i
End Sub

Note   The variable name after the Next statement is optional, but it can make your code easier to read, especially if you have several nested For loops.

For Each...Next

A For Each...Next loop is similar to a For...Next loop, except that it repeats a group of statements for each element in a collection of objects or in an array, instead of repeating the statements a specified number of times. This is especially useful if you don't know how many elements are in a collection, or if the contents of the collection might change as your procedure runs. The For Each…Next statement uses the following syntax.

For Each element In group
statements

Next element

When Visual Basic runs a For Each...Next loop, it follows these steps:

  1. It defines element as naming the first element in group (provided that there's at least one element).

  2. It runs statements.

  3. It tests to see whether element is the last element in group. If so, Visual Basic exits the loop.

  4. It defines element as naming the next element in group.

  5. It repeats steps 2 through 4.

The following Microsoft Excel example examines each cell in the current region for cell A1 on the worksheet named "Sheet3" and formats its contents as red if its value is less than  – 1.

For Each c In Worksheets("sheet3").Range("a1").CurrentRegion.Cells
	If c.Value < -1 Then c.Font.ColorIndex = 3
Next c

The following Word example loops through all the revisions in the current selection and accepts each one.

For Each myRev In Selection.Range.Revisions
    myRev.Accept
Next myRev

The variable name after the Next statement — c in the Microsoft Excel example and myRev in the Word example — is optional, but it can make your code easier to read, especially if you have several nested For Each loops.

Important   If you want to delete all the objects in a collection, use a For...Next loop instead of a For Each...Next loop. The following example deletes all the slides in the active PowerPoint presentation.

Set allSlides = ActivePresentation.Slides
For s = allSlides.Count To 1 Step -1
    allSlides.Item(s).Delete
Next

The code in the following example, on the other hand, won't work (it will delete every other slide in the presentation).

For Each s In ActivePresentation.Slides
    s.Delete
Next

Keep the following restrictions in mind when using the For Each...Next statement:

Nesting Control Structures

You can place control structures inside other control structures; for instance, you can place an If...Then block within a For Each...Next loop within another If...Then block, and so on. A control structure placed inside another control structure is said to be nested.

The following example searches the range of cells you specify with an argument and counts the number of cells that match the value you specify.

Function CountValues(rangeToSearch, searchValue)
	If TypeName(rangeToSearch) <> "Range" Then
		MsgBox "You can search only a range of cells."
	Else
		For Each c in rangeToSearch.cells
			If c.Value = searchValue Then
				counter = counter + 1
			End If
		Next c
	End If
	CountValues = counter
End Function

Notice that the first End If statement closes the inner If...Then block and that the last End If statement closes the outer If...Then block. Likewise, in nested For...Next and For Each...Next loops, the Next statements automatically apply to the nearest prior For or For Each statement. Nested Do...Loop structures work in a similar fashion, with the innermost Loop statement matching the innermost Do statement.

Exiting Loops and Procedures

Usually, your macros will run through loops and procedures from beginning to end. There may be situations, however, in which leaving, or exiting, a loop or procedure earlier than normal can save you time by avoiding unnecessary repetition.

For example, if you're searching for a value in an array using a For...Next loop and you find the value the first time through the loop, there's no reason to search the rest of the array — you can stop repeating the loop and continue with the rest of the procedure immediately. If an error occurs in a procedure that makes the remainder of the procedure unnecessary, you can exit the procedure immediately. You can cut a control structure off early by using one of the Exit statements.

Although the Exit statements can be convenient, you should use them only when it's absolutely necessary and only as a response to an extraordinary condition (not in the normal flow of a loop or procedure). Overusing Exit statements can make your code difficult to read and debug.

Also , there may be a better way to skip portions of your macro. For instance, instead of using an Exit statement inside a For...Next loop while searching for a value in an array, you could use a Do…Loop to search the array only while an incremented index value is smaller than the array's upper bound and a Boolean variable value is False, as shown in the following example. When you find the array value, setting the Boolean value to True causes the loop to stop.

i = LBound(searchArray)
ub = UBound(searchArray)
foundIt = False
Do
	If searchArray(i) = findThis Then foundIt = True
	i = i + 1
Loop While i <= ub And Not foundIt

You use the Exit Do statement to exit directly from a Do…Loop, and you use the Exit For statement to exit directly from a For loop, as shown in the following example.

For Each c in rangeToSearch
	If c.Value = searchValue Then
		found = True
		Exit For
	End If
Next

You use the Exit Sub and Exit Function statements to exit a procedure. The following example demonstrates the use of Exit Function.

For Each c in rangeToSearch
	If c.Value = searchValue Then
		counter = counter + 1
	ElseIf c.Value = "Bad Data" Then
		countValues = Null
		Exit Function	'Stop testing and exit immediately.
	End If
Next c