22
Walt Disney World Swan and Dolphin Resort Orlando, Florida 11/29/2005 - 8:00 am - 11:30 am Room:Swan 7/8 (Swan) The ABCs of VBA Do you want to customize AutoCAD using VBA but don't know how to get started? This course will demonstrate opening multiple drawings to make a modification to the title block's attributes. You will see the advantages of using VBA for dialog box interfaces and working on multiple drawings. CP21-2 About the Speaker: R. Robert Bell - MW Consulting Engineers Robert is the network administrator for MW Consulting Engineers, a consulting firm in Spokane, Washington, where he is responsible for the network and all AutoCAD customization. Robert has been writing AutoLISP code since AutoCAD v2.5, and VBA since it was introduced in Release 14. He has customized applications for the electrical/lighting, plumbing/piping, and HVAC disciplines. Robert has also developed applications for AutoCAD as a consultant. He is on the Board of Directors for AUGI and is active on Autodesk discussion groups. [email protected]

The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

  • Upload
    others

  • View
    3

  • Download
    0

Embed Size (px)

Citation preview

Page 1: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

Walt Disney World Swan and Dolphin ResortOrlando, Florida

11/29/2005 - 8:00 am - 11:30 am Room:Swan 7/8 (Swan)

The ABCs of VBA

Do you want to customize AutoCAD using VBA but don't know how to get started? This course will demonstrate opening multiple drawings to make a modification to the title block's attributes. You will see the advantages of using VBA for dialog box interfaces and working on multiple drawings.

CP21-2

About the Speaker:

R. Robert Bell - MW Consulting Engineers

Robert is the network administrator for MW Consulting Engineers, a consulting firm in Spokane, Washington, where he is responsible for the network and all AutoCAD customization. Robert has been writing AutoLISP code since AutoCAD v2.5, and VBA since it was introduced in Release 14. He has customized applications for the electrical/lighting, plumbing/piping, and HVAC disciplines. Robert has also developed applications for AutoCAD as a consultant. He is on the Board of Directors for AUGI and is active on Autodesk discussion [email protected]

Page 2: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking
Page 3: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

2

Introduction

You are obviously interested in learning how to write code using Visual Basic for Applications. There are some good reasons to learn VBA. Certain features, such as access to the Sheet Set Manager, are only available to non-C++/.NET programmers via VBA. The development environment is far superior to that available in Visual LISP®. Forms are much easier to do in VBA than in DCL. So the question is, how do you get started?

The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking a common request, modifying attributes in multiple drawings, and using VBA to create a program to do the work. This sample application will introduce you to the ABC’s of VBA.

Glossary of Terms

A brief glossary is provided to explain some of the terms you will hear related to VBA.

Breakpoint A selected line in the program at which execution

automatically halts

Class Contains the definition of an object

Compile-time The period when source code is translated to executable

code, for VBA this means when it is saved

Dim Statement used to declare a variable and the type of data it

holds

Early binding A reference is added to the project and its objects are

available while writing the code

Event A procedure that executes in reaction to some occurrence

Form A window or dialog box that forms part of the user interface

Function Type of procedure that returns a result

IDE Integrated Design Environment, the editor in which you

write code, design forms, and debug applications

Late binding Objects are bound only when the code runs, avoiding the

need for a reference

Macro A public Sub procedure that the user executes

Method A procedure that acts on an object, e.g. Copy, Move, or

Delete

Module A set of declarations and procedures

Object An element of an application, e.g. a form, a control, a layer,

or a circle

Object Model The objects related to a specific application, and the

properties, methods, and events related to those objects

Private The item thus declared is available only within the current

module or procedure

Procedure A named group of statements that execute as a unit

Project A set of modules

Property 1: A named attribute of an object, such as Center, or

Layer 2: Type of procedure that defines or returns data in a

class module

Public The item thus declared will be available outside the module

Run-time The period when compiled code is executed

Sub Type of procedure that does not return a result

The VBA Manager

VBA projects are loaded and unloaded with the VBA Manager. Use the VBAMan command to display this manager inside AutoCAD®. Loading a project does not automatically execute it. The user, a CUI command, a menu macro, or Visual LISP code must execute the project’s macros.

The elements of the VBA Manager dialog are straightforward, so there is no need to describe them here.

Page 4: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

3

The VBA Integrated Design Environment (IDE)

The VBAIDE may be displayed using one of two methods. One, you may use the Visual Basic Editor button in the VBA Manager dialog box. On the other hand, you may choose to use the VBAIDE command. Many of the elements of the VBAIDE are described here.

The Project Explorer (Ctrl+R) is where you add and remove a project’s forms, modules, and class modules. Add any of those elements by selecting the target project, or any of its existing elements, select Insert (from the menu bar or the right-click menu) and choose the type of module or form. You remove a selected element by choosing Remove from the File menu, or from the right-click menu.

The Properties window (F4) displays the design-time properties for the selected objects. You can change them

Page 5: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

4

while writing code in this window. Properties may also be changed at run-time. Some properties can only be changed at run-time.

The Immediate window (Ctrl+G) is very useful during debugging. It will display results from debugging statements in your code. You may also type commands directly into the window. You can change the data in a variable during the debug session. You may also observe what the results are of the commands you type in this window.

The Locals window (View menu) displays all the variables and their values for the procedure currently running. The Locals window does not display module-level variables. You may change the value of the variables in this window.

The Code window (F7) is the most important window, of course. Each object in the Project Explorer may have its own code window. Note the gray border on the left side of the code window. You may add breakpoints and bookmarks there.

The Watch window (View menu) automatically displays when watch expressions are defined in the project. Use the Debug menu to add watch expressions.

Debugging

The debugging tools available in the VBAIDE provide you with a complete package to locate and correct errors. Although this course will not delve into debugging applications here are some of the tools.

• The Debug object provides the useful Print and Assert methods

• Breakpoints to pause program execution

• Stepping thru code line-by-line while in break mode (F8)

• The Watch and Locals windows

Two Options You Must Change

The editor has a couple of options that really should be changed from their defaults. The two options are “Require Variable Declaration” and “Auto Syntax Check”.

I strongly suggest always using the “Require Variable Declaration” option. This forces you to think about the assignment of variables. It also helps you avoid variable name errors. When this option is active, any new project will have the statement Option Explicit automatically added to any modules or forms.

I hate the “Auto Syntax Check” option. I have never seen an “aid” get in the way of writing code like this option does! If you begin a line of code, and decide to go grab another line to copy-and-paste, this option will display a message box that your current line has a compile error. Duh. I’m working on it, you stupid computer…

Edit these options by going to the Tools Options menu item. This displays the Options dialog box. Select the Editor tab, clear the “Auto Syntax Check” option, and check the “Require Variable Declaration” option.

Projects

Projects are the container for separate elements such as standard modules, class modules, and forms. They also include references to external libraries which give you access to another application’s objects. Projects are saved as .dvb files. To distinguish projects, give them a different name in the Name property. Projects may be embedded in a drawing, but this is uncommon.

Page 6: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

5

ThisDrawing

Prominently available inside all projects created by AutoCAD’s VBAIDE is the ThisDrawing object. This unique object always refers to the current drawing, even when a running macro changes the current drawing. Document-level events are available in this object. This is also a common location for code in simple VBA projects.

Standard Modules

A standard module contains procedures, user-defined data type declarations, and data definitions. Module-level declarations and definitions are Public by default. This means that public Sub procedures will be visible to the user as macros. Module-level Private declarations and definitions are available only inside that module and are concealed from the user. It is possible to have several standard modules in a single project. Name the modules clearly and uniquely within each project.

Class Modules

This module defines a class. A class defines an object, just as a floor plan defines how a house is built. Class modules use procedures to define methods for the object. Exposed data in the class module act as properties. Class modules also permit you to reuse code without duplication of the code. A class module’s name is the name used when creating a new instance of the object defined by the class.

Forms

Forms are easy to create. It is simply a matter of drag-and-drop. Additional controls are available for purchase or free at many programming-related websites. You must be careful to use only controls in a VBA application that are available directly within VBA or as mentioned previously. Microsoft’s EULA prohibits the use of Visual Basic controls, for instance. You may use controls available thru installed applications as long as those applications are installed on all computers that use the VBA application.

You display forms with the Show method. Hide them using the Hide method. In VBA, a form cannot be closed, only hidden. Therefore, if you show the form again, you may need to make sure the controls are not duplicating data that existed from the previous display of the form.

The form, and each control for a form, has events associated with it. You use those events to react to the user’s input and work with the associated data. Events are procedures also, so you write VBA code to work with the form and its controls under those procedures. A form may also have ancillary procedures that are not events themselves.

Objects

Objects are a combination of code and data that are treated as a unit. A class defines an object. Objects

might be “visible things” such as a form or a control, or within AutoCAD, a layer or a circle. Objects can also be more “abstract”, such as encapsulating the code that is required to display an Open dialog box, or the code needed to access AutoCAD’s Dictionary/XRecords. Objects are a great way to reduce complex code to easy-to-use elements.

Procedures

Named procedures organize groups of code. Use the Sub or Function statements to declare these procedures. A Sub executes the code but does not return a result. A Function executes the code and returns a result. Both types of procedures can accept arguments. Declare arguments with a

Page 7: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

6

name and data type. Arguments act as a variable inside that procedure. Arguments may also be declared as optional.

Procedures will be Public unless they are preceded with the Private keyword.

Arrays, Collections, and Dictionaries

Data often need to be grouped together. There are several mechanisms to perform this grouping.

Arrays are the basic way this is accomplished. Arrays need to store the same type of data throughout the array, although the type can be a Variant. By default arrays are indexed starting at zero, i.e. the first element in the array is at index 0. You reach each element in the array by providing the variable that holds the array followed by the index enclosed in parenthesis, e.g. myArray(2) returns the third element in the array.

Collections are a step up from arrays. A collection is treated as a single object. The members in the collection do not need to be the same data type. You may use the Count property to return the number of members in the collection. The Item property give you access to each member. Note that collections are indexed starting at one, i.e. the first member is at index 1. Members in the collection can be added or removed at any time. Added members may be placed before or after specific members. You can provide a unique key for each member as it is added and retrieve that member by using the key instead of using the Item property.

The Scripting Runtime Library provides dictionaries. They go beyond a collection with a few additional properties and methods.

Variables

Variables need to be “declared”, or defined as holding specific types of data. The declaration of the variable also sets how the variable name appears in the rest of the code. When you use mixed-cased variable names, it becomes easy to determine when you correctly type a variable name. Many experienced programmers use variable names that start with lower-case letters and then use upper-case letters further in the name, e.g. myCircle.

Some programmers use the first part of the variable name to describe the type of data the variable stores, but this technique has its negatives. If the type of data needs to be changed at a later point the variable name no longer accurately reflects the data type. A far better approach is to make the variable name simple and clear enough to describe why it is used, e.g. myPickPoint must be holding a coordinate point.

Usually, the Dim statement declares variables. For example, the following statement declares a variable named aLayout that will hold one of AutoCAD’s Layout objects.

Dim aLayout As AcadLayout

Here is another example, this one declaring a variable named myDist as a double, which is a double-precision floating-point number.

Dim myDist As Double

Arrays are declared by specifying the dimensions of the array and the data type the array will contain.

Dim myArray(0 To 1) As String

Use the ReDim statement to reallocate an array. Redimensioning an array is “expensive” in computer time. If you do not know the exact dimensions required for an array at compile-time it is far better to estimate an appropriate size and redimension it only when required. The existing data in the array is deleted unless the Preserve keyword is used.

ReDim Preserve myArray(0 To 2)

Page 8: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

7

Note that you cannot change the type of data stored in the array except in certain conditions. See the VBA help files for more information.

VBA Data Types

The following list is not exhaustive, but does contain the common data types you will use in day-to-day VBA programming for AutoCAD.

• Boolean True or False

• Double Double-precision floating-point number

• Integer 16-bit integer

• Long 32-bit integer

• Object Object reference

• String A string of characters

• Variant Can contain any kind of data, usually used to store an array returned by a function

Variables can also be declared to store specific objects. This is most often done with AutoCAD objects for the AutoCAD VBA programmer, but can be done for objects exposed by other classes. Adding a reference to an external library opens this option to you. Doing this is called “early binding”. The advantages of early binding are an increase in performance and IntelliSense® becomes available for the objects in the reference. The disadvantage is that the reference might be version-specific, e.g. adding a reference to Microsoft® Office® ties your project to that version of Office.

Assigning Data to Variables

Data are assigned to variables thru the use of the equal sign (=). You place the variable name on the left of the equal sign and the value to the right. If the variable has been declared as an object, you need to use the Set statement. Here are some examples of variable assignments.

myAge = 29 hrWage = 5.15 myLayerName = "0" Set myLayout = ThisDrawing.Layouts.Item("Model")

What is IntelliSense?

IntelliSense is a feature that makes writing code in the VBAIDE a joy. For instance, IntelliSense anticipates your variable and statement names as you type. Type the first part of the variable name. Hit Ctrl+Space. If the partial name is unique enough, the variable name is completed for you. If there are several possibilities, they are listed for you and you can select the one you need.

IntelliSense also works with objects. An object’s properties and methods are displayed in a list when you type a period (.) after the object. This reduces trips to the help files to determine the exact property or method name you need to accomplish the desired task. You should plan for this ability when you create a new class. Make sure your property and procedure names are clear enough for anyone using IntelliSense to determine the needed property or method.

Once you have used IntelliSense a few times you realize just how easy it makes writing code in the VBAIDE. Going back to the Visual LISP IDE is a painful experience from that point.

Page 9: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

8

A Couple of Common Mistakes

A couple of very common errors frequently occur when you first start writing VBA code.

The first one relates to the question of when you need to use parenthesis when calling a function. A function can be called either with or without enclosing its arguments in parenthesis. The rule is that you need to use parenthesis when you are using the result of the function. With that knowledge, determine which of the following examples (that calls a function named GetFiles) are incorrect.

A) GetFiles myFiles B) success = GetFiles myFiles C) success = GetFiles(myFiles)

Example A) is not incorrect because the return value from the function is being ignored. Example B) is incorrect because the return value is being used yet the argument is not enclosed in parenthesis. Example C) is correct because the function’s argument is enclosed in parenthesis and the return value is being used.

The other error results from the confusion regarding the use of the Set statement. Remember, you need to use the Set statement only when you assign an object to a variable. Using that knowledge, determine which of the following examples are incorrect.

A) myLayerName = "0" B) Set myLayerName = "0" C) Set myLayout = ThisDrawing.Layouts.Item("Model") D) myLayer = ThisDrawing.Layers.Item(myLayerName)

Example A) is correct because the assignment is a string and obviously not an object. Example B) is not correct because the Set statement is being used for a string assignment. Example C) is correct as the variable is being assigned a Layout object and therefore the Set statement is required. Example D) is incorrect because the Layer object is being assigned to the myLayer variable without the Set statement.

Making Choices

A program will usually need to branch depending on circumstances. A VBA program can branch in several ways. Most common are the If…Then…Else or Select Case statements. The structure of the If statement is straightforward. A test condition is needed, one that results in either true or false. A Then statement immediately follows the test condition. Code that executes when the condition is true is on subsequent lines. Where code is required when the condition is false you must place an Else statement on a line to itself and its code on lines following that statement. Finally, close the If block with the End If statement.

If theSkyColor = "Blue" Then MsgBox "You are correct." Else MsgBox "Only in my world." End If

There are a couple of variations possible with the If statement. There is a single-line form and the use of the ElseIf statement. These are documented in the VBA help files.

If myName = “Tim” Then WarnOfBeast

The Select Case statement is useful when a single expression may have several different values. Each case is evaluated until a match is found. When a match is found the code that follows that case is executed and then the Select Case block is exited. You may also use the Case Else statement to execute code if none of the other case statements are matched. The Select Case block is ended with an End Select statement.

Page 10: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

9

Select Case favoriteColor Case "Blue" MsgBox "Well, alright then." Case "Yellow" MsgBox "Blue. No! wait yellow..." Case "Green" MsgBox "Hey! That's not in the movie." End Select

Looping

Programs are usually written to make repetitive tasks simple. Therefore, many programs perform some sort of looping. VBA has many looping constructs. The most common is the For…Next loop. The For statement uses a counter to execute its block of code a specific number of times. The Next statement closes the block of code in the For loop and increments the counter. Usually the counter is increased by one each time thru the loop, but you may use the Step keyword to change the increment or reverse the counter.

Dim i As Long For i = 1 To 5 MsgBox "The counter is at " & CStr(i) Next i

A variation of that type of loop is the For Each…Next loop. This form is used to loop thru an array or a collection. You need to declare a variable of the appropriate type before entering the loop. The following example loops thru all the layouts in the current drawing.

Dim aLayout As AcadLayout For Each aLayout In ThisDrawing.Layouts MsgBox "Layout name: " & aLayout.Name Next aLayout

You may need to exit the For…Next loop depending on certain conditions. This is accomplished with the Exit For statement. You can have multiple Exit For statements in a single For…Next loop.

Another type of loop in VBA is the Do…Loop. This will loop while, or until, a condition is true. Therefore, the loop repeats an indefinite number of times. This is useful when you do not know how many times the loop needs to occur. The Until keyword may be used to make the loop repeat until a certain condition is true. The Exit Do statement will force an exit from within the loop.

Odds and Ends

A few other items about VBA are worth mentioning.

• Combine multiple statements on one line by separating the statements with a colon (:). This can be very useful to combine selection set filters, for instance. You will see this in the sample application.

• At times, you need to split a statement into multiple lines. Accomplish this by using a space followed by the underscore character ( _). The IDE will let you know if you split a statement inappropriately by highlighting the lines in question in red.

• You may declare multiple variables on one line by separating the variables with a comma (,). Note that you must declare the data type for each variable if you use this approach.

• Arguments for procedures can be given by name. This is useful when a procedure has multiple optional arguments and you want to provide only a few of those options. A named argument consists of the name followed by a colon and an equal sign (:=) and then the value for the argument. A fine example of this is shown in conjunction with the selection set in the sample application.

Page 11: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

10

• Although you can password protect a VBA application, it is an exercise in futility. The password protection can be easily cracked. Therefore, if you want to develop a secure application, VBA is not the language for you.

The AutoCAD Object Model

At last, we are ready to discuss the specifics of using VBA to program tasks related to AutoCAD. It is very important to get familiar with the object model of AutoCAD. The absolute best place to see the object model is in AutoCAD’s help files. Go to the ActiveX Automation and VBA files, and select the ActiveX and VBA Reference. The first topic in that reference is the object model. The items on that map are clickable. So you can learn about the properties and methods of each object just by clicking on it. Many of the objects also have examples. These examples go a long way in helping to learn how to write VBA code that works with AutoCAD.

Take a few moments to walk thru the object model in the context of the sample application. The sample application is going to perform the following tasks:

• Get drawings to be modified

• Open one of those drawings

• Iterate thru all layouts

• Get all the attributed blocks that are inserted in each layout

• Show all the attributes and their values for a selected block

• Change the selected attribute’s value in all the drawings

Notice how the Application object has a Documents collection. The Documents collection has an Open method that opens a specific Document. Of course, a single Document has a Layouts collection. Each Layout has a Block property (every layout in a drawing is also a block to VBA) which can be used to iterate thru all that layout’s objects. We are looking for BlockReference objects. When we find one, we can use its HasAttributes property to see if there are attributes. If there are, we can use the GetAttributes method to get an array of all its attributes. Each attribute for an inserted block is an AttributeReference object. This object has two properties we are interested in: TagString and TextString.

Page 12: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

11

The Sample Application

Now that we know what we need from the object model, we shall determine the procedures for the application. Obviously, we need a main procedure, one that is a Public Sub so that it is a macro visible to the user. It will be called UpdateAttribute.

There needs to be a procedure to get the drawing filenames. This will be called GetFiles. It will be a function because it returns filenames.

If files were selected, we need a procedure to get all the blocks with attributes from that drawing. An obvious name would be GetBlocks.

Once the user selects the desired block via a dialog box, we use the attribute information gathered from GetBlocks to populate a dialog box for the attributes. The user selects the desired attribute and another dialog box is displayed to get the changed value from the user.

After all that information is gathered, the drawings the user initially selected need to be processed to make the change to that specific attribute in all the inserted blocks that match the block name. Do you think ProcessDrawings is a good name for the procedure?

Of course, there will be some additional procedures to support those primary ones. These ancillary procedures will be developed as the need arises.

All the code presented here will be fully commented and downloadable from the Autodesk University website.

Creating a New VBA Project

Use the VBAMan command to display the VBA Manager. Use the New button to create a new project. Use the Visual Basic Editor button to enter the VBAIDE. Change the project’s name to “ABCs” in the Properties window.

Insert a new standard module and name it “Main”.

Two public variables in the main module hold the block information and the attributes for the selected block. This permits the transfer of data from the main module to the forms that need this information.

A scripting dictionary will be used to store the block names and the block’s attributes. The reasons why a scripting dictionary was chosen will be evident later in the course. A reference needs to be added to the project. Use the Tools References… menu item to display the References dialog box. Scroll down to “Microsoft Scripting Runtime” and select it.

Option Explicit Public AllBlocks As Scripting.Dictionary Public AllAttribs As Variant

The subsequent sections of the handout describe the program flow for each procedure.

UpdateAttribute (The User Macro)

A variable to hold the array of selected filenames is declared and populated by another procedure (GetFiles). The storage mechanism for the blocks in a drawing, a scripting dictionary, is created. The public variables from all three forms are initialized. The object did not exist so the New keyword was used. If any files are selected, then the first drawing in the array is opened and the module-level variable AllBlocks is populated by another procedure (GetBlocks). As long as there are

Page 13: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

12

attributed blocks found, a form is displayed to list the blocks for the user’s selection. When a block is picked, its attributes are placed as an array in the module-level variable AllAttribs. Another form is displayed, listing all those attributes and the value found for each one. The user selects an attribute and a final dialog box is shown, to get the changed value of the attribute. If the OK button is hit in that final form, then all the drawings the user selected are processed and the desired attribute’s text value is changed. Finally, a dialog box informs the user that the process is complete.

Public Sub UpdateAttribute() Dim myFiles As Variant myFiles = GetFiles Set AllBlocks = New Scripting.Dictionary BlockDialog.BlockPicked = "" Set AttribDialog.AttribPicked = Nothing TextDialog.DoUpdate = False If IsArray(myFiles) Then Dim myDoc As AcadDocument Set myDoc = AcadApplication.Documents.Open(myFiles(0)) GetBlocks myDoc, AllBlocks End If If AllBlocks.Count > 0 Then BlockDialog.Show If BlockDialog.BlockPicked <> "" Then AllAttribs = AllBlocks.Item(BlockDialog.BlockPicked) AttribDialog.Show End If If Not (AttribDialog.AttribPicked Is Nothing) Then TextDialog.Show End If If TextDialog.DoUpdate Then ProcessDrawings myFiles, _ BlockDialog.BlockPicked, _ AttribDialog.AttribPicked.TagString, _ TextDialog.NewText MsgBox "Process is complete.", vbOKOnly, "ABC's of VBA" Else myDoc.Close False End If End Sub

GetFiles

The code that is required to display a dialog box for selecting files is lengthy. In addition, this task is needed many times. For this reason, such code is placed in a class module. A search of the Internet will reveal many such class modules. The class module used for this course was downloaded from www.acadx.com and slightly modified to permit easier code maintenance. The modification permits you to keep the class module in its own project, yet still use it in your own projects. Note that the original copyright needs to remain with the class module if you choose to use it for your own projects. This modified class module project will be available for download from the Autodesk University website.

Make a reference to this separate project. Use the Tools References… menu item to display the References dialog box. Change the “Files of type” option to “VBA Project Files”. Browse to the location where you saved the class module’s project and select it.

Page 14: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

13

A variable to hold the CommonDialog object is declared. Run the procedure to initialize the object. Various properties for that object are then changed. The CommonDialog class returns a number to indicate success or failure, so a variable to hold that number is declared. The ShowOpen method is used, and its return value is saved to the variable. That return value is examined and if files were selected those filenames are returned as an array.

Private Function GetFiles() As Variant Dim myOpen As CommonDialogProject.CommonDialog Set myOpen = CommonDialogProject.Init myOpen.DialogTitle = "Select drawings" myOpen.Filter = "Drawings (*.dwg)|*.dwg" myOpen.Flags = OFN_ALLOWMULTISELECT + _ OFN_EXPLORER + _ OFN_FILEMUSTEXIST + _ OFN_HIDEREADONLY + _ OFN_PATHMUSTEXIST myOpen.InitDir = "C:\datasets\CP21-2" myOpen.MaxFileSize = 2048 Dim success As Long success = myOpen.ShowOpen If success > 0 Then GetFiles = myOpen.ParseFileNames End Function

GetBlocks

This function requires two arguments, a drawing file to examine and the scripting dictionary that will be storing the block names and the attributes for each block.

The first thing done is to change the comparison mode for the dictionary. Since the block names are strings the mode is changed to TextCompare. Variables to hold AutoCAD entities, layouts, and block references are declared. Next, we loop thru all the layouts, ignoring the Model tab. All the entities in each layout are checked to see if it is a block reference (an inserted block). If the entity is a block reference and has attributes, this information is passed to an ancillary procedure (AddBlock). The reason for this ancillary procedure will be clear in a moment.

Private Function GetBlocks(ByVal theDoc As AcadDocument, _ ByRef BlockStore As Scripting.Dictionary) BlockStore.CompareMode = TextCompare Dim aEntity As AcadEntity Dim aLayout As AcadLayout Dim aBlkRef As AcadBlockReference For Each aLayout In theDoc.Layouts If Not (aLayout.ModelType) Then For Each aEntity In aLayout.Block If TypeOf aEntity Is AcadBlockReference Then Set aBlkRef = aEntity If aBlkRef.HasAttributes Then AddBlock BlockStore, aBlkRef.Name, aBlkRef.GetAttributes End If End If Next aEntity End If Next aLayout End Function

AddBlock

Items in a scripting dictionary are indexed by a key. In this case, the key will be the block name. We can retrieve a specific block’s attribute data just by referring to the block name. Also, the keys themselves are used to populate a dialog box. This is why a scripting dictionary was chosen.

Page 15: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

14

Obviously, this key cannot be duplicated. Therefore, we need to handle the error that will occur when we attempt to add a block that was already added. Code that you know will cause an error should be isolated in its own procedure so that you can handle the error with ease. In the case of all the error handlers for this project, we will use the On Error Resume Next statement. This causes the procedure to recognize the error but just continue with the next statement in the procedure. So the effect is that a new block is always added to the dictionary and an existing one is skipped (the error is ignored).

Private Sub AddBlock(ByRef BlockStore As Scripting.Dictionary, _ ByVal Name As String, _ ByVal Attribs As Variant) On Error Resume Next BlockStore.Add Name, Attribs End Sub

BlockDialog

The first dialog needs to be constructed now. This dialog box will list all the block names and permit the user to select one. Insert a new form and name it BlockDialog. Change its caption to “Select titleblock”. Add a ListBox and name it “BlockList”. Resize the ListBox to a decent size. Add two CommandButtons. Name one “DoOK” and the other “DoCancel”.

A module-level public variable is provided to communicate the user’s choice of block back to the main code.

Whenever a form is displayed, the UserForm_Activate event executes. Therefore, the list of blocks is cleared of any existing

items. A variable is declared for an array that will come from… the scripting dictionary. A scripting dictionary’s Keys method returns the keys (in this case, the block names) as an array. This means that we do not need to perform any looping to populate the ListBox. A ListBox will accept an array to populate its list thru its List property. Finally, the two buttons’ properties are modified. Notice that the OK button is disabled whenever the form is displayed. It does not make sense to enable that button until the user selects a block.

The BlockList_Click event enables the OK button.

When the user hits Cancel, the module-level variable is cleared and the form is hidden.

When the user hits OK, the module-level variable is set to the selected block name and the form is hidden.

Option Explicit Public BlockPicked As String

Private Sub UserForm_Activate() BlockList.Clear Dim blkNames As Variant blkNames = Main.AllBlocks.Keys BlockList.List = blkNames DoOK.Accelerator = "O" DoOK.Caption = "OK" DoOK.Default = True DoOK.Enabled = False

To array or not array Please note that the blkNames array is not really necessary. The array from AllBlocks can be used directly by the List property and the array is not used elsewhere in this procedure. However, an array was declared so you could see what the data looks like when stepping thru the code.

Page 16: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

15

DoCancel.Accelerator = "C" DoCancel.Caption = "Cancel" DoCancel.Cancel = True End Sub

Private Sub BlockList_Click() DoOK.Enabled = True End Sub

Private Sub DoCancel_Click() BlockPicked = "" Me.Hide End Sub

Private Sub DoOK_Click() BlockPicked = BlockList.Value Me.Hide End Sub

AttribDialog

The dialog box to display the attributes and their values is very similar to the BlockDialog form. Insert a new form and name it AttribDialog. Change its caption to “Select attribute to change”. Add a ListBox and name it “AttribList”. Resize the ListBox to a decent size. Add two CommandButtons. Name one “DoOK” and the other “DoCancel”.

A module-level public variable is provided to communicate the user’s choice of attribute back to the main code as an AttributeReference object.

The UserForm_Activate event clears the list of attributes of any existing items. The ListBox will actually use two columns to display

the attribute data. The first column will display the attribute’s tagname and the second column will display its value. The first column will be used to return the tagname when the OK button is selected. This is the BoundColumn property. The two columns get their widths assigned. A variable is declared for an array. It is created from data in the AllAttribs variable from the Main module. The array has two dimensions. The first dimension matches the count of the attributes. The second dimension stores the TagString (tagname) and the TextString (value) properties of the attribute. The ListBox is populated by its List property. Finally, the two buttons’ properties are modified just like the ones from BlockDialog.

The rest of the procedures are just like the ones from BlockDialog, except that the global variable works with an object and not a string value.

Option Explicit Public AttribPicked As AcadAttributeReference

Private Sub UserForm_Activate() AttribList.Clear AttribList.BoundColumn = 1 AttribList.ColumnCount = 2 AttribList.ColumnWidths = "72;144" Dim attData As Variant ReDim attData(0 To UBound(Main.AllAttribs), 0 To 1) As String

Page 17: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

16

Dim i As Long For i = 0 To UBound(Main.AllAttribs) attData(i, 0) = Main.AllAttribs(i).TagString attData(i, 1) = Main.AllAttribs(i).TextString Next i AttribList.List = attData DoOK.Accelerator = "O" DoOK.Caption = "OK" DoOK.Default = True DoOK.Enabled = False DoCancel.Accelerator = "C" DoCancel.Caption = "Cancel" DoCancel.Cancel = True End Sub

Private Sub AttribList_Click() DoOK.Enabled = True End Sub

Private Sub DoCancel_Click() Set AttribPicked = Nothing Me.Hide End Sub

Private Sub DoOK_Click() Set AttribPicked = Main.AllAttribs(AttribList.ListIndex) Me.Hide End Sub

TextDialog

Insert a new form and name it TextDialog. Change its caption to “New value”. Add a TextBox and name it “Text”. Resize it to a decent size. Add two CommandButtons. Name one “DoOK” and the other “DoCancel”.

Two module-level public variables are supplied. DoUpdate is a true/false flag that communicates back to the main code whether or not the user selected the OK button. We cannot

rely on the NewText variable to determine if we should proceed with the update because the user may want to blank the value, or force the current value on all the selected drawings.

The TextBox is populated with the value from the attribute, and the text is selected, so the user can overwrite the text without manually selecting it. Finally, the two buttons’ properties are modified just like the ones from BlockDialog, except that the OK button is not disabled.

The DoCancel_Click event forces the flag to false and hides the dialog box.

The DoOK_Click event places the text from the TextBox in the NewText public variable, sets the flag to true, and hides the dialog box.

Option Explicit Public DoUpdate As Boolean Public NewText As String

Page 18: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

17

Private Sub UserForm_Activate() Text.SetFocus Text.Value = AttribDialog.AttribPicked.TextString Text.SelStart = 0 Text.SelLength = Len(Text.Value) DoOK.Accelerator = "O" DoOK.Caption = "OK" DoOK.Default = True DoCancel.Accelerator = "C" DoCancel.Caption = "Cancel" DoCancel.Cancel = True End Sub

Private Sub DoCancel_Click() DoUpdate = False Me.Hide End Sub

Private Sub DoOK_Click() NewText = Text.Value DoUpdate = True Me.Hide End Sub

ProcessDrawings

If all the data has been collected, then all the drawings are ready to be changed. The ProcessDrawings procedure will take four arguments.

• The array of drawing filenames

• The name of the selected block

• The tagname of the selected attribute

• The new text for the attributes

There are two approaches to working with a drawing’s specific objects. One you saw earlier, where the entire drawing, or specific layouts, are iterated thru, examining all objects for a match. This approach simplifies the code, and works fine for an expected large percentage of positive matches. The second approach, using a filtered selection set, is better when working in a drawing with a relatively small number of potential matches.

This procedure will use a filtered selection set, as the code needs to work on only one specific block name, although there may be any number of insertions of that block. This also gives you the chance to see how to create a selection set and provide a filter for it.

A selection set filter requires two arrays, one for the DXF-style codes and one for the filter data. The DXF-style codes array needs to be declared as using Integers, not Variants or Longs. The filter data can be of various data types, so its array needs to be declared as using Variants. The size of both arrays must match the number of filters you plan on using. Remember, arrays use a 0-based index. The DXF-style codes are found in the DXF Reference help files.

Notice in the sample code that the index 0 data for both arrays is on the same line. Populating the arrays with the needed data is best done by grouping the two needed statements together in this fashion. Therefore, there are two filters to be used for the selection set: filter for inserts (DXF-style code 0) and of a specific name (DXF-style code 2).

Several variables are then declared. One is a variable to hold the name of an open drawing if one of the drawings in the list is already open. The next stores the actual Document object. A variable

Page 19: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

18

for the selection set object is declared. A variable is declared to hold the array of attributes. Finally, a couple of counters are needed.

A For…Next loop is used to open each drawing in turn. An ancillary procedure (GetOpenFilename) is used to see if the drawing is already open. If it is open already, the Document object is set to the open drawing. Otherwise, the drawing is opened and the variable is set to that newly opened drawing.

An ancillary procedure (GetSS) is used to return a selection set object to this procedure. The details of why a separate procedure is used will be discussed under the GetSS topic.

The selection set is then populated using the filters created earlier. The entire drawing is searched for matching objects. Notice the use of named arguments in the Select method. This method has two other optional arguments that occur before the filter arguments. Since those two arguments are not used, naming the arguments permits the statement to skip them.

Another For…Next loop is used to iterate thru all the objects in the selection set, changing the desired attribute’s value in an ancillary procedure (ChangeAttrib).

The selection set is then deleted. This is a good habit to get into. When a selection set is no longer needed, delete it. There are only 128 “slots” for selection sets. If you do not get in the habit of deleting unneeded selections sets in your code, you will discover programs failing during long editing sessions.

Finally, the drawing is closed, saving the changes if the drawing was not read-only. The process then repeats on the next drawing.

Private Sub ProcessDrawings(ByVal Dwgs As Variant, _ ByVal BlockName As String, _ ByVal TagName As String, _ ByVal NewText As String) Dim fType(0 To 1) As Integer Dim fData(0 To 1) As Variant fType(0) = 0: fData(0) = "INSERT" fType(1) = 2: fData(1) = BlockName Dim openFilename As String Dim myDwg As AcadDocument Dim mySS As AcadSelectionSet Dim myAtts As Variant Dim i As Long, j As Long For i = 0 To UBound(Dwgs) openFilename = GetOpenFilename(Dwgs(i)) If openFilename <> "" Then Set myDwg = AcadApplication.Documents.Item(openFilename) Else Set myDwg = AcadApplication.Documents.Open(Dwgs(i)) End If Set mySS = GetSS(myDwg) mySS.Select Mode:=acSelectionSetAll, _ FilterType:=fType, _ FilterData:=fData For j = 0 To mySS.Count - 1 ChangeAttrib mySS.Item(j), TagName, NewText Next j mySS.Delete myDwg.Close Not myDwg.ReadOnly Next i End Sub

Page 20: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

19

GetOpenFilename

The main procedure, UpdateAttribute, opens the first drawing in the group selected by the user. There is no need to open the drawing again. In addition, any of the other drawings might be open in the AutoCAD session. So this procedure tests to see if a given drawing is open or not.

The function needs one argument, the filename as found in the original array. Since the data type for those filenames in the array is a Variant, the argument here needs to be a Variant. The function will return just the filename and extension, without the path information. That is how each open document is stored in AutoCAD’s Documents collection.

A counter is declared, to be used by the For…Next loop. Each open document’s FullName property is compared to the given fully qualified filename from the calling procedure. If there is a match, the Name property is returned and the loop is exited.

Notice the use of the With statement. This permits the code to use an object without storing it in a variable, while accessing several properties or methods of that object. In this case, both the FullName and Name properties are used within the With block of code.

Private Function GetOpenFilename(fqnName As Variant) As String Dim i As Long For i = 0 To AcadApplication.Documents.Count - 1 With AcadApplication.Documents.Item(i) If StrComp(.FullName, fqnName, vbTextCompare) = 0 Then GetOpenFilename = .Name Exit For End If End With Next i End Function

GetSS

Creating a selection set in VBA is a bit tricky. The issue is that you cannot create a selection set without a name, and if a selection set with that name already exists, the attempt to create it will cause an error. Even worse, if you attempt to get a selection set with a name that does not exist, you will get an error.

Therefore, an ancillary function to return a selection set object is needed, regardless of whether it exists or not. Remember, any code that you know can cause an error is best placed in its own procedure to isolate its error handling.

This function requires one argument, the drawing for the selection set; and an optional argument for the name of the selection set. If the optional argument is not provided by the calling procedure, “mySS” is used as the name. The function will return a SelectionSet object.

The On Error Resume Next statement is used to recognize the error but just continue with the next statement in the procedure.

An attempt is made to get an existing SelectionSet object of the given name. If this succeeds, the selection set is cleared of any existing objects. If the selection set did not exist, an error occurs on both statements. However, the error handler tells the procedure to continue with the next line of code.

So the If statement checks to see if a specific error has occurred. If the error number equals 91, then the selection set does not exist. One is created then. In either case, the function returns an empty selection set, ready for use.

Page 21: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking

The ABCs of VBA

20

Private Function GetSS(ByRef theDoc As AcadDocument, _ Optional ByVal Name As String = "mySS") _ As AcadSelectionSet On Error Resume Next Set GetSS = theDoc.SelectionSets.Item(Name) GetSS.Clear 'Clear the selection set of any items If Err.Number = 91 Then Set GetSS = theDoc.SelectionSets.Add(Name) End Function

ChangeAttrib

The final ancillary procedure for the sample application is the one that changes the attribute’s value. This procedure requires three arguments: the inserted block, the attribute’s tagname, and the new value for the attribute.

An array is declared to hold the given block’s attributes. The BlockReference object’s GetAttributes method is used to populate that array.

A counter is needed for the For…Next loop used to go thru all the attributes found for the inserted block. When the matching attribute is found, the new value is placed in the TextString property and the loop is exited.

Private Sub ChangeAttrib(ByVal theBlock As AcadBlockReference, _ ByVal TagName As String, _ ByVal NewText As String) Dim myAtts As Variant myAtts = theBlock.GetAttributes Dim i As Long For i = 0 To UBound(myAtts) With myAtts(i) If .TagString = TagName Then .TextString = NewText Exit For End If End With Next i End Sub

Conclusion

The ABC’s of VBA has given you a good start on learning how to automate AutoCAD using VBA. You see the advantages of writing programs in the VBAIDE. You have learned the basics of VBA syntax. As you can see, creating forms is very simple. AutoCAD’s object model is explained and you know where to go for more information. Moreover, it is all wrapped up in a working application that allows you to edit multiple drawings.

Page 22: The ABCs of VBA...The first part of this course will introduce you to some features of VBA and its syntax. The second part will help you to get started programming in VBA by taking