You are on page 1of 933



bV Jerrv Winters

MicroStation VBA
Jerry Winters

B!tley Institute Press

Exton, PA




First Edition
Copyright 2006 Bentley Systems, Incorporated. All Rights Reserved.
Bentley, "B" Bentley logo, Bentley Institute Press, and MicroStation are either registered or
unregistered trademarks or servicemarks of Bentley Systems, Incorporated or one of its direct or
indirect wholly-owned subsidiaries. Other brands and product names are trademarks of their
respective owners.

Publisher does not warrant or guarantee any of the products described herein or perform any
independent analysis in connection with any of the product information contained herein.
Publisher d oes not assume, and expressly disclaims, any obligation to obtain and include
information other than that provided to it by the manufacturer.

The reader is expressly warned to consider and adopt all safety precautions that might be indicated
by the activities herein and to avoid all potential hazards. By following the instructions contained
herein, the reader willingly assumes all risks in connection with such instructions.

The publisher makes no representation or warranties of any kind, including but not limited to, the
warranties of fitness for particular purpose of merchantability, nor are any such representations
implied with respect to the material set forth herein, and the publisher takes no responsibility with
respect to such material. The publisher shall not be liable for any special, consequential, or
exemplary damages resulting, in whole or part, from the readers' use of, or reliance upon, this
ISBN Number: 0-9714141-8-1
Library of Congress Control Number: 2006903498
Published by:
Bentley Institute Press
Bentley Systems, Incorporated
685 Stockton Drive
Exton, PA 19341

B!tley Institute Press

Printed in the U.S.A.

John Gooding of Bentley Systems, Inc.
MicroStation VBA, the MicroStation V8 implementation of the
Microsoft Visual Basic for Applications (VBA) engine, provides
MicroStation users and developers with a large number of capabilities from easy customization to tight integration with other Windows
applications. With VBA, users can customize MicroStation to automate
repetitive tasks that are specific to their needs.
Many users have discovered the simplicity of using VBA to make their
jobs easier. The ability to use a standard user interface, the advantage of
using one of the most popular programming languages in the world, and
the ease of connecting and communicating with Microsoft Office and
other applications are just some of the many things that VBA provides.
While MicroStation VBA is popular, you might be among those who are
hesitant - perhaps even a bit leery - to explore programming in
MicroStation. You shouldn't be. VBA is the easiest environment to
explore programming, and Learning MicroStation VBA is an excellent
guide to help you do it yourself.
Too often, one-size-fits-all manuals lack either the appropriate
grounding material a new user needs or the in -depth technical
information experts require. However, Learning MicroStation VBA
accomplishes the rare feat of serving both novice and expert users


I Foreword I

equally well. With the benefit of Jerry Winters' broad VBA expertise and
his knowledge of MicroStation, that's exactly what this comprehensive
text accomplishes.
The introductory chapters thoughtfully and thoroughly step new users
through the basics of Visual Basic for Applications. From his detailed
review of VBA's Integrated Development Environment, through his
careful consideration of how and when to use forms and class modules,
to his comprehensive explanation of object models, Jerry ensures that
novice users not only have a how-to guide for working with VBA, but
they can also benefit from his insight into how VBA tools can be best
applied to create professional applications for MicroStation.
Expert VBA programmers will likewise find Jerry's book an invaluable
reference tool -- one that will help them exploit what VBA has to offer.
Jerry's overview of XML and the Windows API in MicroStation VBA,
for instance, are sure to improve any programmer's mastery.
But for the masses of MicroStation users, this book should help you
finally put your programming fears to rest. Within these pages, you have
all you need to start programming in MicroStation and automate your
most common tasks.

Introducing VBA ........................................ 1-1
What is VBA? . ... . ... .... . .... . ..... ..... . .. . .. . ... . . ..... . .. . 1-1
Why Learn VBA? ..... . . . . . . . . . . . . ....... . . ...... . .. .. . . .. .... . 1-2
When Should You Use VBA? ... ..... .. .. . . . . . . ..... . .. . . . ... .... 1-2
How Do We Use VBA? ... . .. ..... .. ........... ......... . .. ..... 1-2
What does VBA look like? .... ....... . . .. .... ........... . .. . . ... 1-6
Review .. ....... . . . . .. .. .. . .. ... . . . . .... . .... . .......... .. . . .. . 1-7

The VBA Project Manager ............................... 2-9

VBA Project Manager Functionality ... . . . ...... . ... .... ...... . .. 2-10
Macros Dialog Box ................. ..... ...................... 2-12
Review ...... ...... . . ... . . ... .. . ............ ....... . .. ........ 2-13

The VBA I DE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 3-15

Menus ............... ................ ...... ......... . .... .. .. 3-16
File Menu ................................................ 3-16
Edit Menu .......... .. . . ........ ........ ...... . . . . ....... 3-17
View Menu ..... ........... .... . ........ .... .. . ... . ..... .. 3-18
Insert Menu .............................................. 3-19
Format Menu ...... .. .. ........ ............. .. ............ 3-19
Debug Menu . . . .. ........ . ..... .......... ...... .......... 3-19
Run Menu . .. ............. ............ . . .. .. . ........... . 3-20
Tools Menu .. . ..... . ... .......... . .. ....... ... . ..... . . ... 3-20
Add-Ins Menu .. .......... ........... ... . .. .... . ..... ..... 3-20
Window Menu ......... ..... .... .. . . .. . .................. 3-21



I Contents I
Help Menu ......... . .... ... .. .... ...... ........... ....... 3-21
Toolbars ........... . ..... . . . . . ......................... . ..... 3-22
Standard toolbar ................ . ................... . ..... 3-22
Edit toolbar . ... ...... ........ .... ..... . ...... . ........... 3-22
Debug toolbar ............... . ........ . .. . . .. . .. .......... 3-22
User Form toolbar ......................................... 3-23
Windows ........................... .. .. ... ... . ... .. ..... . ... 3-23
Project Explorer ............................. . ............ 3-23
Object Browser ................. .. ...... . . . ... .. .......... 3-24
Properties Window ......... .. ............ . ........ . ....... 3-25
Watch Window ........................................... 3-25
Locals Window ........................................... 3-26
Immediate Window ....................................... 3-26
Call Stack Window ........................................ 3-27
Toolbox Window ........ . .. . . ..... . .. ... .. .. . . . ... .... .. . 3-27
Other Windows ..... . ..................... . .... . . .. . . ..... 3-28
Review ... . .... . ............................................. 3-30

Finding Help ......................................... 4-31

Terminology .... . .... ... . ... . .. ..... .. .... ......... . ....... .. 4-32
Help Files .................................................... 4-34
Contents tab .... .. .... .......... . ...... . .. . ............... 4-35
Index tab ... . ....... .. ................... . .... .. ..... . .... 4-36
Search tab ....................... .. .. ..... . .. . ........... . 4-37
Favorites tab .............................................. 4-37
MicroStation VBA Help File .................. ...... ........ 4-39
The Net. . ............. . ... .... . ................. . ... . . . ..... . 4-40
The Object Browser ......................... . ................. 4-42
Review ...................................................... 4-44

Modules, Forms, and Class Modules .................... 5-45

Modules . .... ........ . ....................................... 5-45
Forms ....................................................... 5-49
Classes ... ... ....... ... .. ...... . . ... .... ......... ............ 5-52
Procedures and Functions ...................................... 5-55
Returning an Array .. . ...... .. ............................. 5-60
Returning 'Types' .. .... .... ......... ... . .......... . .. ..... 5-62
Returning Objects ......................................... 5-63
ByVal and ByRef . ....... .... . .. .. .... ..................... 5-64
Declaring Variables ....................................... 5-66
Option Explicit ........................................... 5-67
Review ................. .... ............ . ... .. ... .. .......... 5-67

I Contents I

Variables ............................................ 6-69

Standard VBA Variable Types . . . . .. .. .. . ... ..... . ..... .. ... . . . . 6-70
Integer. .................................................. 6-70
Long .................................................... 6-70
Double .................................................. 6-71
Boolean .................................................. 6-72
Date . .... . ........................ . ........... . .......... 6-72
String ... .. ....... . ......... ... ..... . . . .... . ... . . . . ... . ... 6-72
Obj ect .. ......... . . . ......................... . .. . . . ... . . . 6-72
Variant .. . . .............................................. 6-73
MicroStation-Specific Variable Types ... . ........................ 6-73
Application . .. . . . . ..... ......... .. ....... . .. . ........ . ... 6-73
DesignFile.. .. .... . . . . .... ... . . . . ..... . . . .. .. .. .... . ... . . . 6-74
ModelReference .. .. . .... . . . .. . . . .... . ... . ......... . . . .... 6-74
Level .. . ................ .. . ... . . . ...... ... .... . . . ..... . . . 6-74
LineElement. .......... . ....................... . .......... 6-75
EllipseElement. ... . . .. . . . ...... . ...... . ......... . ..... . ... 6-75
ArcElement ............................... ... ............ 6-75
TextElement ........ .. .. . ................................. 6-76
Assigning Values and Setting Objects . . .... . . . .... . ... .. .. .. . . ... 6-76
Arrays .. .. ... ......... .. .. . ......... . ..... . ...... . ....... .. . . 6-77
Constants ......... . . . ............ . . . ......... ..... .... ... ... . 6-78
Variable Names ... .. ............... . ..................... . ... . 6-78
Case Sensitivity ........................................... 6-80
Option Explicit ....... . .............. . .... . ................... 6-80
Using Variables ............................................... 6-81
Review .. .. .. . ........................... . ............. . ...... 6-82

Working With Text ................................... 7-83

VBA String Functions ..... . ........ . .. . ................ . ..... . 7-84
UCase ............. . ............ . ....... .. .... ... . .. .. . . . 7-84
LCase . ..... .. .... . .. .... .. . . ... ........... . . . .... . . . .. .. . 7-84
StrConv . .... ..... . .. . . . ...... .. .... . . . . ..... . . . ..... .. .. . 7-85
WeekDayName, WeekDayNumber . . ..... . . . . . . . .. . ....... .. 7-85
MonthN arne .. .... . ........... . . .. . ................ . .. . .. 7-86
LTrim, RTrim, Trim . . .. ... . . ... . . . . . .. .. . ..... . ........... 7-86
StrComp .. . .. . . .... .. . . ... . ... ... .... ... .. . .. . . . . .. . ... . . 7-87
Len . ... . .... . ............. . . .. . ........... . ..... . . . ... .. . 7-90
Left . ... . . .. ..... . . ... ... . . . . . .... . .... . . .. . . . . .... . .. . . . 7-90
Right ..... . ..... . ... .. ...... . ...... .. . . .. . ... . .. . ... . .... 7-90
Mid . ... . .. .. . ... .... . . . ..... .. .. .. ........ .. ......... ... 7-9 1
Replace . . . . . .. .. ...... . . . ....... ... . .... . . . . ... . . ..... . .. 7-92
InStr .. ... . . . . . .. .. .. . ... ........... . . . ... . ... . ....... .. . 7-92
InStrRev . .. .. .. ..... . ... . ........ .. .... .. ... .. ..... . ..... 7-94


I Contents I
Split and Join .. . .. .. . . ... . .. .. ... . ... .. . . .. ... . . .. . ...... . 7-95
Asc and Chr .......................... . ....... .. ..... . .... 7-96
FormatCurren cy .......... . ... . . . ................ . ........ 7-98
FormatNumber ........ .. ..... ... . ... ..... . .. .. ...... ... .. 7-98
FormatDateTime ......... . ... .. . . ........... . .. . .......... 7-99
Format ..... . ...... . ................................ . ... 7-100
& ...................................................... 7-100
vbCr. ................................................... 7-1 00
vbTab .................................................. 7- 101
Review .. . .. . ..... . ... . ......... . ........... . ................ 7-101

Working With Numbers .............................. 8-103

Numeric Functions . ...... ........ . ........... .. .. . ........... 8-103
Addition . ... . .. .. . .... .. ..... .. .. .. ... .. ................ 8-104
.. . ... ......... . ...... . ..... . . . .... . ..... .. . . ..... . . .. .. 8-105
Subtraction .... . ........... . . . .......... . ... . ..... . ...... 8-105
Multiplication ............ . .................... . . . ... ... . 8-105
Division ..... . ...... . . .. .... . . .. . .. .. .... .. ............. 8-106
Squares and Exponents ............. . .. .. .......... . .. . ... 8-106
Square Root ... . ... . . .. .. .. . . . . . .... . ... . . . ... . ....... . .. 8-107
Sine, Cosine, Tangent .. . .... . ............................. 8-107
Arc Tangent. .... . ...... . .................... ... . . ... ... . 8-110
Absolute Value ............ . .... . .... . . . ................. 8-110
Convert to Integer, to Long, to Double, and Value . .. ..... ... . 8-111
CLng ..... .. ............... . ............................ 8-111
Fix . . ................ . ... . .................... . . . ....... 8- 112
CDbl . . .. . .............. . ........... . . . . . .. . . . .......... 8-112
Val ......... . . .... . . . . . . . .. . . . ... . .. .. . . .... .. ..... . . ... 8- 113
IsNumeric ...... .... . ..... .... ........ . ....... ..... ..... 8-113
Round .... ..... ... ... .............. ... . ........ . . .... ... 8-114
Mod - Find the Remainder ................................ 8-114
Sgn - Show me a sign . ... .... . . . .............. . ......... .. 8-115
Rnd and Randomize .... ....... .. ....... . .. . . . . ... . ....... 8-115
Order of Operations ...................................... 8-116
Review .................. .. ............ ... ........... ...... .. 8-116

Standard VBA Calls .................................. 9-117

MessageBoxes .............. . .... . ... . ....... . ............... 9-117
InputBox ................................................... 9-120
Now! ................................................... 9-122
DateAdd ... . ......... ...... ...... ... ... . . ....... ........ 9-122
DateDiff ................................................ 9-123
Timer .................................................. 9-124
FileDateTime ............................................ 9-124

I Co ntents I


FileLen . . ........ . ... . .. .. . ... .. . ..... . .... . . . .. ........ 9- 124

MkDir ................ ... . . .... . ....... .. . . . ..... . . . . ... 9- 124
RmDir ................. . ............................ .. .. 9-125
Dir . ............................ .. .......... . .. . . ....... 9- 125
Kill . . .. . . ................ . . .. .. ... ...................... 9-1 27
Beep . . ... ... . .. . . ........ . ...... ... . ... . ........... . . ... 9-1 28
SaveSetting ... .. . . . .... . ....... . . . . ........ .. ........ .. . . 9- 128
GetSetting . . ..... .. . .. . . ... .... ..... .... . ... . .. . . . .. ..... 9- 128
DeleteSetting ........ . . . . ... . ......... ...... . .. . .. .. . . . .. 9- 129
GetAllSettings . . .... . . .. .. .. .. . .... ...... . ...... . ........ 9-129
Reading and W riting to ASCII Files . .. ..... .. ... . .. . . . . ... ... .. 9-130
FreeFile .... . ..... . .. . . . .......... . ............... . .... . . 9-131
Reading from ASCII Files . ... .. . ... ..... . . . .... . . . .... . ... 9-134
Controlling Code Execution ........... . ... . .. .. . . . ... . .... .... 9-135
For ... Next ............ . ............ . ...... .. . . . . . ..... . . 9-136
While ... Wend . . . ... .. .... .... . . ... . ........ . ........... 9-137
Do ... Loop ................ . .. . . . . .. . .. . .... .. . . ... . . ... 9-138
For Each ... Next ... ...... ..... . . . ...... ... .............. 9-14 1
If ... Then ... . ........... . .. . .... .. . ...... . . . . .. .. ...... 9-141
Select Case . .................. . ....... . ... . . . ....... . .... 9-142
Error Handling . .. . . . .. . . . . .. . . . ........ .. . . . .. . . .... . ... 9-143
Review ......... . .................. . .... .. . . . .. . ... ..... ... .. 9-149

Visual Interface ........... ....................... 10-151

Properties, Methods, and Events ..... . .. ... .. .. ... ............ 10-152
Properties .......................... ... ... .. .......... .. 10-152
Control Events .. ..... . .. . .. ......... . .... .. . .. .. . . ...... 10-154
Common Control Properties ..... ..... . . ..... . . . . .... . . . .... . 10-155
Name .. ... . .. . ....... . ........... ..... . . . ... . . .... . .. .. 10-156
Left, Top . .............. . .... . . . .. .. . . . .. .... ....... . . .. 10-156
Width, Height . . ........... . . ... ............. . . . .. .. ... . 10-157
Visible . . ..... . ........... ...... . .. .... . .... . . ........ .. 10-157
Enabled ............... . .... . ....... . . . ...... . .......... 10-157
TabStop ..... . ........ . .... . ... . . . ..... . ... . . ..... ... .. 10-157
TabIndex .. . ... . . .. ... . .. . .... . . ...... ... ... ... .. .. . . .. 10-157
Tag .. . .... ...... . . . .... . . . ... .. .. . . . ... ... ... . . ...... .. 10-158
ControlTipText .. . ..... . . . ... . .. . ...... . ... . ..... .. . . . . . 10-158
Label . .. .. ....... .. ......... . . . ....... . .. .. ......... . . . 10-158
TextBox . . ........ . ... . .. . . . .. . . . . .. ... . ........... . ... 10-158
Properties ......... .. . . ............. . .... . ....... . .. . ... 10-159
Events . ...................... . .. . ........... ..... . . .... 10-160
Combo Box . . ... . ................. . .. . . .. . .. ... . . . ..... . .. .. 10-160
Properties .......... . ..... . . .. . .. . ..... . .. . .. .... .. .. . .. 10-160
Methods ........ .. ........... . ......... . .... ...... . . .. . 10-161


I Contents I
Events ................ . ... . .... ........... . ..... . ...... 10-161
ListBox .... .. .. . ...... ... ....... .. . . ....................... 10-161
Properties .............................................. 10-161
Methods ............................................... 10-161
Events ................................................. 10-162
CheckBox .................................................. 10-162
Properties .............................................. 10-162
Events . ....... .. ...... .. .... ....... ............. ....... 10-162
OptionButton . .... ......... ..... .. ......................... 10-162
Properties . ................... ...... ................... . 10-163
Events ................................................. 10-163
Toggle Button ..................... . ............... ... ...... 10-163
Properties .............................................. 10-163
Events ................... . ........... ................. . 10-163
Frame .... ... ........ ......... ... ......... ... .............. 10-164
Properties ... . . . ................... ... ...... ..... ....... 10-164
CommandButton .. ..... .......... . .......... ... ........ . ... 10-164
Properties . .... ... .. .. . .. ..... ...... ..... .. .. ..... . . . . .. 10-164
Events ................. . ............ ... .. . ............. 10-164
Tab Strip ....... .. .. ... .. . .. .. . ... ...... ..... ...... .... ..... 10-164
Properties ......... .... ........ ... ... ..... ......... .... . 10-165
Methods ............................................... 10- 165
Events .................... . . . . . ..... .... . . ...... ...... . 10-165
MultiPage ....... .. ...... . ....................... .... ... . ... 10-165
Properties .......... . .......... . ........................ 10-165
Methods .... ..... . . ... . . . ......... ........ .. . . .... ..... 10-165
Events . ...... .. .......... . ... .. ........... .. . . ...... ... 10-166
ScrollBar ....... . ...................................... .. . .. 10-166
Properties ............................ ..... .. ........... 10-166
Events ... ................ ...... ..... ................ . .. 10-166
SpinButton ........ ..... .................... .. .......... .. .. 10-166
Properties . .................... . ................ . ....... 10-167
Events ....... . ......................................... 10-167
Image ... . ...................................... . .......... 10-167
Properties . ............................................. 10-167
User Interface Exercises ............... .. .. . .................. 10-167
Point List Reader. ................................ . .......... 10-174
Write Out File .. ............................................ 10-177
Zoom And Pan .............. ..... ................... ....... 10-182
Review .................................................... 10-185

The MicroStation Object Model - Objects .............. 11-187

The Object Browser ......................................... 11 -188
Auto List Members .. ......... ... .. . .................. .. ..... 11 -190

I Contents I
MicroStation VBA Help File . .. ... . . .. . . . . . . .. . ... .. . . ... .. .. . 11 -190
Adding Watch es ... .. ... .. . . . . . . . . . . . . .................. . . . . 11- 192
The MicroStation Object Model. .............................. 11-192
Application Object ...................................... 11-193
Review .................................................... 11-241

The MicroStation Object Model - Enums .............. 12-243

MsdDesignFileFormat ..................................... .. 12-243
The Enumeration List ....................................... 12-245
Review ............. ................................. . .... . 12-277

The MicroStation Object Model - Types ............... 13-279

Review ................................ .. ...... .. .......... 13-283

The MicroStation Object Model - Events ........ . ..... 14-285

OnDesignFileOpened and OnDesignFileClosed ...... . ...... . . .. 14-286
Review .... .. . .. .. . . .. ... .... . ..... . . . ........ . ..... . . ..... 14-288

Adding To Documents . . . .............. .. . . ..... . .. . . 15-289

Graphical Elements .. ........... . ......... . ....... . .. . . . .... 15-289
Lines .............. .. .... . .... .. .. .. ... . . . .. .. ......... 15-289
Creating Shapes .... . . . ... ... . ........ .. . . ..... . . . . .. . . .. 15-295
Creating Circles ..... . ............... . ................... 15-297
Creating Ellipses . . . . . . . . . .. . ......... .. ... . ... ... . ...... 15-300
Creating Arcs .............................. . .......... . . 15-301
Creating Text. ... ... ...... . ... . .. . .. . ... .. .. .. .......... 15-303
Creating Cells ........... .. ............................. 15-304
Creating New Documents . ............ ....................... 15-307
Security Issues with Creating Data ............................. 15-309
Review .................................................... 15-309

Searching In Files ........ ....................... .... 16-311

The Basics of Searching Files . .. ... ........................... 16-311
Using ScanCriteria ..... . . . ....... . ......................... . 16-316
Multiple Combinations of Criteria ............................. 16-321
Reviewing Three Collection methods . . .... . . . ........ . ........ 16-324
Scan Criteria Methods ...... . ........ . .................. . .... 16-325
Review ............. .. ................. ... ........... . .. ... 16-327

Interactive Modification . ........................... . 17-329

Giving Users Feedback and Information ....................... 17-329
Working With Selection Sets ................................. 17-332


I Contents I
Getting User Input ......... . . . . . ............... . ...... ... ... 17-334
Some Real-World Applications ....... . ....................... 17-338
Using SendCommand . . . ... . . . .. ... ....... .. .. . .. .... .. .... . 17-348
Modeless Dialog Boxes ................... . ..... . .. . .. . ... . ... 17-353
frmMatchProperties.frm ............. . ................... 17-353
Providing User Feedback and Information ..................... 17 -359
UserForm Initialize ...................................... 17-360
frmAlignText.frm ....................................... 17-362
frmExportElements.frm .................................. 17-377
frmDFAV.frm .......................................... 17-381
Interacting with MDL Applications ............................ 17-387
Review ..................................................... 17-390

Interface Essentials ................................. 18-391

Interface Basics . ....... . .. .... . ................ . ............ 18-392
Class Module Review ........................................ 18-393
Class Module Lifecycle ....................................... 18-395
ILocateCommandEvents ... .... .. . .. . .. .... . . ......... .. . 18-396
LocateFilter Event ....................................... 18-398
Accept Event ... . ............ . . . . .... ....... ... ... . .. . . . 18-399
LocateReset Event ....................................... 18-399
LocateFailed Event .. ... .... ... .. .... .. .. ...... .. ... ... .. 18-399
Start Event . ... . ....... ... ............. . ....... . ... ... .. 18-399
Cleanup Event .. .. ... . ....... .. .. .. ............. . .. . ... . 18-399
Dynamics Event ....... .. ............ .. .... .... .... . .... 18-399
LocateCriteria .... ..... . ... .... .. ....................... 18-404
IPrimitiveCommandEvents ..... ... ................... . . . . 18-406
Optimizing The Dynamics Event .......................... 18-426
Review ..................................................... 18-430

Using MicroStation's Built-In User Forms ............. 19-431

Declaring MicroStation User Form Functions ... .......... . ..... 19-431
The mdIDialo~fileOpen Function ........................ 19-432
The mdIDialo~fileCreate Function ............ . ... ... . ... 19-439
The mdIDialo~fileCreateFromSeed Function ... .... . ..... . 19-441
The mdIDialo~openAlert Function ......... .......... .... 19-443
The mdIDialo~openInfoBox Function .................... 19-443
Review ..................................................... 19-444

Class Modules ...................................... 20-445

Encapsulating Similar Functionality . .......................... 20-446
Creating Objects with Properties, Methods, and Events .. ....... . . 20-462
Using Class Modules with Collections ......................... 20-470
Accessing Objects in a Collection .......................... 20-471

I Contents I


Removing Objects from a Collection ... ..... ..... ....... .. . 20-474

Using Custom Class Modules .. .......... . ................ 20-474
Review ........ ... ... . ... .. .. . . ...... . ... . ..... . . . .... ... ... 20-478

VBA for CAD Managers .............................. 21-479

Using VBA for Maintaining Standards ......... . ............... 21-479
Cross-Company Standards ... .. . . . . . ..... . ................... 21-485
Tracking Time .... ... .. . . . . .. . .... . .. .. ...... . ... . .. .. .. ... . 21-490
Drafters .. ... ........ . ...... . ........... . ....... .. .. . ... 21-490
Managers ..... . ............... . ........................ 21-490
Accountants . ... . ..... ..... . .. . . ... .. .... . ...... . ....... 21 -490
Auto-Load and Auto-Run ...... ....... ....................... 21-494
MS_VBA_OPEN_IN_MEMORY ...... . ... .. ... ... . ... ... 21-495
Protecting Projects . .... ..... .. ...... . .. . ................... . 21-498
Distributing VBA Projects .................................... 21-501
Working in High Security Mode . .. ............. . ... .......... 21-502
Review ........... ... . . ................ . .......... ... ....... 21-503

MicroStation File-Based Events ...................... 22-505

OnDesignFileOpened ..................... . .... . .. ... ... .. ... 22-506
OnDesignFileClosed .. .. ... . .... ............ ... . ............. 22-507
ISaveAsEvents Interface ........ . ........ ... .... . ............. 22-5 10
Review ... .. . .......... ... ........... .. .... .. .... ........... 22-5 17

Responding to MicroStation Attachment Events ...... 23-519

The IAttachmentEvents Interface ............................. 23-520
AfterAttach ........ . ......... .. ............................ 23-520
After Detach .. . ... . ... . ..... ..... .... . ... . .. ....... ... . .... . 23-524
AttachmentModified Event. ... . .............. . ........... .. .. 23-525
BeforeAttach Event .................. . ......... . . .. .. ... .. .. . 23-525
BeforeDetach Event ..... . ................................... 23-526
Review ................... . .... .. ........ .. . ...... ... . ... .. . 23-526

Model Events ...................................... 24-527

Review . ....................................... . . ... . .... . .. 24-530

Level Events ............................. . ........ . 25-531

The Active Event. .. .... . ..... . .. . .. . .... ... . . ............... 25-535
The AfterCreate Event .................. . ........ . ........... 25-535
The AfterDelete Event . . . ... . ... .... . .. ...... . ........... .... 25-535
The BeforeChangeActive Event ............................... 25-536
The BeforeDelete Event . .... .. . . . . . . ............... . . . . . ..... 25-536


I Contents I
The ChangeAttribute Event .................................. 25-536
Review ..... . ...... . .............. .. ... . ................... 25-536

Change Track Events ................................ 26-537

BeginUndoRedo Event. ......................... . ............ 26-537
Element Changed Event ........ . ............................ 26-539
Example 1 .............................................. 26-542
Example 2 . ................ ... ......... ... . . ...... ... . .. 26-543
Example 3 . . ... ... .... .. .......................... ...... 26-547
Example 4 .............................................. 26-548
Activating the ChangeTrackEvents Interface ..... . . .... ......... 26-549
Review .................................................... 26-550

Non-Graphical Info - Databases ...................... 27-551

How MicroStation 'link' elements to Databases .... . ..... . ... .... 27-552
Creating a Database from Scratch ............................. 27-554
Using UDL Files .............. ... ......... .. ... ............. 27-563
Linking MicroStation Elements to Database Records ......... .. .. 27-564
Creating Database Records using SQL .. . ..... . ... . ............ 27-565
Creating a User Interface to view Database Information .......... 27-566
Review ..................... . ... ... ..... . ....... . ......... . 27-569

Tags ............................................... 28-571

Getting Information from Tags based on a Selection . . . . .... .. ... 28-572
Getting All Tags in a File ............ . ........................ 28-574
Working with Tagsets ... . ... ............... . ... ....... .. .... 28-575
Getting All Tags of All Files in a Folder ........................ 28-576
Changing a Tag's Value .......................... .. .......... 28-578
Changing multiple Tags in Multiple Files . ...... ........ .... .... 28-579
Exporting Tag Information to a File . . ............ . ... ... ...... 28-580
Review ................ ........ .. . ....... ...... .... ... . .... 28-584

XML ............................................... 29-585

What is XML? ......... . .................................... 29-585
XML File Structure .................................... . ..... 29-586
Reading XML Files ............................. . ............ 29-587
Review .. ......................... .. ....................... 29-597

Batch Processing ................................... 30-599

Processing Files Listed in an ASCII File ......... ............... 30-599
Processing All Files in a Folder ................................ 30-603
Processing All Files in a Folder and SubFolders ................. 30-606

I Contents I


Creating a User Interface for File Selection ............... . ..... 30-608

Logging File Batch Processing ...... . ........ .... .. ........ ... 30-613
Using a Log File .. . ... . ...... .. ................ . . . .. ... . . 30-613
Tracking Activities with a Database . . .. . . . ...... . . . .. . ..... 30-61 5
Storing Information in the Registry . ........ . .............. 30-615
Logging Activities over the Internet. .............. . ..... . .. 30-616
E-mailing Transaction Logs . ... ... ... .... ..... ... .. .. . . .. 30-6 19
Review ... . ... ... .. . . .... . .... . ..... . ...... .. .. ... . . .. ..... . 30-621

The Standards Checker ............................. 31-623

Basics of Implementing the Standards Checker .... .. .... . ....... 31-624
Standards Check A .................................. .... 31 -626
Standards Checker Settings ....... . ... .. ..... ...... . . ......... 31-63 1
Checking for Standards . .. . . .... . .... .. ........ . ... . . . . . .... . 31-634
Where we are at this point. ... . . ... . . . ................. ... 31-634
Standards Checker Reporting . ..... ... .... . . . .. ... ....... . .... 31-639
Automatically Loading Custom Standards Checker Add-Ins . .. ... 31 -647
Review ... ......... .. . ... . ..... . ..................... . .. .. . . 31-648

Using the Windows API ............................. 32-649

Declaring API Calls . . ... ...... . ... .. ............ .. .... . ..... 32-649
Declaring Types ....... . .. .. . . .... .. .. . . ... ....... . .. ... .. .. 32-650
Utilizing API Calls ........ . ..... . . .. . ........... .. .. ...... . . 32-651
GetLogicalDrives ............. . ... . ................... . .. 32-652
GetDriveType ............... . . ... ......... .. .... .. .. . .. 32-652
GetComputerName ....... .. ............................ 32-654
GetVersionEx .................. . ....... ..... .. ... . .. .. . 32-654
Sleep . . . . . .. .. . ......... . .... . . . . . .. ..... . . .. .. .. .. .... 32-656
FindExecutable ... . ..... .. ... . . . .. . ... . ....... . . . ....... 32-656
GetD~kFreeSpace ......... . ............................. 32-657
GetSystemMetrics .. ... ... . ... . .. ... ... . ..... . ... . ... .. . . 32-658
GetTickCount ... . ....... . . . ............ .. ........ . ..... 32-659
GetUserName ............ . ........ . ........... . ........ 32-660
GetWindowsDirectory ............................... . ... 32-660
LogonUser ........................... . . .. . .. . .......... 32-661
MessageBeep .. . .......... . ... ... . . .. . ... .. ... .. ........ 32-662
PlaySound .. . . . . .. ......... .. .... . .. . .. . ...... .. . . . .... 32-663
ShellExecute . .. .. . . . .. . .. .. ... . .......... . ......... . . .. . 32-664
SHGetFileInfo . . ... . ... . ........................ . . . ... . . 32-665
Review . . .. . ......................... . . . ... . ................ 32-666


I Contents I
Using Third Party ActiveX Controls and DLLs .......... 33-667
Using ActiveX Controls ...................................... 33-667
Using Existing DLLs ......................................... 33-670
Microsoft Scripting Runtime ............................. 33-674
Microsoft Speech Object Library .......................... 33-679
Microsoft CDO for Windows 2000 Library ................. 33-680
DSO OLE Document Properties Reader 2.0 ................. 33-681
Review .............................................. ... ... 33-688

Working With Excel ................................. 34-689

Connecting to Excel .................. . ........ . ... . .... . .... 34-689
GetObject .............................................. 34-689
CreateObject ........................................... 34-691
New ........ . .......................................... 34-692
Workbooks, Worksheets, Ranges, and Cells ............. . .... . . 34-692
Cell and Range Addresses ...... . .. . ......... . . .. . ... . . ... 34-697
Working with Worksheets .......... . .. . ......... . ....... 34-702
Tag Extraction ........................ .... . . ....... .. .. ..... 34-707
Review ... . .... ... ... . ... . . . . . . . .... . .... . ... . .... . ... . .... 34-712

Working With Databases (ActiveX Data Objects) . . ..... 35-713

Primer on ActiveX Data Objects ..... . . . ..... . ..... . ... . ...... 35-714
UDL File Basics .... . .. . . . . . ... . ...... .. .... . ..... . .......... 35-716
Connections, Recordsets, and More .............. . .. . . . ....... 35-719
Recordsets . ... . ... . . . . . ...... . .................. . ...... 35-724
The Find Method . ... ... .... . .... . .. .. . .. .. ... .......... 35-727
The GetString Method . .. ...... . . ... . .. .. ..... .. . . . . . .... 35-728
AddNew and Update .................................... 35-729
SQL Essentials .............................................. 35-730
Select Statement ........................................ 35-731
Where .... . ..... . ......... . .... .. . . . . . . ........ .. . ... . . 35-732
Order By ............................................... 35-732
Extending ActiveX Data Objects .............................. 35-740
Examining Database Schema ................................. 35-745
Excel Files as Databases .... . ............................. .. .. 35-749
Review .......... . . . ... . ................................... 35-754

I Contents I


MicroStation Leveraging Mathcad via VBA ........... 36-755

A BriefIntroduction to Mathcad .. ... .. ... . .. ... .. .. . . . . .. . ... 36-756
Adding a Reference and using the Object Browser . . ............. 36-756
Basic Macros that Communicate With Mathcad ................. 36-763
Region Objects - The Basis for All Calculations .................. 36-766
The Mathcad Object Model. .................................. 36-769
Application .......... . . . . . ... . ... . ..... . .............. . 36-769
IMathcadApplication2 . . . . . . .......... . . ... .............. 36-770
Driving MicroStation Geometry from Mathcad ................. 36-771
Review ............................... . ..................... 36-782

Accessing Data from External Applications .... . ...... 37-783

ActiveX / COM Basics . . . . . ... . .. . .. .. ... . ... . .. . . ... .. . .. . .. 37-784
References, Early Binding, and Late Binding . .. . . .. .. .. . ... .... . 37-785
GetObj ect, SetObj ect, and New ..... . .... .. ... . .... . ..... . .... 37-788
When to use GetObject, CreateObject, and New ...... . ...... 37-790
What does 'WithEvents' do for us? ............. . ... . .......... 37-792
Run Macros from Excel or MicroStation? . ........ . . .. .... .. . .. 37-793
Controlling MicroStation from within Excel . ... .. ... ... ..... . .. 37 -794
Running Excel Macros ...... . ...... . .................... . 37-797
Review .. . .. . ....... . .... . ........ .. .......... .. .. . . . ....... 37-805

Writing VB6 Applications ..... . ..................... 38-807

Differences between VBA and VB6 ...... . .. ... . .. ...... . . ..... 38-807
VB6 Project Structure ..................... . ... . .............. 38-809
Controlling MicroStation with VB6 .. .. ............... . ... .... . 38-816
Creating an ActiveX Control in VB6 ........ . . . ............ 38-822
Debugging ActiveX Control Projects . .. . . . . . . .. . . . .. ..... .. 38-827
Compiling our ActiveX Control . . . . ...... .... . . ........... 38-830
Creating ActiveX DLLs . . . ....... . ... .. ... . ........ . .... . 38-832
Compiling and Distributing Applications . .. . .. . . . ..... .. . ..... 38-837
Compiling Applications ................. . ...... . ......... 38-838
Distributing VB6 Applications ...... . .... . ... ... . .. . . .. ... 38-839
Review . ... .... .. . .. . .. . . ... . .. .. . . ... . .. . .. ... .. .. .. . .. .... 38-847

Using VB.NET . . . . ... . .. .. . .. . .. . .. . .. .... . .. . . .... . 39-849

VB.NET Introduction .. .. ....... .. .. . . . ............... . . . ... 39-850
You can do this in VB.NET! .. . . . . . ..... . ... . . . . . ..... . ....... 39-856
A DGN Browser Application .. . ...... .. ..... . . . ....... . ...... 39-862
VBA to VB.NET Reference .. ... .. . . . . .. ........ . .. . . .. ..... . . 39-866
Everything is an Object ... . ... .. . ... .... . . . ............ . . 39-866
Overloaded ... . ...... ... ... ..... . .. ..... . .. . . .... . .. .... 39-867
Procedures and Functions .. . .... . .. . ...... . ... . ... . ...... 39-868


I Contents I
Accessing ASCII Files .................................... 39-868
Traversing a Folder and its Subfolders . ......... . .......... 39-870
Getting All Files in a path ............................. . .. 39-87l
Returning Function Values ............................... 39-872
Windows API Calls ...................................... 39-872
Distributing VB.NET Applications ............................ 39-877
Review ............... ......... ............................. 39-880

Additional Sources ..................................... 881

Index ................................................. 883

Learning MicroStation VEA provides an in-depth tour of one of
MicroStation's most powerful customization abilities. The book starts by
supplying the foundation for understanding VBA basics and then shows
how to apply the fundamentals to real-world situations.
Learning MicroStation VEA provides full coverage of the VBA subject taking you through the basics like the editing environment, modules,
visual interface, and MicroStation object model through advanced
topics like the Windows API, interacting with other applications, and
Visual Basic, among many other things.

Whether you are a MicroStation user who simply wants to make your
job easier or an experienced programmer who wants to master the
nuances of MicroStation VBA, this book is an invaluable resource for
learning MicroStation VBA.
The following type styles are used in this book to distinguish various
Filename or URL

Menu and menu items

Fu nct io n


Fu nctionlndex


Keyboard key



I Introduction I


The accompanying CD includes all source code referenced in each

chapter of the book. The CD also includes procedures, and addenda to
the book as well as a comprehensive Object Model listing and other
example files such as V8 DGN files, Microsoft Excel spreadsheets,
Microsoft Access databases, and more.

The Bentley Institute develops and delivers professional training
programs that are designed to increase the productivity of AEC
professionals. Attend accredited courses at Bentley Institute training
facilities around the world or train on-site at your office location. Train
without travel in virtual classrooms through instructor-led distance
learning and learn any time though OnDemand eLearning. To learn
more, go to


Jerry Winters began his CAD career as many have, at the bottom of the
totem pole, drafting eight hours a day. It didn't take long for him to
discover that in many situations, the computer could complete repetitive
tasks much faster than he could. So, he began writing programs that not
only simplified the drawing creation process but significantly decreased
the amount of time needed to create drawings. Rather than wasting the
time saved by his programming efforts, Jerry used the new found time
to write more programs until he stopped 'using' CAD software and
began 'customizing' CAD software on a full-time basis. So, for the past
15 years, Jerry Winters has been customizing CAD software and
teaching others to do the same.
Whether it's on stage or in the written word, Jerry brings occasionally
complex programming topics down to the level of the average CAD user
(in part because he considers himself an average CAD user). His
extensive knowledge of Visual Basic programming is complimented
with Active Server Page development, database programming expertise,
and the occasional creation of Java applets for graphically-rich webbased development.

I Acknowledgments I


This is his first book on customizing MicroStation with VBA and he

experienced one difficulty throughout the entire book. "There is just so
much that can be done in MicroStation's VEA environment, it is
difficult to know what to include and what to shelf for a later date:'
Jerry and his wife Candice are the parents of six children. They live in
Lake Point, Utah, where they raise their children and their children raise

I would like to thank the Technical Review Committee of Mark
Anderson, Phil Chouinard and Robert Hook, as well as the Bentley
Institute Press Team of Gilda Cellini, Frank Conforti, Lissa Jennings,
Drew Knox, Scott Lofgren, Maureen Rhoads, and Christopher Rogers,
without whom this book would have never gotten off the ground. I
would like to sincerely thank Graham Steele and Rudi Wells for
reviewing the material in this book and helping to ensure that it is ready
to put into the hands of any MicroStation user.
Furthermore, I would like to thank the Bentley Institute fo r affording
me the opportunity to write about MicroStation's implementation of
VBA. I hope the lessons learned in this book will be as rewarding to the
reader as they have been for me.


I Introduction I

Introducing VBA
[B What is VBA?
[B Why should we learn it?
[B When should we use it?
[B How do we use it?
[B What does it look like?

These are five very good questions and they deserve answers.

VBA is an abbreviation for Visual Basic for Applications. Microsoft
licenses VBA to companies such as Bentley Systems, Inc., so users can
customize the company's software. Then, companies that develop
world-class software, such as MicroStation, can give their customers the
best set of tools available. These companies know that one way to
accomplish this goal is to empower customers to modify and
personalize their software to meet individual needs.

I Chapter 1: Introducing VBA I



Learn VBA to rapidly develop programs that meet your individual

needs. Much of what you learn in MicroStation's VBA environment can
be used in other VBA environments. The first two-thirds of the VBA
abbreviation is "VB:' Visual Basic includes both the Visual Basic
programming language and its programming environment. "/\.' finishes
up the final third of VBA. The "/\.' is the Application-specific Objects
and Application Programming Interfaces (APIs).
If we think of VBA as being two-thirds Visual Basic and one third
Application, we could state that two-thirds of everything you learn in
this book is directly applicable to other VBA environments. For
example, if you learn MicroStation VBA, you would be 2/3 of the way to
knowing Microsoft Excel VBA. And this is not far off. So, in addition to
being able to customize MicroStation to meet your needs, learning VBA
allows you to leverage other VBA-enabled applications.


Only under the direct supervision of an adult?
Between the hours of 8 AM and 5 PM?
Holidays? Weekends?
The real question you should be asking yourself is, "Can the program I
need to write be written in VBA?" If the answer to this question is YES,
then it should probably be written in VBA. And as you learn more about
VBA, more and more often the answer to this question will be YES!


MicroStation VBA programming is stored in files with an .mvba
extension. To run any of the code in one of these MicroStation VBA files
you must first load the file. Before we go any further, let's create a new

I How Do We Use VBA? I

drawing file named Introduction.dgn. Go to the MicroStation menu

Utility> Macros> Project Manager to open the Project Manager.

Connect 'iiJ!b Browser

HJ.t.t. Author ...

Au~iary Coordinates
Sa... ed~ews

Named GrO!:J)s

We use the Project Manager to begin new VBA projects and open
existing VBA projects.

D~ oal ~"" : 11

In this dialog box, click the Load Project button. Now, browse to the CD
included with this book for a folder named "MVBA Files': In this folder

I Chapter 1: Introducing VBA I

you will find a file named Introduction.mvba. Select this file and click
the OK button.
,.--------- --E!le Qjl'ectory


D:\MVBA Files\
~ MVBAFiles

This loads the .mvba file into MicroStation and displays it in the VBA
Project Manager.

Introduction VSA Introduction

C:\Program Files\8 entley\ ... \D efault.mvba

D:\MVSA Files\lnlroduction.mvba

Opening an MVBA file does not close the Project Manager. The Project
Manager remains open until you click the Close button in the upper
right-hand corner of the dialog box.
Now that we have loaded an .mvba file, we can run some code. How do
we do it? There are a few ways. Let's begin by running code from within
the VBA Project Manager. If the VBA Project Manager (VBAPM) is
closed, follow the instructions above to re-open it. Make sure to load the
Introduction.mvba file. In the VBAPM, select the VBA Project
Introduction.mvba. Now look at the top of the VBAPM for a triangle
that looks like the play button on a VCR. This is the Run Macro button.

I How Do We Use VBA? I

When you click it, the Macros dialog box opens, which allows you to
select which macro (procedure or function) you want to run .

. Run
. Cancel





M~rosin; I<~II St~!1daL~'proie_c.t ~~.



Select Pr ocedu r eA from the list of macros and click the Run button. The
macros dialog box closes and a diagonal line is drawn in the active
model. Proce dureA draws a line from (0,0,0) to (10, 10,0) in the active
file. If the macro is run and the line is not visible, use the Fit View button
to zoom the active view to display all of the contents of the file.
Remember, the steps to running an MVBA macro are:

1 Load the MVBA file using the VBAPM (VBA Project Manager).
2 Select the project in the list of projects.

Click the Run Macro button in the VBAPM, or click the

MicroStation menu Utilities> Macro> Macros, or hold down the
<ALT> key on your keyboard and press <F8> key.

It's a three-step process. Of course, if the .mvba file is already loaded,

You do not need to load it each time you run the macro. You can run a
specific macro by using one of three methods described above in Step 3.
You have just run a macro using the VBAPM. Now run one by using the
<ALT + F8> keyboard shortcut. Hold down the <ALT> key and then
press the <F8> key to display the Macros dialog box. Select Proced ureC
from the list and click the Run button. ProcedureC draws a square using

I Chapter 1: Introducing VBA I

lines from (0, 0, 0) to (10,0,0) to (10, 10,0) to (0, 10,0) and finally back
to (0, 0, 0).
That's all there is to running a MicroStation VBA macro. Load it and
run it.


Here is the VBA code behind that macro:
Sub ProcedureA ()
' ********

This Procedure draws a line from (10.10.0) to (30.10.0)


Dim Sta rtPoint As Point3 d

Dim EndPoint As Poin t3d
Dim MyLine As Lin eEle ment
Sta rtP oint.X = 0
St artP oint.Y = 0: StartPoint.Z = 0
EndPoint.X = 10: En dPo in t . Y = 10: EndPoint.Z
Set MyLine = CreateLineElement2(Nothing. _
Star tPoint. EndPoint)
ActiveModelReference.AddElement MyLine
End Sub

VBA Projects are broken up into blocks of code called procedures,

functions, and events. Each block of code has a name. The procedure
shown above is named ProcedureA. Comments in the code begin with an
apostrophe. Everything after the apostrophe is part of the comment.
Variables are declared with 'Dim' statements and are then assigned
values or set to objects. Code in procedures, functions, and events runs
from top to bottom. Together we will write a large number of functions
and procedures as we study MicroStation VBA.
Back to the code in ProcedureA. It does not have a graphical user
interface - it is just code. Writing code is one part of VBA

I Review I

The other part of VBA development is the graphical user interface

(GUI) , such as buttons, text boxes, and labels.

. . start Point .-. .- ...... . . . . . . : . . End Point -..- ... , ....... .. :

:: X" : I

::: :: x' ': !

: ~ :z":1

:::::z" :!

.:: ::y":Ir------::: ::v": 'I'" 'I- - - -"

!I' !.

li t

Some of the applications we write will have no GUI, but we will also
explore the visual side of Visual Basic.

VBA projects are contained in .mvba files. Each file contains code and
can also contain graphical user interfaces. Load and unload VBA
projects using the VBA Project Manager. After the code is written, You
run VBA projects and the code they contain by using the MicroStation
menu Utilities> Macro> Macros ... or by pressing <ALT +F8> on the
Learning VBA is very much like learning a new language. It requires
patience and time. Keep this in mind as we continue to study together.

I Chapter 1: Introducing VBA I

The VBA Project

You have already seen how to display the VBA Project Manager.
Remember, go to the MicroStation menu Utilities > Macro > Project
Manager. We used the Project Manager to load a VBA project and run a
couple of macros contained in that project. Let's take a more
comprehensive look at what the Project Manager can do for us.
[8 The Project Manager gives us the ability to load existing VBA

[8 The Project Manager allows us to run the procedures and

functions of projects that have already been loaded.

[8 Start new VBA projects using the Project Manager.
[8 Unload VBA Projects that are already loaded.
[8 Save loaded VBA Projects to a new location and/or a different

file name.
[8 Enter the Visual Basic Editor from the Project Manager.
[8 Record macros from the Project Manager.
[8 Auto-Load VBA Projects so projects are loaded each time

MicroStation is started.

I Chapter 2: The VBA Project Manager I


We can use the MicroStation menu to display the VBA Project Manager
or we can hold down the <ALT> key and press the <F8> key to display
the macros that are loaded and are ready to be run.
We need to be careful when discussing the VBA Project Manager. The
term 'Project Manager' is so generic it could be confused with other
products or functionality. For brevity we will refer to the VBA Project
Manager as the VBAPM from time to time throughout this book.
Now that we have identified the VBAPM's functionality in general it is
time to examine it in greater detaiL


The following graphic shows the VBAPM with its elements identified
with leader lines. We will refer to the image during the remainder of this

Begin a New VBA


You are prompted for the location of the new .MVBA file
and for its name.

Open an Existing
VBA Project

Select an existing .MVBA file to load into the current


(lose VBA Project

This button is enabled only when an existing project is


Save Project As ..

When an existing project is selected, you can save it to a

new location and/or a different file name.

Display the VBA


All saved VBA projects and new projects are edited from
within the VBA Editor.

IVBA Project Manager Functionality I


Run Macro

Click th is butt on to d isp lay th e Run Macro dialog box

wh ere yo u se lect and execute specific macros.

Record Macro

Enabled when an existing project is selected. When

activated, activities in MicroStation are recorded to a
macro in the selected project.

Stop Recording

Enabled only when actively recording a macro. When

stopped, the macro is placed in the selected VBA Project
under the name "Macr01'; "Macr02'; Macr03'; etc. The
Macro Recorder automatically names the macros. You
can rename recorded macros in the VBA Editor.

Pause Record ing


Pa uses the record ing of a macro and togg les between

Pause / Resume Recording.


Sets a specific VBA project to automatically load each

ti me Mi croStation is opened . When you click in the
column, a checkmark indi cates th e fil e is set t o AutoLoad.

We have just identified ten things that you can do directly from within
the VBA Project Manager. One of these is "Run Macro" which, rather
than actually running a macro, displays the Macros dialog box.


I Chapter 2: The VBA Project Manager I



We use the Macros dialog box to select a macro to run but we can do far
more in this dialog box than just running a macro. We can "Step Into" it.




Macros in:


I<All- Standard
. ...- Projects>
_._.... -_._.....


............-.- ..

Step Into executes the macro in debug mode stepping through the code
one line at a time so we can see how the code is executing, what values
are stored in variables, etc. It is one of the best features of VBA, whether
you are a novice programmer or a seasoned developer.
The Edit button takes us into the VBA Editor window with the cursor
on the top line of the selected Macro.
The Delete button deletes the selected Macro from the VBA Project.
This is a very dangerous button. After all, there is no Undo button
displayed in this dialog box. Is there? Use with care.
Macros in: lists VBA projects. If you select <All Standard Projects>, the
Macro list displays all executable macros from all loaded VBA Projects.
Selecting a project filters the Macros list to display only those in the
selected project.
The Description area allows us to type in a description for a selected
macro. This is a nice feature because we are given the ability to provide
more information than by using the macro name only.
For example, we do not need to name a macro,

I Review I


Draw_A_L i ne_From_O_O_O_to_lO_O_O. We can name it ProcedureA and

enter a description in the Macros dialog box.

We have covered every button in the Macros dialog box except for one,
the button that is grayed out in the image above. You use the Create
button to create new Macros. It's simple. Here's how it works:
As you select macros in the Macros list, a TextBox just above the list box
displays the macro selected name. If you change the text in the TextBox
to a macro name (Procedure name) not already shown in the Macros list
box, the Create button is enabled to begin a new macro with the name
specified. So, if you type ProcedureA_l into the TextBox and click the
Create button, a new Procedure named ProcedureA_l is created. Of
course, no code is entered into the procedure after it is created. That is
our job. We can now select ProcedureA_l from the ListBox, click the Edit
button, and go into the new procedure in the VBA Editor to begin
writing code.

The VBA Proj ect Manager is useful for perfo rming a number of tasks.
Among them are:
III To load and unload VBA projects (MVBA files) .
IB To save existing VBA projects to new files and locations.
III To begin new VBA projects.
IB To record macros into existing VBA projects.
III To use Auto-Load to automatically load VBA projects within
III To enter the VBA Editor from within the VBAPM.


I Chapter 2: The VBA Project Manager I


Open the VBA IDE PDQ!!! Yes, the IDE is WYSIWYG. GM?
Translation: Open the Visual Basic for Applications Integrated
Development Environment Pretty Darn Quick!!! Yes, the Integrated
Development Environment is "What You See Is What You Get". Got
The VBA IDE is where we do our VBA programming work. As with
most Windows programs, the VBA IDE is composed of three elements:








I Chapter 3: The VBA IDE I

i E.ila











Nearly all Microsoft Windows applications utilize Menus to issue
commands. Many menu items have shortcuts. For example, holding
down the <CTRL> key and pressing the <P> key does the same thing as
selecting File> Print from the menu. Whether you click your mouse or
use the associated shortcuts, it is helpful to know what a menu item
does. Let's look at the menu items that are available in VBA.

File Menu
imports existing
form (.frm), module (.bas), and
class (.cls) files into our project.
[B Import File

l! import File ...
r;;Xport File .. ,



exports forms,
modules, and classes from our
~Iose and Return to Micro5tation Alt+Q
project to their own .frm, .bas,
and .ds files. After these files have been exported, they can be
imported into another project.
gj E,rint .. ,


[B Export File

removes forms, modules, and classes from our project.

When we attempt to remove an element from our project we are
asked if we want to export it (save it) first.

[B Remove

I Menus I


[B Print allows us to print code and/ or forms.

[B Close and Return to MicroStation hides the VBA environment

and gives MicroStation focus.

Edit Menu
[B Undo and Redo are standard

Windows menu items.

;1 Can'l: Und"


[B Cut, Copy, and Paste perform

standard Windows Clipboard



[B Select All selects all text when in a

Code window or all controls when

in a User Form.
[B The Find, Find Next, and Replace

menu items perform standard Find

and Replace functions.

I ~()


[B Clear deletes selected text or



Can't Redo


,: ttl+X



e.'~$ bE!




Select all


~l ::~:'~;ext


,l R~DI~~8' "

, i -,.--,-,---,--,--,-,,------,-,,--t:~1 ~ndent

~~lj ~~~

__~hift:Tab _

~"l list Propertiesl~letb.ods

Lt, List Constants

i1kj Q:ick Info

Ctrl+ J

Ctrl+Sh,ft+ J

iit',l Parameter Info


A~'l Complete 'liard


[B Indent indents the selected code by

I ~ookmarks

one tab to the right. Outdent (is

Outdent a real word?) shifts the selected code by one tab to the
[B List Properties/Methods displays the Properties/Methods list.
[B List Constants works with an API call that utilizes constants. A

list of the applicable constants is shown.

[B To use Quick Info, set the cursor on a variable, an object, or an

object's property or method and then click Quick Info to display

the type of object or variable on which the cursor was placed.
[B Parameter Info displays information about the Method the

cursor is over.
[B Complete Word shows the list of Constants and Methods in

VBA so we can select something from the list.

[B Bookmarks sets and removes bookmarks in our code and

moves from bookmark to bookmark. A bookmark is a flag that


I Chapter 3: The VBA IDE I

lets you quickly jump to a line of code. Bookmarks are not saved
with .mvba projects.

View Menu







Last P05itio!l


~ Qbject Browser


[mmediate Window

Local~ Window


Watch Window


Stac~" .

~. E,roject Explorer
~, Properties Window

"~ TOolbO~


Ctri+ L


IB When looking at a user form ,

click Code to jump to the code
behind the form .
IB Obj ect displays the form
associated with the code we are
looking at.
IB Click on Definition when the
cursor is over the item you want to
look at to quickly display where the
variable is declared or the method
is defined.

Tb Order

3. MicroStation


IB Last Position moves the cursor

to the previous line of code the
cursor was in.

IB Object Browser, Immediate Window, Locals Window, Watch

Window, Call Stack, Project Explorer, Properties Window, and
Toolbox display a window with the same name.
IB Tab Order displays the Tab Order properties of controls so we
can see the order in which controls receive focus when the user
hits the Tab button in the form.
IB Toolbars toggles the display of the Debug, Edit, Standard, and
User Form toolbars.
IB Click on MicroStation to bring the MicroStation window to the

I Menus I


Insert Menu
[B Procedure displays the Add Procedure

rnsert '--_ __

~j ~~::::~:e . .

dialog box to begin new procedures. This

dialog box is most useful for creating new
Properties for Class Modules.


~I =Iass ~lodule

'-Ile ...

[B UserForm, Module, and Class Module

inserts these new objects into our project.

[B One way to speed up our development is to reuse code that has

already been written. If we place code in ASCII Text Files, insert

snippets into our project by clicking File and then selecting the
file to insert.

Format Menu
Make Same Size
'X 'f

Size to


Size to Gri!J.
tlorizontal Spacing

Ilertical Spacing

Use the Format menu to perform standard

formatting when editing a User Form.

in Form

Atr ange Buttons






Debug Menu
Compile Default

The Debug menu allows us to

perform debugging operations
on our code. We will cover this
functionality in Chapter 9,
"Standard VBA Calls:'


Step into









add Watch .. .


W.,tch .. .

Quick Watch .. .


-t:.~ loggle Br~ akpoint


gear All Breakpoints


1Set N>3 <t statement


Sholl'" Ne~t 5tat~ment


I Chapter 3: The VBA IDE I

Run Menu

Run Macro








Design t10de

IB Run Macro, Break into, and Reset

code execution by using these menu
items in the Run menu.

IB Design Mode is a standard VBA

button that does nothing substantive in
the MicroStation implementation of VBA.

Tools Menu
IB References allows us to add a
reference to existing DLLs and type
libraries. For example, if we want to
work with Microsoft Excel, we can add
a reference to the "Microsoft Excel
Object Library". Doing so makes
working with Excel in VBA very easy.

~ B.eferences ...
ill ' additional Controls ...
t1acros ...
'i - - - - --1
Qptions ...


Def ault

Prop~I'ties .. .

...; QJgital Signature...

IB By default, 14 controls display in the toolbox for use in our

forms . We can add more controls by selecting the Additional
Controls menu item.
IB Macros displays the Macros dialog box where we can create,
edit, run, delete, and debug macros in our project.
IB Change preferences such as font size, tab width, and grid
settings by clicking on Options.
IB The Properties menu item displays the properties for the active
VBA project. In the image shown above, the project is named
IB Digital Signature allows us to sign our VBA projects. This
assures end users that the code they are going to run is created
by a specific company.

Add-Ins Menu
Third party developers can create add-Ins for
VBA. Add-In Manager displays the Add-In
dialog box where we can set properties for
available add-ins.

I Menus I


Since we will not be discussing Add-Ins anywhere else in this book, here
is a snapshot of the m anager with an add-in that has been loaded. Addins can be loaded based on the "Load Behavior" settings.

Available Add-Ins

r. VBA-Add l~f~;"~li';;'Stati~n X~r

' Load Behavior


Stait~p 11];;r;,~ded-'



L -_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

. _ ________ _
Add-In created for MicroS tation VBA
.A !

.g,~sc lipti on

~: ~[

- tl-elp- - '

Load Behavior ..-

::.J ["oadcd/lJnio.,ded
CJ Lo~d on 5.1altup
.. ........-- .... ,)

Window Menu
'H_.ll_ _ _--,

Tile tiorizontaliy
Tile 'lerticaliy

These are the standard menu items

available in nearly every Microsoft
Windows program.


8,rrange Icons

Help Menu
!B We will cover Help issues in the
next chapter. One way to get there
is by clicking the Microsoft Visual
Basic Help menu item.

Help .'--_ _ _ _ _ _ _-,

ill-Microsoft Visual Basic tielp


8,bout Microsoft Visual Basic, , ,

!B About Microsoft Visual Basic displays the About dialog box.

I Chapter 3: The VBA IDE I


Toolbars offer a very quick way to issue a command. One click is usually
all it takes to get things started. Compare this with at least two clicks to
issue the same command using a menu and we can instantly double our
CIP (Command Issuing Performance). As a general rule, all commands
issued by clicking on a toolbar icon can be issued from the menus.
It can take a little while to become familiar with toolbar icons. Until you

learn what each icon does, hold your cursor over an icon to see what the
icon does.

Standard toolbar

The Standard toolbar is very, very important. Why? Because the only
way to save the changes we are making in our VBA project from within
VBA is to click the Save button. We cannot Save changes by using the
menu. We must use the Save icon in the Standard toolbar. And please,
please, please, my friend, save your project often. There are few things
worse than spending a couple of hours working on a project only to have
something silly like a power outage or a fatal error cause you to lose all
of that work.

.~View MicroStation-1 Notice how holding your cursor over an icon displays the icon's tool tip .

n l' ~

:- '@J i:.Ja '"


We could show each and every button on every toolbar but that would
be a bit of a waste because you can move your cursor over the icons to
see what they do.

Edit toolbar
The Edit toolbar displays
functionality found in the
Edit menu.

~i~,,-~~,"~ili~~1:!1I; ~~J~A~"

Debug toolbar

The Debug toolbar displays the functionality found in the Debug menu.

I Windows I


UserForm toolbar

"1 J:) ~

... .

0(: ,i."

\ ~ ;"';,T

lfll I;'. ~ ca . lfioii~;~ ----:':]

The UserForm toolbar exposes

functionality found in the Format

Use the toolbars and menu items to display and hide VBA windows.
Let's take a look at the VBA windows we will be working with on a
regular basis.

Project Explorer
The Project Explorer displays the top-level
objects in the loaded projects.
L... EID UserForm!

8"es Modules

\.. ~ Module!

a. @j Class Modules
'. '@I m!D1

In this project we have a form named

UserForm 1, a module named Module 1 and
a class module named Class 1. The view
shown uses folders to group the common
types of objects.

Click on the Folder icon in the

top of the Project Explorer to
turn off Folders and display the
objects in alphabetical order.

;....@1 Class!
;.~ Module!

; .. I UserForm!


I Chapter 3: The VBA IDE I

Object Browser

(; <g lobals>
@J Ac cuDrawHints
@J ACSManager
@J Application
@J ApplicationElement
@J App licationObjectCo
@J Arc Element
@J AreaPattern
@J Atta chment
@J Attachments

ActiveDes ignFil e
ActiveMode lReference
AddAttachmentEventsHand ler
AddLevel ChangeEventsHandler
AddMode lActivateEventsHandler

The Object Browser gives us a way to explore the objects (classes)

loaded into the current VBA project. In addition to the objects
themselves, we see a list of properties, methods, and events associated
with each object under the "Members" list. The gray area at the bottom
of the window gives us the declaration of the selected method, property,
or event. We can display All Libraries or select a specific library. We also
have the ability to search the loaded libraries.
We will cover the Object Browser in more detail in the next chapter.

I Windows I


Properties Window
Objects in VBA have names. For
example, this user form has the name
UserForm 1. We can use the
Properties window to change this
form's name, color, and other
properties. The Properties window is
used extensively when working with
forms and controls on forms.

Watch Window

ModelReferenceilvlodelReference UserForrn1 .Cornrnand811tton1_Click


UserForm1.Cornrnand811tton1 _Click


UserForrn1 .Cornrnand8uttonI _Click

AttachedCeliLibr6 <No cell libra Cell Library

"graph paper Siring

UserForrnl .Cornrnand8utton1_Click
UserForrn1 .Cornrnand8utton1_Click
UserForm1 .Cornrnand8utton1 _Click
UserForm1 .Cornrnand8utton1_Click
UserFonn1 .ComrnancI8utton1_Click

CursorlnforrnationiCursorlnformati UserForrn1 .Cornrnand811tton1 _C lick

UserForrn1.Cornrnand811tton1 _Click

"C:'Prograrn I String
HasActiveDesign True

UserForrn1 .Cornrnand8utton1 _Click

H asAct iveMode l~


UserForrn1 .Corn rnand8utton1_Click






IsAcadernicVersi, False


UserForrn1 .Cornmand8utton1_Click

IsCeliLibraryil.ttac False



The Watch window is a favorite among VBA developers. It allows us to

watch the value of a variable, object, or property. As we can see above, I
added a watch to a variable named MyApp. This variable points to the
MicroStation Application. Take a look at all of the properties with which
we can work.


I Chapter 3: The VBA IDE I

Locals Window

No cell library>


"chapjer03.dgn (2D -

va Dor string



The Locals window looks a lot like the Watch window. There is one
primary difference however. To look at items in the Watch window, you
must add a watch to the item. The Locals window automatically displays
the variables declared in the active procedure or function along with
each variable's type and value.

Immediate Window

The Immediate window does a couple of things for us. First, it allows us
to display text as our code executes. When we use the following code
Dim MyApp As Application
Set MyApp = Application
Debug.Print MyA pp.Capt ion

the caption of the MicroStation application is printed to the Immediate

window. For this reason, it is also called the Debug window.
The other thing the Immediate window does is it allows us to execute
code immediately. For example, we can type
MsgBox "Learning MicroStation VBA "

I Windows I





in the Immediate window and press the <Enter> key. When we do so, a
MessageBox displays.

Call Stack Window

Call Stack




As we step through our code line-by-line or break into our code as it is

executing, the Call Stack window shows us where we are (the top line),
where we started (the bottom line), and how we got there (all of the lines
in between). There are times when one procedure calls another
procedure which calls a function which calls a procedure. Knowing how
we arrived inside a procedure or function can help us debug it.

Toolbox Window
The Toolbox window displays the standard
controls that can be placed on our user
forms. It only displays when a user form is
the active window in VBA. If we are working
with a user form and the toolbox is not
visible, click on the Toolbox icon in the
Standard toolbar or go to the View >
Toolbox menu to display it.

~ Aabl~1ffiI

..!..l:..:J ~.!l~

All of the windows discussed so far are dockable except for the Toolbox.
This means they can be snapped to the bottom, top, right, or left
window of the VBA IDE. These windows' dockable property is set in the


I Chapter 3: The VBA IDE I

Docking tab of the Options dialog box. To view this, go to the Tools
menu in VBA and select Options.

I~ I

Editor Format

r Docl<3ble -


I! General I Docking ]'------------ - -

.---- -- -- --- ---- -

I ~ii~iii:~~i~t.i.Wi6~9.0.:
I ~ L.ocals Window
I ~ ~atch Window

Ii ~

!:.roject Explorer

I ~ Ptoperties Window
I ~ Qbject Browser




r - OK-- I (




_ Help

If an attempt to dock a window fails, look at the Docking property of the

window in the Options dialog box and turn on Docking for the specific
window you want to dock. As with many applications, these windows
are docked by dragging the window to the edge where you want the
window docked. They are un -docked by dragging the window away
from the edge where the window is currently docked.

Other Windows
There are a couple of additional windows in VBA we should discuss. As
we have already discovered, VBA projects are composed of forms,
modules, and classes. Each of these elements has its own windows.

Here is a Form.

CommandButton I

----....---.-1 __,_,_,._,,_,_





. ... .


. . ., .. . .. . . .
- . ... . . . . . . . . .. . , ... ... . , . .. . . .
. . . ,. . .. ..
.... . . . . ., . .
. ... . . . . . .
... . , . . . . .. .. . . . . ..... ., .. .... ..
.... , ...

, '-

I Windows I


A single CommandButton has been added to this form. This form

window allows us to place controls on it. Remember, the controls are
placed from the toolbox to the form .
What happens when we are running this form and the user clicks the
button? Code is executed. Double-clicking the button takes us to Code
window behind the form. Another way to see the code is to right-click
on the button and select View Code.

This is the C1i ck

Event of

CommandButton 1.


Code takes us to the default

event of the control we

right-click over. So, here we
can see the C1 i ck Event. Are
there other events we can
work with? How do we see
them and write code In

Dim !{yApp As Application

Set !{yApp = Application
Debug . Print !{yApp . Caption
Dra1JL ine

Take a look at the top of the

Code window. There are two
ComboBoxes. The left one
contains the controls and
objects available in this Code
window. The right one
contains the events we can
work with. Selecting an event


I Chapter 3: The VBA IDE I

from th e right ComboBox takes us into the code for that event.
Open Module (Code Module) and Class Module windows by doubleclicking on their icons in the Project Explorer or by right-clicking on the
icons and selecting View Code. They look exactly like the Code window
shown above.
We have spent a few pages discussing the VBA IDE. As we continue
learning MicroStation VBA, we will discover other facets of the IDE and
get into more detail.

The VBA IDE (Visual Basic for Applications Integrated Development
Environment) is where we do our programming, i.e. writing code and
creating user interfaces. As you become more familiar with this
environment, you will be able to develop your programs much more

Finding Help
Finding help can be one of the most difficult aspects of learning a new
programming language. Why? If you have a question for someone, you
can converse with them until the question is clear. That's easy. When
learning VBA, however, you don't always know what to ask. For example
you might ask, "How do I put something on a form that forces a user to
enter a numeric value?" If you could ask a VBA guru this question, you
will get a straightforward answer. Working through a Help file for the
answer is different. For starters, even if you know what to ask but you
don't know the correct terminology, you won't find the answer.
Distressing? Yes. Frustrating? Definitely. The end of civilization as we
know it? No.
This book is targeted toward helping you learn MicroStation VBA. It is
filled with code samples and explanations but it does not contain every
answer to every possible question. Here a few things that will provide
help when you need it.
In this chapter:



Help Files


The Net


The Object Browser



I Chapter 4: Finding Help I

Thingy. Dilly Whopper. Whatchamacallit. Gizmo. Whether we are
asking a person or a computer, these words will get us nowhere. How
can we get a little closer to the right keyword?
Let's begin by looking in VBA.
Holding your cursor
over a variety of objects
Controls I
displays a Tool Tip.
r ~- A abl ~~ P" r.: ;: L1:-'...!.l~:;%JQI
There is a ComboBox
icon in the Toolbox. If
Combo Box we are
more likely to find answers than if we ask about a DropDown. Both
combinations of words may make sense to us, but using the correct
name for the control gets us closer to finding answers to our questions
than using terminology that, although descriptive, is not correct.




- -





Since the terminology used in VBA may be foreign to you when getting
started, it is a good idea to make notes or highlight areas of this book
and other resources when you come across a word or phrase you want to
remember or that you may want to be able to find quickly at a later date.
For example, if you are asked to provide a string, you may produce a
piece of flexible material useful for restricting blood circulation in one's
index finger with the intent of reminding you of something. As for me,
I'm just as likely to forget the string is tied around my finger as I am to
forget why the string is there in the first place. What does this mean?
Before long we are collectively fingedess. Or is it finger-free? Digitless?

ITerminology I


What is a string when it comes to VBA? It is a type of variable that can

hold text, numbers, and other characters. "This is a test" is an example of
a string. Since the word string is very different when dealing with VBA
when compared to Benjamin Franklin's experiments with electricity,
highlighting the definition of a string type variable in a book may be
In addition to highlighting existing text, get out a pen or pencil and
write in the margins of this book.
"Chicken Soup for the VBA Programmer:' "The VBA Word of the Day:'
"The VBA Programmers Daily Calendar:' These products may not exist
but they could be very helpful. Why? Frequent and regular exposure
reinforces retention.
If your goal were to obtain a dark tan, you would want regular exposure
to the .sun. If your goal were to bake bread, it would be out of reach if
you rarely stepped inside a kitchen. "Practice makes Perfect;' "No Pain,
No Gain;' and "If at first you don't succeed, try, try again:' We have
heard these statements over and over again. Each of them reminds us
that frequent and prolonged exposure and practice is usually necessary
for success in any endeavor. While you don't need to write a thousand
lines of code every day to learn VBA terminology, 20 lines of code every
day will do more than 100 lines of code only on Monday mornings.
If you expose yourself to VBA frequently, the terminology becomes
familiar and you will be in a better position to find the help you need.


I Chapter 4: Finding Help I

One way to display the VBA Help File is to go to the Help menu in VBA
and select Microsoft Visual Basic Help.

Locate P;eviou ~ ~iext
Back Forward Stop
Refresh Home
-~---------'-----",---'----------------:""'I: I

J;;ontimts ' llnd-;--lr~~~~hrFavo;~~l

- -

Visual Basic

~ ~
~ Visual Basic Conceptual Topics
Visual Basic HowTo Topics
Visual Basic Language Reference
Visual Basic Addln Model
~ Microsoft Forms Reference

tt ll .




We lcome to the Visua l Basic documentation.

. !I'

Visual Bas ic in cludes ma ny docume ntatio n too ls,

each designed to he lp you learn and use a
particular aspect of the product. The documentation
provided with Visua l Bas ic includes the following:


Visual Basic User Interface He lp

Look he re for He lp on interface el ements of the
Visu al Basi c Editor, such as command s, dial og
boxes, win dows , and toolbars.
Visual Basic Conceptual Topi cs
The Conceptual Help top ics include informatio n
to help you understa nd Vi sual Bas ic
programming .
Visual Basic How-To Top ics
Look in the Ho w To section of He lp to find usefu l
common pro cedures, for examp le, how to use
the Object Browser or ho w t o set Visua l Basic
Environme nt options.
Vi sua l Basic Lang uage Reference
The Lan guage Reference is t he pl ace to fi nd
He lp on Visua l Bas ic the lang uage : all its
methods , properties , stateme nts , functio ns ,
op erato rs, and objects .


It is filled with a large amount of information but also gives us the ability
to organize our own unique help file by using of the Favorites tab.

So, you want a little help with a ComboBox? Let's begin in the Contents
tab and drill down to the ComboBox starting with the Microsoft Forms

I Help Files I


Contents tab




















~o';ients I Index

I Search I Favorjtes I


Il Visual Basic HowTo Topics

Il . , Visual Basic Language Reference
Il . , Visual Basic Addln Model
8 (QJ
.. I,
. ! i[
~ Mrcrosoft Forms Obl~ct Model Overv" ",:1

Il . , Mrcrosoft Forms Desrgn Reference

Il Microsoft Forms Developer TiPs
El (J;lJ Microsoft Forms Object Model Refere, : ..J
Il . Events
'Il . Methods
8 (QJ Objects, Collections, and Controls
~ CheckBox Control
~ ComboB ox Control

1 i1!:~ > , ' ,! i~!


ComboBox Control
Se e Als o

Exam pl e


~I etho d s

Com bin es t he feat ures of a listBoK and a TeKtBoK .

Th e user can e nter a new va lu e, as with a TeHtBoH , or


the user can se lect an existing value as with a li s tBoK .

Re mark s
If a ComboBoK is bound to a data source, then the
Comb o BoK inserts the va lue t he user enters or selects
into that data source, If a mu lticolumn combo box is
bound, then the Bou ndCo lumn property determines
which value is stored in the bound data source,

The li st in a ComboBoK consists of rows of data, Ea ch

row can have one or more columns, which can appear
with or without headings, Some ap pl ications do not
supp ort co lum n heading s, others provid e onl y limite d

It may take a little digging to find what you are looking for using the

Contents tab but knowing the correct terminology is a big help. At the
top of many help topics, are links for "See Also;' "Example;' "Properties;'
"Methods;' "Events;' and "Specifics". If you are looking for more
explanations, "See Also" is very helpful. If you are looking for code to
copy and paste, "Example" is the link you want. For information about
specific Properties, Methods, and Events, click the appropriate link.
The body of help topics often contain hyperlinks to other topics and
pop-ups to explain the highlighted text in greater detail.
You can print help Help topics by clicking the Print icon at the top of the


I Chapter 4: Finding Help I

Index tab

!;;ontents [ Index l l iarch


Type in the key~ord to find:



CompareMode property
Filter function
InStrRev function
Replace function
Split function
string conversion
String data type
String function
String keyword
String$ function
data types
leftmost characters
middle characters
removing spaces
returning from functions

culling and pasting
from numbers
5 tring data type
text files
TextStream obiect
The binary compatibility DLL or E>
The binary compatibility DLL or E>
Then keyword
This (Me keyword)
Tile Horizontally command
Tile Vertically command
Time function
time intervals
Time keyword
time stamp
Time statement
Time$ function
Timer function


timer s


Date data type

rightmost characters


The Index tab displays a different way to organize help topics. It works
much like the index of a book. This is another area where using correct
terminology is very helpful. If you enter "string" in the keyword textbox,
you get a large number of linked topics. Enter "text" and you get a
number of unrelated topics (if we are looking for information on the
String variable type) but also a link to the "String data type:' So even if
you don't have the exact terminology, getting close to the correct word
may link you to the correct topic.

I Help Files I


Search tab

19.!:l!.e:ntslli:d . Search


Type in the word!s) to search for:

Use the Search tab to enter a word

or series of words to search for in
the help topics. It returns a list of
all help topics containing the
word(s) entered. For example, if
you enter "combobox:' you are
returned a listing of 67 topics.
Some topics are properties such as
"List PropertY:' Other topics are
instructional, such as "Ways to
put data in a ListBox or
ComboBox:' Keep in mind that
most help topics are linked to
related topics. So, we could begin
with a search fo r "combobox" and
read far more than 67 topics by
jumping to other help topics.

1- [ list Topics

ComboBo~ Control

Unable to unload withi ...
Understanding Obieet. ..
; MatehF ound. MatehR .. .
I Layout Event, OldLeft .. .
Style Property
I DblCliek Event. CanP ...
ComboB ox Control. A. ..
Ways to put data in a ..
! list Property
Things you can do wit. ..
, Style Property Example
MatehE ntry Property..
AutoTab Property
BoundColumn Property
Text Property
CurX Property
TextColumn Property
, Value Property
Locked. DropButtonS ...
DropDown Method Ex. ..
DragBehavior Property
ListWidth Property Ex. ..
ListRows Property EK ..
, lineCount Property
! Add items to a list usin...
Listlndex Property
I ListStyle Property


o Search previous results

Mrerosolt Fa...
Visual Basic ..
Visual Basic..
Visual Basic ..
Microsoft Fa...
Microsoft Fa.. .
Microsoft Fa .. .
Microsoft Fa .. .
Microsoft Fa.. .
Microsoft Fa ...
Microsoft Fa .. .
Microsoft Fa.. .
Mierosoft Fa...
Microsoft Fa .. .
Microsoft Fa .. .
Microsoft Fa .. .
Microsoft Fa .. .
Microsoft Fa.. .
Microsoft Fa.. .
Microsoft Fa.. .
Microsoft Fa .. .
Microsoft Fa.. .
Microsoft Fa .. .
Microsoft Fa.. .
Microsoft Fa .. .
Microsoft Fa .. .
Microsoft Fa.. .
Microsoft Fa...
Microsoft Fa .. .








B Match similar words

o Search titles only

Favorites tab















----------'--"-----'-'-'-'---'--"--'--"----'-':'-n- - - - - - - - - - - - --

&W data in a ListBox or





See Also
In a ListBoK or (1I'ttfijifi]:litft with a single co lu mn,

the AddItem method provides an effective

technique for adding an individual entrr,no the list.
In a multico lumn listBo H or (1tttjj1ifi1: I I howe ver,
the List and Co lum n properties affer another


techn ique; you can load the li st from a twodimensional array.

If you find a particularly helpful help topic or one that was difficult to
find, add it to your Favorites. The "Current topic" text box displays the


I Chapter 4: Findin g Help I

default topic title. Fortunately, you can change this description to
whatever you want it to say. For example, you could change it to
"ComboBox - Adding To the List:' Then click the Add button.
Favorites is one of the best ways to personalize the standard Visual Basic
Help file. Sure, you can print out page after page after page, use a
highlighter and make a binder. That is a fine way to catalog what you
have learned and create additional reference material. But Favorites is a
quick way to find 'bookmarked' topics and jumps to linked topics. It also
keeps the Copy and Paste functionality available.
To sum up, VBA stands for Visual Basic for Applications. Thus far in
this chapter, we have been discussing how to find help for the "VB"
portion ofVBA. What about the ''!\'? The Application? Good question.
Learning everything about the VBA programming language will give us
some good background but will not get us very far when attempting to
interact with MicroStation.

I Help Fi les I


MicroStation VBA Help File








.----.. -.-.-. ------..-.--.-.----.-. ----.-. -.-.- .-.- . -..--.-.-- . . . . . -.----.-.--.. . -- . _-_ . _ . __. _ . . . __. 1-.---------".-.






IIndex I ~ealch I FavOljtes I

II-licroStation V8
Visual Basic for

8 ~~

E:l Getting Stalted with Visual Basic

[) Loading and lunning a VBA maclO

MiclOStation va Objects
[) Flequently asked MiclOStation VBA Questions
ll ( ] Using the MiclOStation VBA Intellace
[) RecOlding and Revising MaclOs
[) Automating common MiCloS tation tasks
[) WOIking with MicloStation Objects
[) WOlking with MiclOStation Events
[) Customizing MicloStation with Visual Basic
[) Convelting hom MiclOStation Basic to VBA
rl CJ Developing Code in VBA
[) Changes 101 MiclOStation va XM Edition


Visual Basic and

VBA are thoroughly
modern objectoriented
environments used
by both professional
~ ~
developers and
Visual Basic is the
dev elop me nt
platform f or a larg e
numb er of
comm erci al
products, some of
whi ch yo u may use
on a daily basis,
Visual Ba sic f or
App li cations shares
m ost of Visual


FtJ (j Enums
Lt! G:iJ 0 bjects
rl CJ Events
rl CJ Methods

if ] CJ PlOpelties
rl (] Types
C':::::\ C ............... I.........

MicroStation has its own VBA Help file. It contains MicroStationspecific help for VBA. Searching for the file 'microstationvba.chm' on
your computer should show us where it is and let you open it.





Ili},M!craSla!ianV.8,Vi~ua!Basi~,f~~~llpl!cali~ns, I:f'tlp



,--' -"'-'"


.. ,.. ..








{;ontents i 1nde)( l5.ealch 1 Favorjtes I




Vertex List Example

~ Accessing an Element's Vertices

This example illustrates some of the methods of the

[!] Adding and Modifying Tag Elements

command that impleme nts event handlers for start, data

point, and reset. Because the other event handlers of
IPrimitiveCommandEvents do nothing in this example,
they have been omitted.

VertexList interface. The example is a simple primitive

[I Adding the Contents of a Fence to a Named GIOUp

[I Analyze Ale

[!] Application Element Example


Most of the logic for th is example is in the data point

event hand ler. It tests to determ ine if it has a sa ve d
reference to an element. If not, it tries to locate an
element using LocateElement. If it successfully finds an
element that supports the Verte)Clist interface , it
retrieves the list of vertices using GetVertices. It prints
the vertex list, and saves the reference to the object. On
subsequent data points, it detects that it already has an
element. It uses GetClosestSegment to get the index of
the first vertex of the segment closest to the data point.
Using that information , it prints the range of the closest


The reset event ha nd ler in this example just restarts the

command. It invokes CommandState.StartPrimitive.
StartPrim it ive first resets MicroStation's command

[I Alcs By Length

Area Booleans

[!} AUaching and Searching a Cell Library

ill Bsp~ne Curve Point Extraclion

l!I Bspline SUI/ace From Gridded Point Interpolation

Bspline Surface From Poles

~ Bspline SUI/ace From Scattered Point Approximation
[!) Centering the View at the Cursor
Changing a Reference File Path from Absolute to Logical
~ Changing an Attachment to Reference the Default Model
~ Changing Coordinate Readout in a SaveA.s Event
Changing the Update Order of Attachments

~ :',,_~;~: :=_:-~=:~':;;,;r,'::~_:::~;~":':.~__




" . :,: "--,,:,~,: :r----" - -.-~-

One of the most helpful sections in the MicroStation VBA help file is the
Examples folder, which contains excellent explanations as well as the
code to perform specific tasks.


I Cha pter 4: Find ing He lp I

Another excellent way to open the MicroStation VBA Help file is to
select a MicroStation-specific API call in the Object Browser or in our
code and hit the <Fl> key on the keyboard.

Got Net? technology/ vba/default.aspx
gets us to Microsoft's VBA web site. If this URL is difficult to remember,
you can also use, which contains links
to white papers, Knowledge Base articles, and other reference materials
that comprise a wealth of information on VBA programming and its
associated topics. Although you are not likely to find much about
MicroStation's VBA-specific implementation here, you will find many
other examples on how to accomplish specific tasks m the VBA

Visual Basic for Applications

lltkrulloll..Iolll.A~. nu,\L.nJ!!I\b...."!J$r.u.J.t.'(~


\lj~ttI .hfie
~lllh:,l;iQfl1 (Ve.I\) ,oftw. ~, .... ,IQ~~nt kJt (~I<) 'fI.b!.f ~'I\,.P{~~tl\t'f~kw .. r ""Mo,.,
(l5V'J) too ~a."h(IO ,(I,.rt.enfl1\ty tG t:h/J'lr ,'CDhe{lhcnj with Ute nrflmler'nJ~ClYIj"'lbon IDE Md the: Mo(tOt<lttVL.11J.a1 e.Ule


'"~""d ,,,I"'.f'ql"U~


l,j(II!I""h'l'll o!IIfld 1 ~!"I'~rl

fndtt$nth! et~Cd UlltaiiOiU Abo!" yU"

~b"" It'ull .ilMwilN to tt\o mot'!: ~mi\"'lOA- -q,I.lef'tlM~ 4tlO'A
viJual ruri fU "~licatiOtls .

YU" " MUM! Itderrn<i'*

lir)d lin'll' to l1uwrlto$iJ Ihl v.bA IMUIJGU. rIl!1oraM s'. tar
If1iCIJ ,nx)s .st "",eU.u ~m.;s 1'0 ClnJin~ 'i'!lr~iulu 0' Ula
h.OIJlJ;s:~e t.f.(lCIIflc., for arlO( T-t(OSibM of Q!fittl.


VI\lIoI Uatl <


itud ~lJl,ItU"J


'~l n ;t~bd.,.lJl.f:.tll'.tj!

Qt}ti)r 1I1J~~1i~Jl ~~J~

M;,t,jtu;m ~t) Ibrr !S,'I


ISV' e,w;Jt 't~rA.M

lSI( C:Q,m"I~" C8'H

; CcIl'o)~tU-"
, f .t H " ite ~itir;orlt .n)j
clitHlda l\lbl'l'tlS~JH

rur epllliNti!i'tl


i.M1"",,,,. b~"fdl, "'llJ l!Iot.~~m.Rt... qJ'

t!cto1lng ytiA
Lici-nlin9 \'0~ .oMbl"" yeu to


yOLOr vu.,.

'I\/It.kd $PpIkMh~(l to OJn.:m~/f.,

hdrWU' ,,,,:'Yyrt

( P C VIlA

LlIf'ln alzout the tf"d"lnicaf Su"ptlrt' OpUVf1i- , t~

you llirorf!. 'JU(illl....o..! "itt'r Ut.e \'~'" ~t1lifl\f
P'~QU!. .

'iRA PArt tW: prp9 r.!1'!!

W~ rOl,lli~A1. V&~. YQllll4<lqI"!~II., ~~ ..


Ml!!rn~t (I t tf'J. Hit;I'on!t \1ft:. J"4'tto.e, 'I*~"T' . ~Qotn. Qf th. tlf"',,,h.t( l(>lfU <I"t .u. it P.i, th~"' .

V.eA 4,'.
j)tIt'J:J!!\.XQMIlU.Y..lJI.~,IL'U .Q


tn~1~IlI'\O tNJ VilA ~O)(

ONbl" 11 \1) l).... lIl\)J);tt 1$1~
Mnef(.'f ~ Wlt~ol,,~b/l;(I \I'M intc ynlOl ~DII~~tll\.


See ttI~ ~mp.tlnI1;1 'Nhleh l!IilVtl; hetf\, vaA ~d tN

111". b\lV4 VQA ' f'".)!JIIt(lo


~t. ", .. rt

YM :CO$'

'9: )'jiY'" (Wig. liB Wilt!!; Mi ....MjM

'!:4 "J "J ~~ 1"''' i' " .i:'~$ TMI~ I~ ... ~Hi~~.
(;o.n.... rt lOad, 'r))O'\ '1isulIl ouic k.r APpliGafl('nf" to 'Ils\llIl

nuk Jlttr ,.p."et'.l mi.o""bl'.o tr. ~':t,)1oft Vi'lJ" SNdij)

200'~ T~[5 fotthe MtUOib/'t Offico '!;,s'~M.
do.:um~n t ~hli1tlU tI"!A "'UOf'\s to mit;!1'ItfI , !hI!! me~r


(,)11. eOI'J"'l'lie.'"l i:UlIoU nd fdditJ6l'I.t1 tUOdn:..U for

):'\1111 ''' f)l! ''".:;~ "''i ".1

t-';~\"..,..11' ,,) . ! . ...

e ,'.po'''t

u!l.~A.'(I..t$..Stt.lIl:J!!J.tJlt\:' ~i.U'tt'OJ Ctt

:Il~!UlJl~R4.t.'~~ ~~l1>J:P.I.lltM)

I The Net I


Google, Yahoo, and other search engines can unlock the rest of the
Internet's VBA knowledge for us. Remember, there are a lot of
programming languages out there. A search for "Message Box" returns
us a large number of web pages to check out, but in addition to VBA
results, we will get pages for C#, C++, Java, JavaScript, Fortran, Pascal,
and other languages.
A quick trip to the Bentley web site and a search for "vba" nets some
good information as well. Why not go directly to the source?
Login \ Site Map ! PI illt i Stor e


[=~:==~==lse,uch L~~_:~~~j L~~~_~_. .__. _.___~.~J



E2J Bentley ,com

~ Recommended Pages ~ SELECTservices (Sl.Jppo rt)

Found: 31 results in "2 catego,"if:'5

Bentley I Exten d ab l e \'l ith VB{VBA programs

Be ntley I Customizable to the \!l ay you wo rk

Th~ Customization CQre technolog~ lets pro '~r amrn~r::; and ;;sdrr"'Jinistr2ltors e...<tefld .:i nd config ure

Bentl ey I Develooer Resources Over .... iew

5ELECTservices I 8279 - GEOPAK VBA Routines

rDutlne~ cr~ prQvlde-d fr~~ for your u:,e, Vlif: .:i~ Bentley hope that '1')!J r8.:Jp
pr,),judiv ity benefits from out'jn1ating l::;E0PAK Civil Eng in eering Suite I/~ith VBA .

The following <;c)PAK '.JBA

SELECTservices I B348 - Nico'osoft Office VBA Patch Utility TechNote

ThiS doctJm~nt is provided to ~d.jress knQ1/.Jn t:omp3tlbil!t~' issues th3t may be encoljn~ered after
h~ v ing inst.;dled pnJd1Jct:. pro"iidlng ditft5rent ve t'sio ns (:f the ""licro:'::; oft Il BA runtlm..: libroril?$ on th~
::;ory,e- compute-r, The st:::p::; des,:ribo::d bt5lc,w can be. i:ak~n to p ...
SELECTservices I 0223 - Configul'ation Variables in MiC"oStation va
ThIS techrllcal C'o('ument ~r0 '/lde:; ;~ d:'::'5ulptl,)n of the '.'-:Jt'iables In r~lIcroSt.jtion va (200 . .1 EditIon) V3.0
atld 1)3.1)

19 lOO,:) Ben'tiey Systems, Incotpora w.d





I Tem15 of U.H~ I W-ehmaster 1 1.800.SF.NT1.EY

Bentley.microstation.v8.vba. This is a good place to ask questions. And
who knows? As you become a VBA guru in your own right, you may be

I Chapter 4: Finding Help I


able to help others by answenng their questions in this Discussion




~ Back






~ @J ~~ : P Search i): Favorites ) : B T ~, ~

-j AQ.dress !~ .com/en-us/communitY/DiScussion+GroupsIDirectory/

.~. ~Go



Discussion Group Directory

You can 3CCI;:SS any of these groups via most popular discussion group nl;:wsre
from a Web browser, visit
VI/ant to post a test article


a newsre.3der? Go to Bentley.testposts,

Bentley ,rnicrostation .'18 .xme.3rlyacce$$ (new)
Benti",,, ,rnicrostation .'18 .gener.~1
Bentley .rnicrostation ,v8 ,d'.NQ

Bentlev .rnicrostatior, .'113 .te xt

Bentley .microstation ,vS ,dimerlsioning
Bent!~y .rnicrost:ation ,v8 ,database
Bentley ,rni ct'o~tation ,v8 ,plottinl)

It'b~Jl?d?~~~1~<;2L(!h~:~kl.,?~B e ntl e y ,rn iero ~:ati.':.:',_:'1 ~;; ba_


We talk to MicroStation through its Object Model. The top level of the
MicroStation's Object Model is the Application Object. Using the Object
Browser in VBA is a great help when you are trying to discover
something about an Object Model. As shown, you can restrict browsing
to the MicroStation DGN library so all MicroStation Classes show up in
the ListBox on the left. Selecting "Application" in the left ListBox

IThe Object Browser I


displays its properties, metho ds, and events m the listbox on the right
side of the window.

Object Browser

tMicroSt .lti~~~DG'I-'-- .........;.. j


vI '" ~


Cla sse s

Members of'Application '

" : I1i'
_ 11i'

Gl <global s>

li!'J AccuDrawHin ts
li!'J ACSManager
li!'J 'Appiication-

ActiveMode lReference
ActiveWo rkspace
Appli cation Objec tConnec tor
.,~ AddAttachm entEventsHandl er
Arc Element
"~ AddChangeTrackEve ntsHand ler
AreaP attern
" ~ AddLeve lChangeEventsHand ler
Attac hmen t
"'~ Ad dMod alDialogEventsHandler
Attac hments
;~ Add Mo delAclivate Eve ntsHa ndler
.,~ Ad dMo delChang eEventsHand ler
Auxili aryCoo rdinateSystemE lemer
8s plin e
,~ Add SaveAsEventsHa ndle r
.. "~ Ad dViewUpdateEve ntsHa ndler
8s piin eCurve
,.~ Appen dXDatum
8 sp iineCurveElement
8spiineSurfa ce
."~ ApplyHorizonta lScalingFixForEMF
v ~ .o:.~ AnnhMprti (';:. I~(' ~l i nnj:" ivj:" nrI=MJ='
R c: n li n p !=:l l tf~ (' p l= lpmpn t

~ 'APpiicaiionElemeni'





ACSManage r

Properly Ac1iveDesignFile As Desi<tnFile


Member of MicroStati()llOGII .Auplic.l1ion

For example, if you have selected Application in the ListBox on the left
and want to do something with the active design file in MicroStation,
click on ActiveDesignFile in the right-hand ListBox. The description at
the bottom of the Object Browser tells us the ActiveDesignFile property
of the Application Object returns a DesignFile object. We can now select
DesignFile in the Classes list (the listbox on the left) to see the Design
File's properties, methods, and events in the Members list on the right.


_ ,.._v...., ,..

~ .~



Memb ers of 'Oe s ignFile'



~ ComplexSh ape Element


~ Comp lexS trin gElemen t

Au thor
~' 11i' Client
. ,~ Close


Co neElemen t
CopyCo ntext
CrossHatc hPattern
Cu rso rlnformation
Da tabaseLi nk
Data8 10ck


ti:lOlme'nsi;;;;Elemeni ' .


. I1i'


Commen ts
Cu stomP ropertyExists
I1i' Date Created
I1i' DateLastPlotted
I1i' DateLastSaved
I1i' DefaultModelReference
"~ DeleleLevel
I1i' Dimens ionStyles




;ef! DLong
~.~ nrnnn::lhlp l= lprnpnt

y) .0<'\

l= }(fr:::lr trnlnrT :::I h lp

Functi on AddNewLeve l(LevelNam e As Siring) As Lev.. 1

Member 01 Mi cr oSttlti onOGl I.De$ignFile


I Chapter 4: Finding Help I

Selecting AddNewLevel in the Members list shows that we need to
supply a LevelName when using AddNewLevel. It also shows that
AddNewLevel returns a Level Object.
One reason the Object Browser is helpful is because you can start at a
very general level (Application) and work down through the object
model to the object, property, method, or event for which you are
looking. We can literally browse through the available objects and APIs
using the Object Browser.
Other ways to find/get help? Take a Bentley-approved VBA training
class or attend the annual Bentley Developer Conference.

Finding help is not always easy. Knowing where to look is the first step.
Next, using correct terminology moves us along the path to finding the
answers to our questions. Learning to use tools, such as the Object
Browser, provides more answers.
Keep in mind that the most simple subjects still require effort to learn
and retain. VBA is no different. If you allow yourself to become
frustrated, the chances of success are diminished. You can learn
MicroStation VBA. You really can.

Modules, Forms, and

Class Modules
We have already discussed the basics of how MicroStation VBA projects.
modules, forms, and classes are used to create programs that improve
productivity and accuracy. That much we know. It's time to learn when
to use each of these design elements and how they work together.
In this chapter:







Procedures and Functions

Code modules are the foundation of every VBA project. We use them to
declare variables that can be used from within the code module, by
other code modules, by forms, and even by class modules. Windows API
functions are declared in modules so the API calls can be used in our
project (more on Windows API functions later in the book). Procedures
and functions inside modules can be run from the VBA Project



I Chapter 5: Modules, Forms, and Class Modules I

Manager. In fact, code modules are so essential that an initial code
module is created every time a new VBA project is created. Procedures
written in code modules are the starting point for running code and
displaying forms.
Enough talking. Let's write some code.
Let's begin by creating a new VBA project named Chapter 05. Save it in
the folder C:\MicroStation VBA. After this new project is created, you can
see that a code module named "Module 1" is created automatically.
Rename this module modChOS.
Continue by creating a new procedure named Ma in. Inside the code
module, type
Sub Main()

When you press the

<Enter> key after typing
the above code, VBA
finishes the new procedure
by entering an "Exit Sub"
for us. At this point, the
module should look like
The next thing we are going to do is enter some code in our new
procedure "Main:'
Sub Main ()
' Declare Variables
Dim MyLine As LineElement
Dim MyCir As EllipseElement
Dim CenPt As Point3d
Dim LineSt As Point3d
Dim LineEn As Point3d
Dim RotMatrix As Matrix3d
' Create Hor iz ontal Line
LirieSt . X = -1
LineEn.X = 1
Set MyLine

Application . CreateLineElement2(Nothing. LineSt. LineEn)

Application.ActiveModelReference.AddElement MyLine
' Create Vertical Line
LineSt . X = 0 : LineSt . Y = 1

I Modules I


Li ne En . X = 0 : Li neEn . Y = -1
Set MyLine = Appl i cation . CreateLineElement2(Nothing , LineSt, LineEnl

App1 i cation.ActiveMode l Reference . AddElement MYLire

' Creat e Ci rc l es
Se t MyCir = Appli ca t ion . Cr eat e El l ip se Ele ment 2(Noth i rg, _
CenPt, 0.25, 0.25, RotMatrix )
Application.ActiveModelReference.AddElement MyCir
Set MyCir = Application . CreateEllipseElement2(Nothing, _
CenPt, 0.5, 0.5, RotMatrix)
App l ication.ActiveM odelReference.AddElement MyCir
End Sub

The code above may look like a whole lot of gibberish at this point, but it
will make much more sense as we continue to learn VBA together.
Notice the comments inserted into the code. Remember, comments
begin with an apostrophe C).
Running this code draws two circles
and two lines in MicroStation that
create a target shape that looks like this:
The code works great. From now on,
any time we need to draw a target with
these dimensions centered at (0, 0, 0)
we have the code to do it. In mere
milliseconds, we can draw this target by
running the macro Ma in whenever we

The more
we do, the less
often we will
be able to run
our macros.

. I


If we need to draw the target centered at (4, 5, 0) we can copy and paste
the code, and rename the procedure to Ma i n2. And then we can create
Ma i n3 with different coordinates, then Ma i n4, then Ma i n5 and so on.
Right? Well, we could do that but there is a better way.
Let's change the way we are doing things a little bit. Instead of having
Ma in draw a target at (0, 0, 0), we create a new procedure that draws the
target at the X, Y, and Z coordinates we specify. We will do this by
creating parameters for the new procedure.
Sub DrawTarget (CenX As Double , CenY As Double , CenZ As Doub l e)
' Declare Variables
Dim MyLine As LineElement
Dim MyCir As EllipseElement


I Chapter 5: Modules, Forms, and Class Modules I

Dim Ce nPt As Point 3d
Dim LineSt As Po i nt3d
Dim LineEn As Point3d
Dim RotMatrix As Matrix3d
'Create Horizontal Line
CenX - 1
LineSt . Y CenY
Lin eEr.X
CenX + 1
LineEn . Y CenY
Li ne En.Z
Set MyLine

Application . CreateLineE l ement2(Nothing. LineSt. LineEn)

Ap plicat i on . Act i veModelReference . AddE l ement MyL i ne

'Create Vertical Line
Li neSt. X CenX
Li neSt.Y
CenY + 1
Li neEn . X CenX
CenY - 1
Set MyLine

Applicat i on.CreateLineElement2(Nothing. LineSt. LineEn)

Appl i cat i on . Act iveM ode lRef e r ence. Add Elem ent MyLine
' Create Ci rc le s
CenPt . X CenX
CenPt . Y = CenY
CenPt.Z = CenZ
Set MyCir = Application.CreateEllipseElement2(Nothing. CenPt._
0.25 . 0 . 25. RotMatrix)
Applicat i on.ActiveMode l Refere nce . AddElement MyCir
Se t MyCir = Appl ication.Creat eEllip se Ele men t2 (Nothi ng. CenPt._
0 . 5. 0.5. Rot Mat r i x)
Application.Act i veModelReference . Add El ement MyCi r
End Sub

The code from Main has been copied and pasted into the same code
module. The new pasted procedure is then renamed DrawT arg et. The
goal here is to make our code more flexible, so we can draw targets
anywhere we specify. Our new procedure, DrawTarget. requires us to
specify three parameters named 'CenX', 'Cen Y', and 'CenZ'. We declare
them as doubles (very precise numbers). Let's take a look at how we
use it.

I Forms I


Sub Ma in ( )

' Draw Targets

Draw Target 0, 0,
DrawTarget 3, 0,
DrawTarget - 3, 0 ,
DrawTarget 0 , 3,
Dr awTarget 0 , - 3 ,
End Sub

Our procedure Mai n now draws five targets. More flexible? More
powerful? Absolutely. But the coordinates are still hard-coded. This m ay
work at times when we are setting up a page that is to be printed in
MicroStation. But how can we let the user specify the coordinates? Let's
expand our program a little by introducing a graphical user interface.

User forms provide a graphical user interface
(GUI) for our users. We begin by inserting a
new User Form.
This form has three abels, three text boxes and
a command button. We will keep the default
names for each of these form elements except
for the text boxes. We want to rename the text
boxes to txtX, txtY, and txtZ. This is done in the
properties window inside VBA.
When a control is selected, we can make
changes to the control's properties in the properties window. As with
other windows in VBA, if the properties window is not displayed, show
it by going to the menu in VBA, View > Properties Window. After
changing the names of the text boxes, change the caption properties of
the labels and the command button to reflect the image shown above.
After you modify the properties of the controls on the form, change the
name of the user form to "frmChOS".
We are going to enter some coordinates in the text boxes. When the user
clicks the "Place Target" button, we want to use the entered coordinates
and use the OrawTarge t procedure we just created. We need to write
some code in the C1 i ck Event of the CommandButton. Double-click on

I Chapter 5: Mod ules, Forms, and Class Mod ul es I


We can also
get here by
on the button
and selecting
View Code in
the pop-up

the button when we are designing in our project to be taken into the
eli ck Event of the CommandButton.

CDbl(txtY . Text), CDbl(txtZ.Text)

You only need to enter one line of code to use both the DrawTarget
procedure and the values entered into the form's text boxes.
We are almost finished with this little project. We have a procedure that
draws targets. We have a form that allows users to enter coordinates. We
now need to give the user a way to display the form. We don't want the
user to enter the VBA environment to run this program, so we will make
another change to the procedure named Ma i n.
Sub Main ()

' Di splay the Form

fr mCh05 . s how

End Sub

Save your VBA program now. It would be a shame to lose this work.
Click on the Save button in VBA.
Saved? Good. Now run your program and see how well it works. From
within MicroStation, hold down the <ALT> key and press the <F8> key
on the keyboard.

I Forms I
The macro named
Ma i n that is shown is
the procedure Ma i n.
When it is selected,
displayed so we can
enter numbers for X,
Y, and Z coordinates
to place our targets.


Macro name:




Step Into

Macros in:

<All Standard Projects>



Before we continue,
let's review what we have done.
We wanted to allow our users to draw a target symbol inside
MicroStation. The first thing we needed to do was write some code to
draw the target correctly. So we put our code in a procedure named Main
and got the basic code that draws a target shape working by hard-coding
everything. When that code was working properly, we made the code
more flexible and useful by taking the code out of Ma i n and creating a
new procedure named orawT ar get . We provided three parameters that
could be used to specify the location of the target. After all of this work
was completed, we tested it by modifying the code in Ma in to use the
Procedure named or awTa rget.
The next step was creating a user interface (more on using forms in
Chapter 10). We used the values from the text boxes for the parameters
of orawTa rget (converting the values from a string to a double by using
the standard VBA COb 1 function) . The last step was to change the code
in Ma i n to display the form.
We now have a code module and a form working together using ~l a in to
display the form. When the user clicks the button, values from the form
are parameters in orawTarget, which resides in the code module. So,
why is orawTarget in the code module? Couldn't it be in the code area of
the form? Yes, it could be and the code would still work. However, other
modules and other forms would not be able to use orawTa rget as easily if
it had been placed in the form's code area. One of the things that makes
a code module so great is that declared procedures and functions can be
easily utilized in other areas of our project. We may have three forms


I Chapter 5: Modules, Forms, and Class Modules I

that are using code in a code module as well as procedures and functions
in a different code module, for example.
Our code is working pretty well right now but can we do anything else to
make our project more flexible and powerful? That is a good question.

You may know that we use classes to create objects, but did you know
that by putting a little thought into creating class modules, they can be
useful for years to come. How so?
In our current project, we can draw a target at any coordinate we specify.
That's pretty powerful and it meets our needs today. What happens,
however, if a year from now we find we want to change the target's size?
The procedure 0raw Tar get only allows entry of three parameters (X, Y,
and Z). We could modify the procedure to require four parameters, the
last one specifying the size. But this could break parts of our code that
are only providing three parameters. We could also make the new
parameter optional, but there is a better way.
We can create a new class that has X, Y, and Z properties. It will also
have a Draw method. When this is in place, we will add a Scale property.
We could add a Level property, a Color property, a NumberOfCircles
property, etc. We can add these properties today, tomorrow, or next year.
It doesn't matter when we add them. We just need to make sure that
when we add them we do so in a way that allows the previous code using
the class to continue to work properly without modification.
Time to write some code.
Let's add a new class module to our project. Do this by using the VBA
menu Insert > Class Module. Name it clsTarget (using the Properties
Window for this). It will have three properties and one method. The
most basic way to implement properties for classes is to declare variables
as Public in the General Declarations area of the class module.
Implement methods by creating procedures in the class module.
Begin by defining the properties.
Pu bl i c X As Double
Pub l ic Y As Double
Publ ic Z As Double

I Classes I


Next implement the Draw method. Recall that you can get this finished
project on the CD that accompanies this book and open it instead of
typing in all of the code. The Draw method was created by copying and
pasting DrawTarget and changing 'CenX' to 'X', 'CenY' to 'Y', and 'CenZ'
to 'z' to use the X, Y, and Z properties defined in the class module.
To make sure we are all on the same page, look at the screen shot of the
finished class.

'Declare Variables
Dim My L i ne As L ineElement

Di m Mye i r As El l ipseE l ement

Dim CenPt As Point3d
Dim LineSt As Point3d

Dim LineEn As Point3d

Dim RotMatrix As Matrix3d
'C reate Hori~ontal Line

=X- 1


LineSt.Y;::: Y
LineSt.Z '" Z


X + 1


Set MyL ine = App 1 ieat ion. CreateL ineE lement2 (Nothing,
App l icatlon.ActiveHodelReference,AddElement MyLine

L ineSt,

L ineEn)

LineSt I

L ineEn)

'C["eat.E: Vert-ieEll Line

LineSt.X "" X
LineSt.Y = Y + 1

LineSt.Z =
LineEn.X =
LineEn . Y =
LineEn . Z:c

Y - 1

Set HyL ine = App 1 ieat ion. CreateL ineElement2 (Nothing I

Application,ActiveHodelReference , AddElement HyLine
'Create Circles


CenPt . Y = Y
Ce nPt. Z :a Z
Set MyC i r = Application . CreateEllipseElement2(Nothing. CenPt.
App l i c ation . Act ive llode lRefer e nce . AddElement MyC i r
Set MyC i r = App li cation . Cr eat e El l i pse Element2(Noching. CenPt,
Appl i cation , Act i veMode lReference , AddEl ement HyClr

0 . 25. 0 . 25.


0.5, 0.5, RotHatrix)

End Sub

And here is the code as it should be typed:

Public X As Double
Public Y As Double
Public Z As Double
Sub Draw (X As Doub l e, Y As Double , Z As Double)
'Declare Variables
Dim MyLine As LineElement
Dim MyCir As EllipseElement


I Chapter 5: Modules, Forms, and Class Modules I

Di m Ce nPt As Po i nt3d
Dim Li neSt As Point3d
Dim LineEn As Point3d
Di m Ro t Matrix As Matrix3d
' Cre ate Hor iz on t al Li re
X- 1
X+ 1
LineEn. Y Y
Set MyLine = Application.CreateLineElement2(Nothing, LineSt, LineEn)

Application . ActiveModelReference.AddElement MyLine

'Create Vertical Line
LineSt. X X
Lin eSt.Y
Y+ 1
Li ne En. X X
Y- 1
Lin e En.Z
Se t MyLin e

Appli cati on . Cr eat eLin e Ele ment 2(Not hi ng , Li neSt , Li neEn)

Application.A ctiveMode l Reference.AddEle men t My Line

' Create Circles
CenPt . X X
CenPt. Z Z
Set MyC ir

App li cat i on .CreateEll i pse Element2(Noth i ng , CenPt , _

0.25, 0.25, RotMatrix)

Application . ActiveModelReference.AddElement MyCir
Set MyCir = Application.CreateEllipseE l ement2(Nothing, CenPt, _

0 . 5, 0.5, RotMatrix)
Application.ActiveModelReference.AddElement MyCir
End Sub

I Procedures and Functions I


Once everything is in place, we can use it as follows:

Private Sub CommandButtonl_Click()

Dra.Target CDbl(txtX.Text). CDbl(txtY.Text l . CDbl ( txtZ.Text )
Dim ~!yTarget As NelJ clsTarget
HyTarget . X = CDb 1 (txtX. Text)
HyTarget . Y = CDbl(t x tY.Te x t)
~! yTarget . Z = CDbl(txtZ.Text)
HyTarget . Dr alJ
Ene! Still


Instead of using the Dr awTarg et procedure we previously created, we can

use the clsTarget class. Comment out the 'DrawTarget' line of code.
Next declare a variable as a "New clsTarget" then set the X, Y, and Z
properties of the object and invoke the Draw method.
Class modules are often considered reserved for advanced programmers
but this doesn't need to be the case. As we see here, classes can be
implemented very easily and simply. We will discuss classes in greater
detail in a later chapter.


Modules, forms, and classes each use procedures and functions.
Procedures are declared with the "Sub" keyword and functions are
declared with the "Function" keyword.
Let's look at a few procedures and fun ctions.
Sub DrawCi rc l e ()
' Dec l are Var i ables
Di m MyCir As Ell ipseE l ement
Dim CenPt As Po i nt3d
Dim RotMatr i x As Matrix3d
' Create Circle
CenPt . X 0
CenPt. Y 0
CenPt . Z 0
Set MyCir = Application.CreateEllipseElement2(Nothing, CenPt , _
0 . 25 , 0 . 25 , RotMatrix)
Application . ActiveModelReference.AddE l ement MyCir
End Sub


I Chapter 5: Modules, Forms, and Class Modules I

DrawCi rc 1e draws a circle at (0, 0, 0) with a radius of 0.25. It can be run
by itself without the any other procedure or function.
Sub DrawCircle2(Radi us As Double)
'Declare Variables
Dim MyCir As EllipseElement
Dim CenPt As Point3d
Dim RotMatrix As Matrix3d
'Create Circle
CenPt . Y = 0
CenPt. Z = 0
Set MyCir

Applicat i on.CreateEllipseElement2(Nothing. CenPt. _

Rad iu s , Radi us , Rot Ma t rix)

Application.ActiveModelReference.AddElement MyCir
End Sub

DrawCircle2 is a procedure with a single required parameter.

DrawC ire 1e2 cannot be run by itself - it requires another procedure or
function to run it. When it is called by another function or procedure,
the required parameter "Radius" must be supplied like this:
DrawCircle2 1.5
Sub Dra wCi rcle3(X As Double, Y As Double, Z As Double, _
Optional Radius As Double = 1.25)
'Declare Variables
Dim MyCir As EllipseElement
Dim CenPt As Point3d
Di m RotMatrix As Matrix3d
' Create Circle
CenPt . Y = Y
CenPt.Z = Z
Set MyCir = Application.CreateEllipseElement2(Nothing , CenPt. _

Radius, Radius , RotMatrix)

Application.ActiveModelReference . AddElement MyCir
End Sub

I Procedures and Functions I


Ora wC ire 1e3 requires us to provide X, Y, and Z values and gives the
option of providing a radius. If we supply a radius, it uses the value we
give it. If we do not provide the radius, it used a value of 1.25.
Here is one way to test our procedure DrawCi rcl e3:
Sub TestDrawCircle3 ()
DrawCircle3 2.25, 2.25, 0
DrawCircle3 2.25, 2.25, 0, 1.125
End Sub

The first time we call DrawCi rc l e3 we do not provide the optional

parameter. The second time we call it we provide a radius value of 1.125.
The first circle will be drawn with a radius of 1.25 (the default value) and
the second will be drawn with a radius of 1.125.
Once an
Parameter is
declared in
after it must
also be

Sub TestDrawCircle3 ()
DrawCirc le3 2 .25, 2 . 2 5, 0
DrawC ircl e 3 2 . 2 5, 2 . 25, 0, 1.1 25
DrawCi rcle3(X As Double. Y As Double, Z As Double, [Radil/s As DOl/ble= 1.25])

End Sub

As we call functions and procedures, VBA displays a tip that shows us

the parameters for the function or procedure that we are calling. Notice
how the radius parameter is enclosed in square brackets. The square
brackets tell us that "Radius" is an optional parameter. We are also
shown the value of the optional parameter that will be used if we do not
supply a value.
Another type of parameter that can be declared in a procedure or
function is called ParamArray. A ParamArray must be the last
parameter declared, because when we supply a value or values to the
parameter in code, we can supply any number of values for the
parameter. Here is an example of how it is declared and used in code:
Sub DrawCircle4 (X As Double , Y As Double , Z As Double, _
ParamArray Radi i() As Var i ant)
' Declare Variables
Dim MyCir As EllipseElement
Dim CenPt As Point3d
Dim RotMatrix As Matrix3d
Dim I As Long
'Create Circles
CenPt.X = X


I Chapter 5: Modu les, Forms, and Class Modules I

CenPt. Y = Y
CenPt.Z = Z
"0" 1= LBound(Radii) To UBoJ'ld(Radii)
Set MyCi

= Application.CreateEllipseE'ementZ(Nothing, CenPt, _

Radii(I), Radii(I), RotMatrix)

Appl i cation . ActiveModelReference . AddE l ement MyCir
Next I
End Su b

We don't know how many radius values will be provided in the Radii
ParamArray. So, we use a For ... Next loop which allows us to look at
each one and use it in creating a new circle. Here is an example of how
we call a procedure with a ParamArray in code:
Sub TestDrawCircle4 ()
DrawC i rcle4 1. 1, 0 , 0 . 25 , 0 .5, 0 . 75, 1. 1.25 , 1. 5
En d Sub

We provide an X of 1, a Y of 1, and a Z of O. Then we begin providing

radius values. After the code is run, we have six new circles in our
MicroStation design file.
Here are the six circles created by

Te st DrawCi rcl e4 .
We have created over 100 lines of code so
far in this chapter. The current module
now has nine different procedures in it.
Five of them can be run by themselves,
the others must be called by other
procedures or functions .
Speaking of functions, let's examine them in detail.
Function Pi () As Dou bl e
Pi = Atn ( l) * 4
En d Function

Here is a function named Pi . It does not accept any parameters and the
type of value it returns is a Double.
We specify what value is to be returned by assigning the return value to
the name of the function.

I Procedures and Functions I


This function, Pi, can be used now wherever we need the value of Pi.
The procedure DrawCi rc 1e3 allows us to provide the radius of the circle
to be drawn. But what do we do if we only know the area of the circle we
want drawn? We can calculate the radius if we know the area but we
need the value of Pi to do so. Rather than hard-coding a value of
"3.14159" for Pi, we can use the Pi function we just created.
Sub TestPi ()
Dim Circ leArea As Double
Dim CircleRadius As Double
CircleArea = 3 . 5
CircleRadius = Sqr(CircleArea / Pi)
DrawCircle3 2.5, 2.5, 0, CircleRadius
End Sub

We calculate the radius of the circle based on a given area. We then use
that value in the radius parameter of the procedure DrawC i rcl e3 .
The function Pi we just created does not have any parameters. It does
not need them because the calculation is always the same. Let's look at a
few additional functions that come in handy from time to time. They are
named RTD (Radians To Degrees) and DTR (Degrees To Radians).
Function RTO (AnglelnRadians As Double) As Double
RTD = Angle l nRadians * 180 / Pi
End Func t ion
Function OTR (AnglelnDegrees As Double) As Double
DTR = AnglelnDegrees * Pi / 180
End Function

These two functions perform calculations that are very common to

those of us who use MicroStation. They are shown here for instructional
purposes only because MicroStation's VBA implementation has a
function named Deg rees that converts radians to degrees and a function
named Ra di an s that converts degrees to radians.
The functions DTR and RTD (as well as the functions Degrees and Radi ans )
have one required parameter. Here is how we use them:
Sub OrawArcl ()
Dim MyArc As ArcElement
Dim CenPt As Point3d
Dim RotMatrix As Matrix3d

I Chapter 5: Modules, Forms, and Class Modules I


Set MyArc = App l ication . CreateArcElement2(Nothing. CenPt. 1.5 . _

l.5. RotMatrix. DTR(45). DTR(90))
Application . ActiveModelReference.AddE ' ement MyArc
End Sub

Create ArcE l ement2 requires several parameters. One of them is the Start
Angle. Another is the Sweep Angle. Both parameters require the value
to be given in radians. Many of us don't normally think in radians, we
think in degrees. So, we can use the DTR function shown above to convert
from degrees (which we think in) to radians (which the function is

Here is the arc created by the above code. It begins at 45 degrees and has
a sweep of 90 degrees.

Returning an Array
Functions return a value, right? Yes. But functions can actually return
more than one value through the use of an array.
As we will discuss more in the next chapter, but for now know that an
array is a variable that contains more than one value and that we can
return an array in a function. Here's what it looks like:

C) character
allows one
line ofcode to
span multiple

Function Pol a rP oi nt (X As Double. Y As Double. Z As Double. _

Angle As Double. Distance As Double) As Variant
Dim XChange As Double
Dim YChange As Double
XChange = Cos(Angle) * Distance
YChange = Sin(Angle) * Distance
Dim PPoint(O To 2) As Double
X + XChange
PPoint(l) = Y + YChange
PPoint(2) = Z
PolarPoint = PPoint
End Function

I Procedures and Functions I


The Pol arPoi nt function allows us to define a starting point (X, Y, and
Z), an angle, and a distance. In return, we are given the resulting X, Y,
and Z elements of the coordinate as an array.
We return an array by declaring the return type of the function as a
variant. As we will learn in the discussion on variables, a variant can
hold any type of value, object, or array of values or objects. We declare
an array of doubles within the function and then we assign the array
variable to the function name. Here's one way to test the PolarPoint
Sub TestPolarPoint ()
Dim StartCen As Point3d
Dim CenPt As Point3d
Dim RotMatrix As Matrix3d
Dim X As Variant
StartCen.Y = 2
StartCen . Z = 0
Set MyCir = Application.CreateEllipseElement2(Nothing . _
StartCen . 1. 1. RotMatrix)
Application . ActiveModelReference . AddElement MyCir
Dim RotAngle As Double
For RotAngle = 0 To 360 Step 30
X = PolarPoint(StartCen.X. StartCen.Y. StartCen . Z. _
DTR(RotAngle). 4)
CenPt.Y = X(l)
CenPt.Z = X(2)
Set MyCir = Application.CreateEllipseElement2(Nothing. _
CenPt . 1. 1. RotMatrix)
Application . ActiveModelReference.AddElement MyCir
Next RotAngle
End Sub


I Chapter 5: Modules, Forms, and Class Modules I

What do we get when we run Testpo 1arPoi nt?

Returning 'Types'
Thus far we have written functions that return either a single value or an
array of values. You can also return types. MicroStation VBA has a
'Point3d' type with three properties: X, Y, and Z. Let's copy and paste the
Polar Point function and make use of this type.
Funct i on Po l arPoint2 (X As Doub l e , Y As Double , Z As Double,
Angle As Do uble, Distance As Doub l e) As Point3d
Dim XC hange As Double
Dim YChange As Double
XChange = Cos(Angle) * Dista nce
YChange = Sin(Angle) * Dista nce
Dim PPoint (O To 2) As Double
Pola rP oint2 . X X + XChange
PolarPo i nt2 . Y Y + YChange
Polar Poi nt2 .Z
End Fun ction

Instead of returning an array as in the previous example, we are

returning a Point3d type. Here is an example that uses the Po 1arPoi nt 2
Sub TestPolarPoint2 ()
Dim Sta r tCen As Po i nt3d
Di m CenPt As Poi nt3d
Di m Rot Matr i x As Matr i x3d
Dim X As Varian t
StartCen.X = 2

I Procedures and Functions I


StartCen . Y = 2
StartCen . Z = 0
Set MyC i r = App i cation.CreateEllipseElement2(Nothing, _
StartCer.,1. 1. RotMatrix)
Applicatior . ActiveModelReference . AddElement MyCir
Dim RotAngle As Double
For RotA~g l e = 0 To 360 Step 30
Cen Pt = PolarPoi nt2 ( Sta r t Cen . X, St ar t Ce n . Y, StartC en . Z, _
DT R(Rot Angl e ) , 4 )
Set MyC i r = App l ication . Create E llipse E lement2(Nothi~g, _
CenPt , 1 , 1, Rot Matrix)
Application . Act i veModelReference . AddE l ement MyCir
Next RotAngle
End Sub

Returning Objects
One additional return type is worth mentioning. In addition to
returning values and types, a function can return objects. Here is one
Functi on GetExcelWS ( ) As Object
Dim Exc el App As Obje ct
Se t Excel App = GetObje ct ( , "E xcel. Appli cat ion " )
Set GetEx celW S = Exce l Ap p . ac t ive s heet
En d Funct io n

This function gets the active worksheet in Microsoft Excel. Excel must
be running for this function to work correctly. How do we use it? Let's
take a look.
Sub Te s tGetE xcelWS ()
Dim MyWS As Object
Dim Ce 111 As Doub l e
Dim Ce 112 As Double
Dim Cel13 As Double
Set MyWS = GetExcelWS
MyWS.Range( "B2 " )
Ce 111
MyWS.Range( "C2")
Ce 112
MyWS . Range( "D2")
End Sub


I Chapter 5: Modul es, Forms, and Class Modu les I

This procedure gets the values of three cells in Excel. It really is very easy
to get data from or write data to Excel. We will discuss more on working
with Microsoft Excel later in this book.

8yVai and ByRef

We have seen how we can provide parameters when we call procedures
and functions. By default, values are passed by reference. The other way
values can be passed is by value. What do these mean?
Sub GetThreeVals(x As Dou bl e , Y As Double, Z As Double)

Y= 2
Z= 3
End Su b

This procedure accepts three parameters. Inside the code, we use the
parameter's names and assign values to them. It is important to
understand this is because using variables directly in this manner will
change the values in the function or procedure that calls this procedure.
Sub TestGetThree Vals ( )
Di m A As Doubl e
Dim B As Dou bl e
Dim e As Dou bl e
A = 100
B = 200
e = 300
GetThreeVals A, B, e
End Sub

Here we have variables A, B, and C. We assign values of 100, 200, and

300 respectively. Then we use these variables (A, B, and C) when we call
GetThreeVals. Since the procedure GetThreeVals has its parameters
declared without the keyword "ByVal", the values are passed into the
procedure "ByRef". ByRef means the values of the parameters may be
modified inside the procedure. And if they are modified in the
procedure, the variables will maintain these values outside of the
procedure. So, before the line of code "GetThree Vals A, B, C" is
executed, the values of A, B, and Care 100, 200, and 300. After
GetThreeVal s is executed, the values of A, B, and Care 1,2, and 3.

I Procedu res and Functions I


Ge tThr eeV a1s changes the values of the parameters that are passed in.
This can be a powerful feature if it is used correctly. It can also cause a
great deal of confusion if it is not understood. Suddenly, variables that
were holding one value could hold another value.

If we do not want a function or procedure to change the values of the

variables passed as parameters, there are a couple of ways we can do this.
The fi rst technique requires discipline on our part. The second
technique is a more definite method.
Sub Get ThreeVa l s2 (X As Doub l e , Y As Doub l e, Z As Doub l e)
Di m db l X As Doub l e
Dim dblY As Double
Dim dblZ As Double
db l Y Y
db l Y 2
End Sub

Instead of manipulating the parameters directly, we place the values of

the parameters into variables declared inside the procedure. Then we
manipulate these local variables. This keeps us from changing the
Another way to maintain the integrity of the parameters passed into our
fun ctions and procedures is to declare them explicitly as "ByVal".
Sub GetThreeVals3 ( ByVal X As Dou ble , ByV al Y As Double, _
ByVa l Z As Double)

Y 2
Z 3
End Sub

Taking the additional step of declaring a parameter as ByVal guarantees

the integrity of the parameters.


I Chapter 5: Modules, Forms, and Class Modules I

Declaring Variables
Variables are used extensively throughout our code. Variables are
declared with a name and a type. We will learn more about this in the
next chapter. What is important to understand now is that variables
have a scope. There is a pre-determined amount of time when a variable
can be used. The variable's scope depends on where it is declared and
what keywords (if any) are used when it is declared. There are two places
where variables can be declared. One place is inside the procedures and
functions in which they will be used. We have seen numerous examples
of this so far. The other place we declare variables is in the General
Declarations area of code modules, forms, and class modules.

In the General Declarations area of this code module, I declared three

variables as the same type (as doubles) but used different keywords to
declare them: Dim, Private, and Public.
Dim St artPo i ntX as Double

Declaring a variable with Dim" in the General Declarations area of a

code module or form code area means the variable is only available for
use from within that module, form, or class module. StartPointX was
declared by using the Dim" keyword so, again, it can only be used from
within the module or form in which it is declared.
'Private' has the same effect as using 'Dim: You can use the variable
within any function or procedure in the code area in which it is
declared. One function can set its value and another function in the
same module, form, or class can read the value.
'Public' variable declarations behave differently dep ending on where the
declarations are made.

I Review I


Forms - Publicly declared variables are in scop e wh en the form is in

scope (usu ally only wh en it is displayed) . Oth er areas of a project can
access the variable through th e form's name. For example,
UserForml . TestVariable

4. 5

We can use th e variable TestVa ri ab le only by addressing it th rough th e

form an d the form m ust b e 'in scope' fo r this to work.
Modules - Publicly declared variables are in scope for all areas within
the sam e project.
Classes - Publicly declared variables are seen as read/write properties
for the class.

Option Explicit
By default, if we attempt to use a
variable that is not declared, it
inherits the type of 'Variant: We
can force ourselves to declare
variables by using "Option
Declarations area of modules,
forms, and classes.
In this example, we have declared "Option
Explicit" in the General D eclarations area.
When we attempt to run the macro test shown
above we get an error.

Compile error:

Variable not defined

oK m )i [


To avoid this error, we need to declare X as a

double, integer, or long. More on variable types
in the next chapter.

[B Write code as procedures, functions, or inside user form events.
[B In procedures and functions utilize required and optional

[B In functions you can return values, arrays, types, and obj ects.


I Chapter 5: Modules, Forms, and Class Modules I


In procedures and functions you can make changes to the

variables passed into them as parameters if the parameters are
declared as "ByRef". Declare a parameter as "ByVal" to keep the
variable's value from changing.


Declare variables in procedures, functions, and events or in the

General Declarations area. The scope of these variables depends
on where they are declared and what keywords accompany the


What is N? N is a variable. In the above equation it represents a number.

If we were to solve for N we would get a value of 3.
"Learning MicroStation VBA " & N & " Easy:'
What is N? N is a variable. In the above
equation it represents a string of characters.
Learning MicroStation VBA 15 Easy,

What string of characters does it represent?


In this chapter:

Standard VBA Variable Types


MicroStation-Specific Variable Types


Assigning Values and Setting Objects






Variable Names


Option Explicit


Using Variables



I Chapter 6: Variables I
A variable is a name that represents a value or an object. The examples
above show variables with a name of N. In one instance the variable
holds a numeric value. In the other it holds a string of characters. In
general, we know in advance what type of value or obj ect a variable will
be representing. Since we know this, we specify what type of variable we
will use by declaring it.
Dim N as I nteger
N= 7 - 3 - 1

Here, we declare the variable N as an integer. This means it will be a

whole number between -32,768 and 32,767.
Di m N As String

" I S"

MsgBox "L earning Mi croStation VBA " & N & " Easy ."

Here we declare N as a string. A string is a group of characters. After a

variable is assigned a value, you can use it in the place of a number, text,
or some other type of value or object.
We will use variables extensively throughout this book. Let's examine
some of the more common types of variables available to us.

Di m PageNumber as Integer

We said that an integer is a whole number between -32,768 and 32,767.

If we need a variable to hold a value greater than or less than the range of
an integer, we must declare it as something different.

Dim MySalary as Long


A long is a whole number between -2,147,483,648 and 2,147,483,647.

These numbers are much larger than those in the range of an integer. It

I Standard VBA Variable Types I


requires more memory to allow for this greater range of numbers so we

should only use it when we need it.

Dim HoursToLearnVBA as Double
HoursToLearnVBA = 36 . 25

A double is also called a double precision flo ating point number. What
does that mean? It means the precision available for a double is twice the
precision available for single (also a variable type but not used as much)
and the decimal point can float to allow for greater precision of small
numbers or larger numbers with less precision. It is important to
understand the "floating point" portion of the description. If we expect
an extremely large number to be extremely accurate, we may not only be
disappointed but we could have less accurate results than we expected.
Consider this next macro, Var i ab l eTestC . It has a variable named N
declared as a double in which the numbers "1234567890123456789"
have the decimal in a different position each time with the last two
numbers shown are '46'. VBA rounds the '456' number to '46' because a
double variable is given a specific amount of memory in which to keep
its value. When we attempt to put more in it than it can handle, it rounds
the number to something it can hold.
Sub Vari abl eTestC ()
Dim N As Doub l e


12 . 3456789012346

123 . 45 678 9012346

1234 . 56789012346

12345 . 6789012346

123456 . 789012346

1234567 . 89012346

12345678 . 9012346

123456789 . 012346

1234567890 . 12346

12345678901 . 2346


I Chapter 6: Variab les I



123456789 0123 . 46


End Sub

Doubles can hold very precise numbers but as the value of the number
increases, the precision decreases. This is something to keep in the back
of your mind as you develop applications.

Dim ICanLearnThis as Boolean


A Boolean data type can hold one of two values: True or False.

Dim XMRelease Dat e as Date
XMRe l easeD ate

" 5119/2006 8 : 00 : 00 AM"

A Date data type holds a Date/Time value.

Di m MyLevel Name as St r ing


A string data type contains text. Letters, numbers, and other characters
we on our computer keyboards can be held inside this variable. We have
seen that numeric variable types have ranges of values. This is because
their data types have a predefined amount of memory set aside for each
variable. Strings are no different. So how many characters can be held
inside a string variable? Approximately 2 billion (2,000,000,000). That is
a lot of characters.

Di m MyExcelApp as Obje ct
Set My Ex c e 1APP

Get 0 bj e ct(. .. Ex c e 1 . APP1 i ca t i on " )

Object type variables point to objects. The variable MyExcelApp, for

example, could point to an instance of Microsoft Excel, an application.
Microsoft Excel is an object with specific properties, methods, and

I MicroStation-Specific Variable Types I


events. Others objects have their own unique properties, methods, and
events. When we declare a variable as an object, it is a generic object
without any previous knowledge of its properties, methods, or events.
Only after we set the variable to an object does it know what kind of an
object it is as well as its other attributes.

Di m PointArray as Variant

Variables declared as a variant can hold any type of value, point to any
type of object, or even contain an array of values.


The variable types we have discussed are standard VBA variable types.
They can be used in MicroStation VBA, in Excel VBA, in Word VBA, or
in Access VBA. Let's consider some of the variable types specific to
MicroStation that we will regularly use.

Dim MSApp As Appl i cat i on
Set MSApp = Application

The application variable type points to the MicroStation application. It

is the top level object when dealing with MicroStation. A few of the
things we can do from a variable declared and set to the MicroStation
application are:

Get the ActiveDesignFile property


Get the ActiveModelReference property


Get the ActiveSettings object and its properties


Get the ExecutingVBProject object and its properties


Get the UserName property


Get the left, top, width, and height properties


I Chapte r 6: Vari ables I

Dim MyDGN As OesignFile
Se t MyDGN

Application.ActiveDesign File

The DesignFile object refers to a MicroStation DGN file. Top-level DGN

properties and collections are available to us via the DesignFile object.

Get and set the Author, Client, Comments, Company,

Keywords, Manager, Subject, and Title Properties


Get the ForrnatMajorVersion and FormatMinorVersion



Get the Levels collection


Get the Models collection


Get the N arne and Path properties

Di m MyM odel As Mode l Refe r ence
Set MyM ode l

Ap pli cat i on . Ac tiv eMod el Refe rence

The ModelReference object is where the rubber meets the road. When
we draw inside a file, we do it through the ModelReference object.
When we want to find out what is in a file, we do it through the
ModelReference object. We will work extensively with this object
throughout this book.

Di m My Level As Level
Set MyLevel

Application.ActiveDes i gnFile.Levels(l)

Levels allow us to divide our designs into groups of objects. We usually

group our objects based on their specific typ e of geometry or
annotation. Road centerlines may be placed on one level, Lot numbers
on another, and title block lines on another, etc. Here are a few of the
properties we can get and set from the level obj ect:






I MicroStation-Specific Variable Types I

[B ElementLineWeight
[B IsActive
[B IsDisplayed
[B IsFrozen
[B IsLocked
[B Name
[B Number
[B Plot

Di m MyLine As Li neElement
Set MyLine

App l icat i on . CreateLineElement2(Noth i ng .

Po i nt3dFromXYZ(0. O. 0) . Po i nt3dFromXYZ(4 . 5 . 6))

Appl i cation . Act i veMode l Refe r ence . AddE l ement My Li ne

A LineElement is created with a start point and an end point. After it is

created, we can add it to our model.

Di m MyC i rc l e As El l i pseElement
Dim Rot Matr i x As Matrix3d
Set MyCircle


Point3dFromXYZ(0 . O. 0) . 1.5 . 1.5 . RotMatrix)

Application . ActiveMo delReference . AddE l ement MyCircle

Lines, circles, and arcs form the basis of much of the geometry found in
our MicroStation files. From MicroStation's perspective, circles are
essentially ellipses with equal major and minor radii. The code shown
above draws a circle centered at (0,0,0) with a radius of 1.5.

Dim MyArc As ArcElement
Dim RotMatrix As Matrix3d
Set MyArc

CreateArcElement2(Nothing .
Point3dFromXYZ ( O. O. 0). 1.75. 1.75. _



I Chapter 6: Variables I
RotMatrix, Radians(45 ) , Radia ns (90) )
App li cation.ActiveModelRefer ence . Add Element MyA r c

We are given several ways to create a new LineElement, EllipseElement,

and ArcElement. In this example, we create a new ArcElement by
providing a center point, major and minor radii, a start angle, and a
sweep angle.

Dim MyT ext As Text Eleme nt
Di m Rot Matr i x As Matr i x3d
Set MyText

Create Text Elementl{Nothing , "M icroStation VBA" , _

Point3d Fro mXY Z( O, 0 , 0), Ro t Ma t ri x)

Appl i ca ti on . ActiveModelReference . AddEl ement MyText

The TextElement object needs the text to display and a starting point.
When it is created we can set other properties such as the color, level,
and textstyle (which includes font, size, etc.) .
We use many more types of objects when programming MicroStation in
VBA and there is much more to learn about the objects we have just
introduced. They will be covered in greater detail as we continue to
learn MicroStation VBA.


Value-type variables and object-type variables are declared similarly.
When giving the variables values or setting them to objects, there is one
major difference.
Di m LevelName As Str in g
Level Name

"Easeme nt"

Di m EasementLeve l As Level
Set EasementLevel =
Act i veDes i gn Fi 1e . Add NewLevel (L evel Name)

Here we have tV'TO variables. One is declared as a string and the other as a
We assign a value to the LevelName variable by stating the variable by
name, using an equal sign, and then the value we want it to have. When
we use the Level object, we use the keyword 'Set' to set the variable to an

I Arrays I


object. We only use 'Set' when we are setting a variable to an object.

After it is set to an object we can address it by its name without using

When we think about an array in MicroStation, we think about taking
an element and copying it multiple times. An array in VBA is a single
variable name with multiple elements in it.
Dim StartPo i nt(O to 2) as Doub l e



5. 6

StartPoin t( 2 )


In this example, we declared a variable as an array with three elements

numbered 0, 1, and 2. We can address the elements individually by
specifying their index within the array.
In the next example, I created an array where each Point3 D type has an
X property, a Y property, and a Z property. Notice how I addressed each
element in the array by its index (0 and 1) and then addressed the X, Y,
and Z properties.
Sub ArrayTestA ()
Dim MyVerticies(O To 1 ) As Point3d
Dim MyLine As Li ne Element
My Ve r tic i e s ( 0 ) . X

My Ve r tic i e s ( 0 ) . Y

My Ve r tic i e s ( 0 ) . Z

My Ve r tic i e s (1

MyVerticies(l) . Y

MyVerticies(l) .Z

Set MyLine

) .

CreateLineElement1(Nothing . MyVerticies)

ActiveModelReference . AddElement MyLine

End Sub


I Chapter 6: Variab les I

A constant is similar to a variable with one significant difference: a
constant value does not change.
Const PI As Double =


You can declare constants as public in the General Declarations area of a

code module, as shown above, from within individual procedures and
functio ns. Constants are useful any time you need a value that does not
change. For example, if you are writing a program that labels line lengths
in a design file, you could specify a constant for the distance the text is to
be offset from the line. A constant can also provide a units conversion
factor, such as from inches to cubits.
Another reason to use a constant is for calculations that make routine
references to specific values, such as a Shim Width value in multiple
calculations. Instead of using a value of 0.6 when we make the
calculations, we can declare a constant with a name of ShimWidth and
assign it a constant value of 0.6. This makes our code easier to read and
allows us to change our ShimWidth value in only one place (where the
constant is declared) instead of wherever the value is used.

Thousands of pages of text have been devoted to naming variables. The
best place to start this discussion is with the rules imposed on us by

IB Variables must begin with an alpha character (A through Z) .

IB Variable names cannot contain spaces.
IB Name characters are A-Z, 0-9, and _ (underscore).
IB Variable names must be 255 characters or less.
IB Variable names cannot be keywords (such as 'Dim.', 'New', 'Left',
'Right', 'Form', 'Public').
IB Letters used in variable names can be uppercase or lowercase.

I Variable Names I


Based on the rules already identified, here are a few variable declarations
that work:


As LineElement

Dim txteMyText As TextEl ement

Dill strName As String
Dim db l Startx As Double
Dim intLevelNumber As Integer
Dim pt3dSta rtPoi nt As Poi nt3d

Each of the declarations shown above are legitimate variable

declarations. They follow the rules. The first, myLine, is slightly different
than the others. Each of the other declared variables begins with
characters that identify the type of variable. strName says the variable
type is a string. dblStartX says we are working with a double type
It is important to know if a project requires using variable naming

conventions. A naming convention is an additional set of rules on how

to name variables. For example, one convention may state that each
variable name begin with three characters followed by an underscore L)
character, then a name consisting of no more than seven characters.
Another convention may not use an underscore. Yet another convention
may require that the scope of the variable be identified inside the
variable's name.
As mentioned, many pages have been devoted to the topic of variable
naming conventions, so we will not spend much time here on the
subject. You should understand that naming a variable myLine or
lineMyLine or line123 does not cause your program to work any
differently than naming it elem li ne_LineA.
Naming conventions can extend beyond variable names. Procedure
names, function names, and control names can also be within the scope
of a naming convention.
Here is a link to a web page that discusses variable naming conventions:!html!
decon VariableNames. asp
Another way to become familiar with naming conventions is to search
online for "variable naming convention" or "Hungarian Notation".


I Chapte r 6: Vari ables I

Case Sensitivity
Consider the following variable declarations:
Dim myLine As LineElement
Dim MYline As LineElement

Since VBA is not concerned with capitalization in variable names, the

two variables declared above are the same. When we use a variable
inside VBA, VBA automatically changes the capitalization to that used
in the declaration. Some programmers use this to make sure they do not
introduce typos into their code. They may always use a capital letter
somewhere in the variable name when they declare it and then type all
lower-case when they use it. When a line of code is complete and the
cursor is moved to the next line of code, VBA automatically updates the
capitalization of the variable that had been typed in lowercase.

We have spoken for a while about variable types and declaring variables.
There are many arguments as to why we should declare our variables.
However, VBA does not force us to do so. It is possible to use a variable
even if it is not formally declared. When we do this, the variable takes on
the characteristics of a specific variable type when it is first used. For
example, it will be a 'Variant Double' if its value is 1.23456. Or it will
become a 'Variant String' if its value is "owhatafooliarn". One way we can
make sure we declare our variables is to use "Option Explicit" in the

I Using Variables I


General Declarations area of our m odules. Another way is to go to the

VBA menu Tools> Options.

r Editor L~ditor Form~lGener~l.~'?0ing~. ___________.____._.


Code Settings

~ Auto Syntax Check

~ Auto Indent

Q Require Variable Declaration

Tab Width:

~uto List Members


~ Auto Quick I nfo

~ Auto Data Tips
Window Settings

~Drag-and-Drop Text Editing

~ Default t o full Module View
~ Procedure Separator

OK .! I




Checking the 'Require Variable Declaration' button causes VBA to enter

"Option Explicit" for us.

After a variable is declared and a value is applied to it or it is set to an
object, the variable can be used any time the value is needed.
Sub Variab l eTestD ()
Dim MySalary As Dou bl e
Dim MyHourly As Double



MySalary I 52 I 40

MsgBox "My Hourly Rate is " & FormatCurrency(MyHourly. _

2 . vbFalse . vbFalse . vbTrue)
End Sub

l'ly Hourly Rate is $593.54

Here we have two variables. One of them (MySa lary)

is given a value of 1234567. We then use MySa lary to
calculate MyHourly. We then use the standard VBA
function FormatCurrency to convert the variable
MyHourly to a two-decimal place currency value


I Chapter 6: Variables I
and concatenate "My Hourly Rate is" with the result of the
FormatCurrency function.

Variables are names that hold values or refer to objects. Variables
declared within a function, procedure, or event are local to that function
and cannot be used outside of it. Variables declared in the General
Declarations area of a form or code module can be used from within the
form or code module in which they are declared. Variables declared as
'Public' inside a code module can be used anywhere in the VBA project.
Variables declared as 'Public' in class modules become read/write
properties of that class module.
We will use variables extensively throughout this book. After all,
without variables everything would be static - nothing could change.
Lines would always be drawn from the same point to the same point and
text would always be inserted at the same point and would always say
the same thing.

Working With Text

We work with text every day. This book is composed of text: words,
phrases, sentences, paragraphs. The ability to work with text is
invaluable in our programming.
Recall that the type of variable that deals with text is a String.
Sub TextWorkOl ()
Dim BookTitle As String
BookTitle = " Learning MicroStation VBA "
MsgBox UCase(BookTitle)
MsgBox LCa se (B oo kTitle )
MsgBox Left(BookTitle , 12)
MsgBox Right(BookT i tle , 12)

End Sub
In this example, we have a
variable named BookTitle that
is declared as a String. It is
given a value of "Learning
different functions are then
BookTitle as a parameter and
the result displays in four






oStation IIBA

learning microstation "ba


I Chapter 7: Working With Text I


Let's take a look at each of the VBA functions that deal with text
(Strings) one-by-one.

Fu nction UCase(Stri ng)
The UCa se function converts the supplied string to upper case.
Su b TextWork02 ( )
Di m st r NewLev el As St ring
strNew Level = In put Box( "E nt er New Leve l Name :" )
strNewLevel = UCase( strNewLevel )
Application . ActiveDes ignFi le.AddNew Level strNewLevel
End Sub

.' oK

Enter New Level Name:


[ Cancel)

In this example we use an
InputBox to allow the user to
enter the name of a new level. We
then convert it to upper case and
use it to add a new level
(AddN ewL eve 1) to the active design



.. ,~,

~' :-~/-:;-: ::.),-~-'-"-"~\,,,,:(

~,l!!~l,~~",i!ger . : . . 'v ,;.", ,~j~>'

Levels . Filter


'@ 'f(I I Ii@


= - -- -- -,- -- -

ISymbology IByLevel :\'j II !~ (no



j Number

[ @All Levels
- [> Fillers


. (.:".


Functi on LCas e (Stri ng)
The LCase function converts the supplied string to lower case.
Sub TextWork03 ()
Debug .P r i nt LCase( "LCase Lowers Capita l Letters ." )
End Su b

I VBA String Functions I

is used to
place text in
Window. It is
often used to
display text to
aid in
To view the
Window, go
to the VBA
menu View>





capital letters.

In this example we used text directly in the function instead of assigning

the text to a variable.

Function StrConv(String . Convers ion As VbStrConv. _
[Lo cale IO As LongJ)

StrConv is used to convert the provided string through a variety of

parameters. The constant most used with StrConv is 'vbProperCase'.
Sub TextWork04 ()
Dim BookTitle As Stri ng
BookTitle = "learning microstation vba "
MsgBox StrConv(BookTitle. vb ProperCase)
End Sub




This example uses the vbProperCase constant

to capitalize the first letter of each word.


WeekOayName, WeekOayN umber

Function WeekdayName( Weekday As Long.
[Abbreviate As Boolean = FalseJ. _
[FirstDayOfWeek As VbDayOfWeek = vbUseSystemDayOfWeek]) _
As String

Each day of the week (Sunday through Saturday) has a number assigned
to it. The Weekday Name function takes that number and converts it to the
day's name.
Sub TextWork05 ()
Dim TodaysDate As Date
Dim WeekDayNumber As Long
TodaysDate = Now
WeekDayNumber = Weekday(TodaysDate)
MsgBox WeekdayName(Week DayNumber)
MsgBox WeekdayName(WeekDayNumber . True)
End Sub


I Chapter 7: Working With Text I

The first parameter supplied to
WeekdayName is the day number.
'Abbreviate', is optional with a
default of false. When we
supply a value of true, the
WeekDayName is abbreviated.



r----'OK-' ')1


Function Mo nthName(Month As Long, _
[Abbreviate As Boolean = False]) As String
The MonthName function is similar to the
WeekdayName function but as the name
implies, it returns the name of the month
instead of the name of the day.

Sub TextWork05B ()
Dim MonthNum As Long
For MonthNum = 1 To 12
Debug.Print MonthName(MonthNum)
Next MonthNum
End Sub



LTrim, RTrim, Trim

Function LTrim(String)
Function RTrim(String)
Funct ion Trim(String)
Use the Tri mfunctions to remove spaces from the beginning, end, and
both beginning and end of a string.
Sub TextWork06 ()
Di m StringToTrim As String
StringToTrim = Trim Functions Trim Space Characters .
Debug . Print LTrim(StringToTrim)
Debug.Print RTrim(StringToTrim)
Debug.Print Tri m(StringToTrim)
End Sub

I VBA String Functions I


Function StrComp(Stringl, String2, _
[Compare As VbCompareMethod = vbBinaryCompare ] )
The need to compare two pieces of text is common. Is "Sidewalk" the
same as "SIDEWALK"? Not always.

TextWork07 ()

Di m str NewLevel As String

Dim l vlExistLevel As Level
strNewLevel = InputBox( " Enter New Level Name: " )
For Eac h lvlExistLevel In Appl ica tion.ActiveDes i gnF il e .Le ve ls
I f StrComp(strNewLevel , lvlExistLevel.Name , _
vbTextCompare) = 0 Then
MsgBox " The level" & strNew Level & " already ex i sts ."
Exit Sub
End If
App li catio n . ActiveDesignFi l e .A ddNewLevel strNewLevel
End Sub

This procedure asks the user for a new level name. It compares the
newly-entered name with the name of each existing level name. If it
finds a match, a MessageBox displays and we exit the procedure.

StrComp allows us to specify how th e provided text is to be compared. In

the above example, the constant 'vbTextCompare' returns a value of zero
(0) when the characters are the same, independent of the capitalization .
With 'vbTextCompare', "SWalk" and "swalk" are the same.

TextWork08 ( )

Debug . Print
Debug . Print
End Sub

StrComp( " SWal k" ,

StrComp("swalk ",
St r Comp( " SWalk " .
StrComp( " swalk "

" swa 1 k "

" SWa 1 k "
" swa 1 k "
" SWa 1 k "




I Chapter 7: Working With Text I

St rComp lets us know whether the provided text is the same but it also
tells us which text comes before the other. It is often used for sorting text

Here is one more example of StrComp, called a bubble sort. It takes an
array of strings and sorts them alphabetically. This technique is a little
more advanced, so it may be good to return to it after we have learned
more VBA programming.
Sub Bubb l eSort ()
Dim strNms(O To 7) As String
"Jerry "
"Ca ndice "
"Brandon "
"Kyle "
"B enjam i n"
"J acob "
st r Nms(6)
"Nathan "
"Ol i via "
Di m MadeChange As Boolean
Di m tmp Name As St r i ng
Di m I As Long
MadeChange = True
While MadeChange = True
MadeChange = False
For I = LBound(strNms) To UBound(strNms) - 1
If StrComp(strNms(I), strNms(I + I),
vbBinaryCompare) = 1 Then
tmpName = strNms(I)
strNms(I) = strNms(I + 1)
strNms(I + 1) = tmpName
MadeChange = True
End If
Next I
For I = LBound(strNms) To UBound(strNms)
Debug . Print I & " " & strNms(I)
Next I
End Sub

Let's break down this procedure into segments.

I VBA String Functions I

Dim str Nms(O To 7) As Str i ng
st r Nms(O)
"Jerry "
st r Nms( l )
"Candice "
" Ky 1 e "
"Jacob "
strN ms(5)
"Nathan "
strN ms(6)
s tr Nms (7)
" 01 i v i a "

The firs t thing we do is declare an array of strings and give each element
in the array a value.
Dim MadeChange As Boolean
Di m tmpName As String
Dim I As Long
MadeChange = True

Now we are setting up for the sorting portion of our routine. We want to
run through the sorting portion at least once so we set the MadeChange
variable to True and then immediately begin a While ... Wend routine.
While MadeChange = True
MadeChange = False
For I = LBound(strNms) To UBound(strNms) - 1
If StrComp(strNms(I) , strNms(] + 1 ),
vbBinaryCompare) = 1 Then
tmpName = strN ms(I)
strNms(I) = strNms(I + 1)
strNms(I + 1) = tmpName
MadeChange = Tr ue
End If
Nex t I

The above section is the heart of the routine. We continue to look at

each value in the st rNms array and compare it to the value in the array
just after it. If the value we are looking at is alphabetically greater than
the one after it, we swap the two elements in the array and set the
MadeChange variable to True. Only after each value is examined and a
swap is not made do we continue with the next segment of our code.
For I

= LBound(strN ms) To UBound(strNms )

Debug . Print I & " " & strNms(Il
Next I

The last little segment of code prints out the elements in the strNms
variable array in their sorted condition.



I Chapter 7: Workin g With Text I

Come back to this procedure after we have spent a little more time
working with VBA and it will be easier to follow. Sorting text is
accomplished easily and quickly using a Bubble Sort with the St rComp

Function Len(Expression)
The Len function tells us how many characters are in a string.
Sub TextWork09 ()
Dim LevelName As String
LevelName = InputBox( "E nter new level name: " &
(Must be 8 characters)")
If Len(LevelName) <> 8 Then
MsgBox "The l eve l na me must be 8 characters. Try again. "
End If
End Sub

In this example, we ask the user for a new level name. We also request
that the name be eight characters long. After the value is entered, we use
the Len function to check the length. If it is not eight characters >
means not equal to), we ask the user to try again.

Fu nc ti on Le ft( String . Length As Long )
The Left fun ction allows us to provide a string and specify how many
characters we want returned to us beginning with the first character
(left) of the string.
Sub TextWorklO ()
Dim FilePath As String
Di m FileDrive As String
FilePath = Applicat ion. ActiveDesignFile . Path
FileDrive = LeftCFilePath . 1)
MsgBox "The current file is on the" & FileDrive & " drive. "
End Sub

Here, we get the path of the active design file. We then look at the first
character of the FilePath variable and put it into the Fi leDrive variable. A
MessageBox then displays the Fi leDrive variable with some other text.

Function Right(String. Length As Long)

I VBA String Functions I


You guessed it. The Ri gh t function is a companion to the Left function.

It gives us the n umber of characters we ask for beginning with the last
(right) character.
Sub TextWorkll()
Dim FileName As String
FileName = Dir("C;\Program Files\Bentley\MicroStation\*.z", vbArch'vel

While FileName <> ""

MsgBox FileName & " is a " & Right(FileName, 3) & " file ."
FileName = Dir
End Sub




all of the files ill the C:\Program

Files \Bentley\MicroStation folder and their file extensions in message
boxes. Since there are quite a few, it will take a long time to click the OK
button on each message box.

HINT: When a program is executing, we can break into the execution of

the code by holding down the Control key CTRL and press the
<Break> key. The break key is normally in the upper right-hand corner
of the keyboard.

Fun ct i on Mid ( String, Sta rt As Lo ng , [L ength] )
The Mid function allows us to specify a string and the index of the
starting character we want to have returned to us. We have the option of
specifyi ng how many characters we want to have returned or we can
leave the Length parameter empty and have Mi d return all of the
characters following the specified 'Start' character index.
Sub TextWork12 ()
Di m Boo kTi tle As String
BookT i tle = "Learn i ng MicroStation VBA "
Deb ug . Print Mid(BookTitle , 3 , 6)
Debug . Print Mid(BookTitle, 6)
Debug . Print Mid(BookT i tle , I nStr(I , BookTitle , " " ) + 1)
End Sub


I Chapter 7: Working With Text I

We used the Mi d function three times in
the above example, each time a little
differently. The first time we asked Mi d
ing MicroStation VBA
MicroStation VBA
to begin at the third character and to
return six characters in all. The second
time we asked fo r the sixth character
and each character after it. Notice how
we leave out the Length parameter entirely. The third time we did not
hard-code the beginning character. We used the InStr function to look
for the first space in the variable BookTitle and added one (1) to the
character number so we began with the character after the first space.
The length is not provided so we get everything after the space.

Function Replace(Expression As String, Find As String, _
Replace As String, _
[Start As Long = 1J, [Count As Lo ng = -lJ, _
[Compare As VbCompareMethod = vbBinaryCompareJ) As St ring
The Replace function allows us to provide a string, a character or
characters to look for, and a replacement for the character(s) we are
looking for.
Sub TextWork13()
Dim Fi lePath As String
Dim FileP ath2 As String
FilePath = Application.ActiveDesignFile .Fu llName
Fi lePath2 = Replace( FilePath, " \ ", "II " )
MsgBox Fil ePa th & vbCr & "t urn s int o" & vbC r & Fi l ePath2
End Sub

In this example we look for a backslash

then replace each one found with two
forward slashes.

Function In St r( [ StartJ, _
[S tr in g1J, [S t r in g2 J, _
[C ompar e As VbCom pa reM et hod

C:\Microstation VBA\docs\chapter07.dgn
turns into
c: //Microstation VBA//docs//chapter07. dgn


vbBin aryCom par eJ )

I nS t r helps us identify where a character or group of characters appear

in a string. For example, if we look in the string ''ABeD'' for string "e",
InS t r returns the number 3 because "e" is the third character in

I VBA String Functions I


''ABeD''. If the character(s) we are looking for is notfound, InStr returns

a value of zero (0).
Su:) TextWork14()
Di m Fu'l ame As Stri ng
Dim FirstSpace As Long
Dim FirstName As String
FullName = InputBox( "En ter your full name.")
FirstSpace = InStr(l, FullName, " " )
FirstName = Left(FullName, FirstSpace - 1)
MsgBox "Your first name is " & FirstName &
End Sub

Here is another simple example of the use of the InS t r function. We ask
the user to enter his/her full name. We look for the first space in the
entered name, then get everything beginning from the start of the
Fu llName up to the character before the FirstSpace.
Sub Tex tWork15 ()
Dim FilePath As String
Dim FirstFolderPath As String
Dim SecondBackSlash As Long
FilePath = ActiveDesignF il e.Fu l lName
SecondBackSlas h
InStr(4, FilePath, "\ " )
Left ( Fil ePath , SecondBackSlash)
MsgBox Fi lePath & vbCr & FirstFolderPath
End Sub

Often when we use InS t r we begin looking

at the first character. This is why the
c: \~licrostation VBA\docs\chapter07. dgn
C:\~licrostation VBA\
number one (1) appears as the first
parameter. In this example, however, we
rm oK mjl
begin by looking at the fourth character.
This is because we know that the third
character of a file path will likely be a backslash. The goal with
TextWork15 is to get the path of the root folder of the active DGN file.
One more example:
Sub TextWork16 ()
Dim TextElem As Tex t El ement
Dim MyMod As ModelReference
Set MyMod = Applicat i on.ActiveModelReference
Dim MyElems As ElementEnumerator
Set MyElems = MyMod . GetSelectedElements
Dim MyElem As Element


I Chapter 7: Working With Text I

While MyElems.MoveNext
Set MyElem = MyElem s. Current
Select Case MyE l em. Type
Case msdElementTypeText
Set TextElem = MyElem
If InStr(l . Text Elem . Text. "[BY]") > 0 Then
Text Elem. Te xt = Repl ace(T ext Elem.T ext . _
"[BY]". "JKW " )
TextElem . Rewrite
End If
En d Select
End Sub

In this example we look at each selected element in the active file. If we

find a text element selected, we use In St r to see if a particular string is in
the text element. If it is, InStr returns a number greater than zero (0).
When we know the search string is inside the text element, we use the
Replace function to replace "[BY]" with "JKW".
This is one way to perform 'Search and Replace' operations on our files.
You could use this when you need to place the name of a contractor in
your file but the file is created before the contract is awarded. Simply use
a tag, such as [CONTRACTOR], then replace it later with the name of
the contractor.

Fun ction InStrRev(StringCheck As String. _
StringMatch As String . _
[Start As Long = -lJ. _
[Compare As VbC ompareMethod = vbBinaryCo mpa reJ) As Long

InStrRev, as the name implies, looks at the end of a string first instead of
the beginning. This is the reverse of InS t r which begins looking at the
beginning. Here is one way to use it:
Su b Text Work17 ( )
Di m FilePat h As String
Di m Fol derName As Str i ng
FileP at h = Act iv eDe s ig nFile. Pa t h
Fo l de r Name = Mi d( FilePath . InSt r Rev( Fi le Path . " \ " ) + 1)
MsgBox "The current file is in t he " & Foldcr Name &
"f ol der. "
End Sub

I VBA String Functions I


We get the path ofth e current file, then use the InStrRev function inside
a ~1 i d function to get the location of the fi rst backslash we find. Since we
don't want to display the folder name beginning with the backslash, we
add one (1) in our Mi d function to get the characters following the

Split and Join

Function Split(Expression As String. [DelimiterJ. _
[Limit As Long = -lJ. _
[Compare As VbCompareMethod = vbBinaryCompareJ)
Function Join(SourceArray. [D el i miter J) As String
Sp 1it is used to take a String and split it into a text array.

J 0 intakes an array and joins it into a String. We specify a delimiter fo r

each call. Let's take a look at on e way to use Sp1i t and J 0 i n.
Sub Text Work 18()
Dim FilePath As Str i ng
Di m Ne wTex tFil e Path As Str i ng
Dim xSpl i t As Variant
FilePath = Activ eDes i gnFile . Full Name
xSp l it = Sp l it(F il ePath . "\ " )
xSplit(UBound(xSplit)) = xSplit(UBound(xSplit)) & " .extract "
Ne wT ext Fi le Pat h = J oi n(xSp lit. "\ " )
Ope n NewTex t FilePath For Output As #1
Pr i nt #1 . Fi lePath
Cl ose #1
End Sub

In this example we want to create a new ASCII text file with an

extension ".extract" in the same folder as our active design file. Sp 1it the
path of the active design file into an array using the Sp 1it function.
Specify the backslash as th e delimiter. When you step through the code,
this is what we have after the string is Sp 1i t:



Variantistring(O to 3)





xS plrt(1)

"Microstation VBA"






The variable FilePath contains the path to the active design file. The
variable xSp lit is an array Sp 1i t from FilePath using the backslash (\) as


I Chapter 7: Worki ng Wi t h Text I

the delimiter. Take the last element in the array (using the UBo und
function) and add a new file extension of ".extract" to it.
Next, Join the array with the backslash (\) as the delimiter into the
variable NewTextFilePath.
Lastly, create an ASCII Text file using the NewTextFilePath variable as
the file name. Inside this new file print the contents of the Fi lePath
Here is what the
fi le looks like
when it is opened
in Notepad.






C:\Microstation VBA\docs\chapter07.dgn

Asc and Chr

Fu nct i on AscC String As String) As In t ege r
Function ChrC Char Cod e As Long )
The characters on our computer keyboards have an ASCII number
associated with them. Upper case "x.' has its own unique number
different from lower case "a". Some characters not on our keyboards
have their own ASCII number. You get the ASCII number of a character
with the As c function. If you know the ASCII number of a character, you
can use the Chr function to get the character.
Su b TextWork19 ()
Dim strCharacter As String
For I = 0 To 255
Debug.Print I & vbTab & Chr(I)
Next I
End Sub

I VBA String Functions I


This code takes each ASCII character from 0

to 255 and prints it to the Debug
(Immediate) window. Now, why would we
want to do this? Because some of the ASCII
characters that are not on your keyboard can
come in handy. For example, character
number 169 is the copyright symbol.
Character number 174 is the registered
symbol. Character 176 is the degrees symbol.
Character 177 is the plus/minus symbol used
for geometric tolerances.



173 174
175 176


Now that you know that character 169 is the copyright symbol, you can
use it in a message box:
Sub TextWork20 ()
Dim s tr Copyright Not i ce As Str in g
strCopyrightNot i ce = "Learning MicroStation VBA " &
Chr(169) & "2005 "
MsgBox strCopyr i ghtNot i ce
End Sub

Learning MicroStation VBA 200S

The Asc function does the opposite of the Chr

function . You provide a character and get
back the ASCII number - something you
might do when creating graphical user
interfaces (GUIs).

Private Sub TextBoxl_KeyPress(ByVal KeyAscii As

Select Case KeyAscii
Case Asc( "O" ) To Asc( "9" )
Case Else
KeyAsci i = 0
End Select
End Sub

The preceding code is the KeyPress Event of a TextBox. The KeyPress

Event gives the ASCII number of the character the user attempted to
type into the TextBox. In this example, if the KeyAscii property contains
a number 0 through 9, then do nothing. Otherwise give the KeyAscii


I Chapter 7: Working With Text I

property a value of zero (0). The net result is that only numbers 0 to 9
can be entered into the TextBox.

Function FormatCurrency(Expression. _
[NumOigitsAfterOecimal As Long = -lJ. _
[IncludeLeadingOigit As VbTriState = vbUseOefaultJ. _
[UseParensForNegativeNumbers As VbTriState =
vbUseOefaultJ. _
[GroupOigits As VbTriState = vbUseOefaultJ) As String
Use FormatCurrency to take a number or string then display it as
currency. In some countries, such as the U.S., this places a dollar symbol
in front of it. Other parameters include the grouping numbers with
commas, etc.

Sub TextWork21 ()
Dim MySalary As Double
Dim MySala ry2 As Do uble
MySalary = 123456.78
MySalary2 = 0 .1 234
MsgBox FormatCurrency(MySalary, 2, vbFalse , vbFalse , vbTrue)
MsgBox FormatCurrency(MySalary. O. vbFalse. vbFalse, vbTrue)
MsgBox FormatCurrency(MySalary2. 2, vbFalse, vbFa ls e, vb True)
MsgBox FormatCurrency ( MySalary2, 2 , vbTrue, vbFalse, vbTrue )
End Sub




$0.1 2

OK .. ~

Function Forma t Number(Expression , _
[N umOigitsAfterOecimal As Long = -lJ, _
[ I nclude Lead i ngOigit As VbTriState = vbUseOefaultJ , _
[ UseParensForNegat i veNumbers As Vb TriState =
vbUseOefaultJ , _
[GroupOigits As VbTr i State = vbUseOefaultJ) As String

I VBA String Functions I


FormatN umber looks the same as FormatCu r re ncy. The main difference is
that Fo rma tCu rre ncy places a currency character in front of the number,
whereas FormatNumber returns only a formatted number.
Sub TextWork22 ()
Dim MySalary As Double
Diw MySalary2 As Double
MySalary = 123456.78
MySalary2 = 0.1234
MsgBox FormatNumber(MySalary. 2. vbFalse. vbFalse. vbTrue)
MsgBox FormatNumber(MySa l ary . O. vbFalse . vbFalse . vbTrue)
MsgBox FormatNumber(MySalary2 . 2 . vbFalse . vbFalse . vbTrue)
MsgBox FormatNumber(MySalary2 . 2 . vbTrue . vb False . vbTrue)
End Sub

... m






Function FormatOateTime(Expression,
[NamedFormat As VbOate Ti meF orma t = vbGeneralOate])
As String
Use Fo rmatDate Time to specify a date/time and how format it. Here are
your options and the results:
Sub TextWork23 ()
Di m DateTo Format As Date
Date ToFor mat = "1 / 1/2005 4 :45 PM "
MsgBox FormatDateT i me(Date ToFormat ,
MsgBox For matDateTime(Date ToFormat ,
MsgBox FormatDateTime(DateToFormat,
MsgBox FormatDateTime(DateToFormat ,
MsgBox FormatDateTime( DateToFormat ,
End Sub

vbLongDa t e)
vb ShortDate)


I Chapter 7: Working Wi th Text I

1/1 /20054:45:00 PM



Saturday, January 01, 2005


4 :45:00 PM

r--' OK -'-







Function Format(Expression, [Format], _
[FirstDayOfWeek As VbDayOfWeek = vbSunday],
[ Fir s tWee kOfYear As Vb Fi rstWeekO f Year = vbF i rstJanl])




of specific

types of formatting:
FormatCurrency, FormatNumber, Format DateTime. These functions work
great for standard formatting situations. However, VBA does not
provide a FormatPhoneNumber function. So, how do we take ten digits
and turn them into a fully formatted phone number?
Sub Text Work 24( )
Dim My Phone As Str in g
My Phon e = "800555121 2"
MsgB ox Format(My Ph one, "(###) ###-#### " )
End Sub

The Format parameter in the Forma t function

allows a great deal of flexibility. See the VBA
Help file for more formatting options.

(800) 555-121 2

Use the Ampersand (&) symbol to concatenate strings. I use the
ampersand extensively in this book to take multiple strings and combine
them into one.

We have a few constants available for use with strings, such as vbCr
constant, which is for a Carriage Return. It is similar to pressing the
<Enter> key on the keyboard. Look at previous examples of the vbCr
constant and the results it generated.

I Review I


Use the vbTab constant to simulate the user pressing the <Tab> key on
the keyboard.

Strings refer to text. Letters, numbers, and other characters combine to
form a single piece of text. This section focused on working with these
strings of characters. You learned to capitalize, make lowercase, get the
left-most or right-most characters, split them, join them back together,
format them, and a number of other things.
Take time to work through all of the examples accompanying each of the
functions. Remember, you can step through the code one line at time by
using the <P8> button.
The next section deals with numbers.


I Chapter 7: Working With Text I

Working With Numbers

For hundreds and even thousands of years, the world's greatest
mathematicians attempted to calculate an accurate value for Pi. When
asked about this value today, we casually state 3.14159 or something
close to it. A2 + B2 = C2. This formula is second nature to children on
our school's playgrounds today, whereas not long ago the equality of the
sum of the squares of the sides of a right triangle to the square of the
hypotenuse was a great unknown.
While software development, in general, deals in large part with logic,
add MicroStation to the mix and the need to manipulate numbers
increases exponentially. After all, what is a line? The shortest distance
between two points. Each of these points is composed of three numeric
values: an X, a Y, and a Z. To draw a circle at the midpoint of that line in
VBA, we need to calculate the line's midpoint. How do we do that? Find
the phone number of a math teacher? Not so, my friend.

VBA makes working with numbers a breeze. It doesn't do all of the work
for us, but we can do a great deal with very little pain.



I Chapter 8: Working With Numbers I

1 + 1 = 2. We learned this many, many years ago. The plus symbol ( +) is
used in VBA to add numbers. Take a look:
Sub TestAdditionSubtraction()
Dim SelPt As Point3d
Dim CenPt As Point3d
Dim CadMsg As CadlnputMessage
Di m TextElem As TextElement
Dim RotMatrix As Matrix3d
Set CadMsg = Application.CadlnputQueue.Getlnput
Do While True
Select Case Cad Msg.lnput Type
Case msdCadlnputTypeDataPoint
SelPt = CadMsg.Point
Exit Do
End Select
CenPt = SelPt
CenPt.X = CenPt.X + 1
Set TextElem = Application.CreateTextElementl(Nothing,
CenPt , RotMatri x)
ActiveModelRe ference.AddElement TextElem
CenPt = SelPt
CenPt.Y = CenPt.Y + 1
Set TextElem = Application.CreateTextElementl(Nothing,
CenPt, RotMatrix)
ActiveModelReference.AddElement TextElem
CenPt = SelPt
CenPt.X = CenPt . X - 1
Set TextElem = Application.CreateTextElementl(Nothing,
CenPt, RotMat ri x)
ActiveModelReference.AddElement TextElem
CenPt = SelPt
CenPt.Y = CenPt.Y - 1
Set TextElem = Application.CreateTextElementl(Nothing,
CenPt, RotMatrix)
ActiveModelReference.AddElement TextElem
End Sub

"1" , _

"2", _

"3 ", _

"4", _

I Numeric Functions I



We let the user select a point in MicroStation. We then use the selected
point as a basis for the insertion of each of the text elements we add to
the model. We add 1 to the X element of the selected point to get the
location for the text "1". We add 1 to the Y element of the selected point
to get the location for the text "2". Points 3 and 4 require us to subtract
from the X and Y respectively.

10 - 3 = 7. Use the minus symbol (-) to subtract values in VBA, as in the
example in the procedure Tes t Ad dit i onSub t ract io n.

2 X 6 = 12. Use the asterisk (*) symbol to multiply in VBA. The previous
reference works when in a math book but in VBA it is written 2 * 6 = 12.
Sub TestMultiplication ()
Dim Di stance l nlnches As Double
Dim Di stancelnMM As Double
Distance l nlnches = CDbl (InputBox( "Enter distance in inches :" ))
Distance l nMM = Distancelnlnches * 25 . 4


I Chapter 8: Working With Numbers I

MsgBox Distancelnlnches & " is equal to " &
DistancelnMM & " Millimeters."
End Sub

Multiplying the entered value by 25.4 converts the entered value from
inches to millimeters.

There are two ways to divide numbers in VBA. No, not long division
and short division. The first method returns a very precise number.
When you want precision (and you usually do), use the forward slash (I
) symbol like this: 5 / 2 = 2.5
Function ToMile s(DistancelnFeet as Double) As Double
ToM il es = Di s t ance l nFeet / 5280
End Funct i on

The function ToN i 1es allows us to supply a distance in feet that returns
the distance in miles. Actually, the return value is in decimal miles.
Another way to divide numbers is using the backslash symbol (\ ). This
returns a whole number instead of a decimal number. 5 \ 2 = 2.

Squares and Exponents

Remember A2 + B2 = C 2? The little twos shown above the A, B, and C
square the values of A, B, and C.
In VBA, we write the expression like this:
A 1\2 + BI\2 = C1\2. The caret symbol allows an exponent. If you were to
cube (raise to the power of 3) a number, you would use D A 3.
Function GetCircleArea (C ir cleRadius As Double) As Double
Dim Pi As Double
Pi = Atn(l) * 4
GetCircleArea = Pi * Circ leRadius A 2
End Function

I Numeric Functions I


Square Root
Use the Sq r function to get the square root of a number. Here's one way
to use it:
SJb GetLineLength( )
Dim SelElem As Element
Dim LineElem As LineElement
Dim SelElems As ElementEnumerator
Set SelElems = ActiveModelReference.GetSelectedElements
Whil e SelE l ems . MoveNext
Set Sel Elem = SelE l ems.Current
Select Case SelE l em .Type
Case msdElement TypeLine
Set LineElem = Se l Ele m
Dim St Pt As Po i nt3d
Dim EnPt As Po i nt3d
StP t = LineE l em . StartPo i nt
EnPt = Lin e El em.En dPo i nt
Li neLeng t h = Sqr((StPt . X - EnPt.X) A 2 + _
(StPt . Y - EnPt.Y) A 2)
MsgBox " Li ne found wi th length of " & Line Length
End Se l ect
We nd
End Sub

Pythagorean's theorem is used in this example. We get the change in X

and the change in Y of the selected line. We square these values, add
them together, then get the square root of the total.

Sine, Cosine, Tangent

The Si n, Co s, and Tan functions require an angle in radians.
Sub TestS i nCos ()
Dim XChange As Double
Dim YChange As Double
Dim Pi As Double
Dim HypLength As Double
Dim HypAngleDegrees As Double
Dim HypAngleRadians As Double
Pi = Atn(l) * 4


I Chapter 8: Working With Numbers I

HypLength = CDbl(InputBox( "Enter Hypotenuse Length: " ))
HypAngleDegrees = CDbl(InputBox( "Enter Angle: " ))
~ypAngleRadians = HypAngleDegrees * Pi / 180
YChange = HypLength * Sin(HypAngleRadians)
XChange = HypLength * Cos(HypAngleRadians)
Deb ug. Pri nt "T estSinCos() "
Debug. Pr int "HypLengt h = " & HypLen gt h
Debu g .P ri nt "HypAngleDegree s = " & HypAngle Degr ees
De bug . Pr i nt "HypAngleRad i ans = " & HypAng l eRadians
Debug.Print "XChange
"& XChang e
Debug . Print "YChange = " & YChange
End Sub

To calculate the change in X and the change in Y, we need hypotenuse

length and an angle, as long as we have access to the Sin and Cosine of
the angle. After getting the user input, convert the supplied angle in
degrees to radians. Then use the angle in radians with the Sin and Cos
functions to give change in X and change in Y
TestSinCos ()
HypLength = 10
HypAngleDegrees = 30
HypAngleRadians = 0.523598775598299
8 . 66025403784439
YChange = 5

3 11;J)

Let's use the Tan function now. The first example supposes you know the
leg of the triangle along the X axis.
Sub Tes tT anl ()
Dim XChange As Double
Dim YChange As Double
Dim Pi As Double
Dim HypAngleDegrees As Dou ble
Dim HypAngleRadians As Double
Pi = Atn(l) * 4
XChange = CDbl(InputBox("Enter X Side Length: " ))
HypAngleDegrees = CDbl ( InputBox("Enter Angle: " ) )
HypAngleRadians = HypAng l eDegrees * Pi / 180
YChange = Tan ( HypAngleRa dians) * XChange
Debug. Pri nt "Tes tTanl( ) "

I Numeric Functions I
End Sub


"XC hange = " & XChange

"HypAngleDegrees = " & HypAngleDegrees
"HypAngleRadians = " & HypAngleRadiars
"YChange = " & YChange

TestTan1 ()
XChange = 4
HypAngleDegLees = 36.8699
HypAngleRadians = 0.643501149881057
YChange = 3.00000025679859



Sub TestTan2( )
Dim XChange As Double
Dim YChange As Double
Dim Pi As Double
Dim HypAngleDegrees As Double
Di m HypAngleRadians As Doub l e
Pi = Atn(l) * 4
YChange = CDbl(InputBox( "Enter Y Side Length :" ))
HypAngleDegrees = CDbl(InputBox( "Enter Angle: " ))
HypAngleRadians = HypAng l eDegrees * Pi / 180
XChange = YChange / Tan(HypAngleRadians )
Debug.Print "TestTan2() "
Debug.Print "YChange = " & YChange
Debug.Print "H ypAngl eDegrees = " & HypAngleDegrees
Debug .Print "HypAngleRadian s = " & HypAngleRadians
Debug.Print "XChange = " & XChange
End Sub

TestTan2 ()
YChange = 3
HypAngleDegLees = 36.8699
HypAngleRadians = 0.643501149881057
XChange = 3.99999965760191

A ,



I Chapter 8: Working With Numbers I

The values used

for the previous
exampl es make
use of th e right

As we write code, it is common to make little mistakes along the way.
The world calls these "bugs" but we could call them "creative
programming:' The net result is the same: the code doesn't work. It is
helpful to test our calculations with numbers that give us predictable

Arc Tangent
Sin, Cos, and Tan help when we know the angle involved. If we do not
know the angle, we can get the angle by using Atn (ArcTangent).
Sub TestATan ()
Di m Pi As Do ubl e
Di m Angle Degrees As Double
Di m AngleRadians As Double
Pi = Atn (1) * 4
An gle Radi an s = Atn (3 / 4)
AngleDegrees = AngleRad i ans / Pi * 180
MsgBox AngleDegrees
End Sub


Absolute Value
The Abs function gives us the Absolute Value of the supplied number.
Sub TestAbs ()
Debug.Print "The absolute value of 4 is " & Abs(4)
Debug.Print "The absolute value of -5 is " & Abs(-5)
End Sub

I Numeric Functions I


The absolute value of 4 is 4

The absolute value of -5 is 5

C .i

Convert to Integer, to Long, to Double, and Value

We have discussed declaring types of variables. VBA gives us the ability
to convert values from one type to another.
One of the most common conversions is from a string to a number.

CDb l ( I nputBox( "E nter Y Si de Length :" ))

Above we use the COb1 function to convert the results of the InputBox to
a double.
Sub TestClnt ()
Deb ug . Pri nt
Debug.Pr i nt
Debug.Pr i nt
Deb ug . Pr in t
End Sub

Cl nt(4 .5 6)
Clnt(4 . 23)
Clnt( - 4 . 56)
Cl nt (- 4 . 23)

When converting from a double to an integer,

something needs to be done with the decimal
portion of the number because an integer is a
whole number. It is important that you
understand how this works. CI nt arrives at an
integer by rounding the number. Take a look
at the code in "TestCInt" and the results
shown in the Immediate window.

The CLn g function works just like the CI nt function, except it converts
the provided number to a long. You could ask, "If CLng does the same
thing as CI nt , which one should I use?" That is a good question.
Remember, that a long number can be significantly larger than an
integer. To use Cl nt on a number such as 40,000.123 would create an
overflow error. CI nt and CLng are often used when assigning a value to a


I Chapter 8: Working With Numbers I

variable. So, if you assign a value to a variable declared as an integer, you
should use CI nt. If you are assigning a value to a variable declared as a
long, use CLng.
Sub TestCLng ( )
Debug . Pr int
Debug.Pr i nt
Debug . Print
End Sub


40 000
-4 0 001


The Fix function looks like it works the same as the CI nt or the CLng
function. It returns a number without the decimal portion of the
number. However, it works a little differently. Let's look at the results of
the code below.
Sub TestFi x ()
Debug . Print
Debu g .Pr int
Debug . Print
End Sub

Fix (4OOOO.56)
Fix(4OOOO . 23)
Fix( -40000.56)

The Fi x function simply drops the decimal

portion of the provided number. It does not
do any rounding. Fix can return numbers
that fall within the integer and long range.

COb 1 converts the supplied parameter to a double.
Sub TestDoubl e ()
Di m LineLength As Double
LineLength = CDbl( InputBox( "Enter the li ne length :" ))
End Sub

I Numeric Functions I


CI nt, CLng, and COb 1 work well if the supplied parameter is numeric,
providing the number 3.14159 works with any of these functions.
However, if you pass the parameter as 2.5", an error pops up. The Va 1
function has the ability to give us the numeric value of a supplied
parameter. The best way to understand how it works is to run some code
and look at the results.
Sub Test Val ()
Debug.P r int
Debug.P r in t
Debug . Print
Debu g.Pr in t
Debug . Print
Debug. Pr int
End Sub

Va l ( "4 . 5""" )
Va 1 ( "4.5 i nches " )
Val( "$5 , OOO " )
Val( "45 degrees " )
Val( "Approx . 528 0 feet " )
Val ( "23 feet 12 inches " )

Notice that when the parameter supplied to

the Va1 fun ction begins with a numeric
value, Va1 returns all of the numeric
characters until it finds a non-numeric
character and returns the numeric values it

Many of the functions we have just reviewed return numeric values.
I sNumer i c returns a Boolean value (True or False). It looks at the
parameter and determines if it is numeric.
Sub TestIsNumeric ()
Debug.Print I sNumer i c( "4 . 5""" )
Debug . Print I sNumer i c( "4 . 5 i nches " )
Debug . Print IsNumeric( "$5,OOO " )
Debug . Print IsNumer i c( "45 degrees " )
Debug.Pr i nt IsNumeric( "Approx. 5280 feet " )
Debug . Print IsNumeric( "23 feet 12 inches " )
End Sub


I Chapter 8: Working Wi th Numbers I

Is Numeri c looks at the entire parameter
and determines if it is numeric. If any
portion of the parameter is not numeric,
we get a false value returned. Notice how
the dollar sign ($) is a numeric sign.

Fa l se


CI nt and CL ng round decimal numbers to whole numbers. The Round
function lets us specify how many numbers we want to appear after the
decimal point. Take a look:
Sub Tes tRound ( )
Debug.Print Round(3.14159 . 4 )
Debug.Print Round(3.14159 . 3 )
Debug.Print Round(3.14159. 2 )
Debug.Print Round(3 . 14159. 1 )
Debug . Print Round(3.14159 . 0)
Debug . Print Round(1.455. 2 )
Debug.Print Round (1. 455 . 1 )
Debug. Print Round(1.4 . 0)
Debug . Print Round(1.5 . 0)
End Sub

3 . 142
3 . 14
3 .1
1. 46
1. 5

Mod - Find the Remainder

The Mod function gives the remainder value of two numbers, but you use
it quite differently than most other functions. Where most functions call
the function then provide parameters separated by commas,. in the Mod
function you supply the numerator, call Mod , then supply the
Sub TestModl ()
De bug . Pr int
Debug.Pr i nt
Debug . Print
Debug . Print
End Sub

5 Mod 2
7 Mod 3
23 Mod 7
280 Mod 2

. >.

I Numeric Functions I


Sgn - Show me a sign

Is a number positive or negative? Or is it neither? The Sgn fun ction
returns a value of -1, 0, or 1 depending on whether the supplied value is
negative, zero, or positive.
Sub Tes tSgn ()
Debug . Print Sgn(-4.5)
Debug.Print Sgn(O)
Debug.Print Sgn(4.5)
End Sub


Rnd and Randomize

Once in a while you need to generate a random number. This example
shows how to create a random number between a lower and higher
number. The result is a random point cloud consisting of 300 points
between (25,25 ) and (50, 50).
Sub TestRnd ()
Dim I As Long
Dim Lower As Long
Dim Higher As Long
Di m PointCen(O To 1) As Po i nt3d
Dim PointElem As PointStringElement
Lower = 25
Higher = 50
For I = 1 To 300
PointCen(O) . X Round((Higher
Lower -'- 1 ) * Rnd(l) , 2 )
PointCen(O) . Y Round((Higher - Lower + 1) * Rnd ( 1 ) , 2 )
Poi ntCen (1) . X PointCen(O).X
PointCen(l) . Y PointCen(O) . Y
Set PointElem
Application . CreatePointStri gElementl(Nothing , _
PointCen, True)
ActiveModelReference . AddE le ment PointElem
End Sub


I Cha pter 8: Working With Numbers I

Order of Operations
2 + 5 * 8 / 12 + 13


(2 + 5) * 8 / (1 2 + 13)


2 + (5 * 8 / (1 2 + 13))


Each of these expressions returns a d ifferent result. The numbers are the
same and the operations are the same but th e results are different.
The order in which numeric operations are carried out is important to
understand. Multiplication and division come first, addition and
subtraction come second. If there is any question, place parenthesis
around the operations you want grouped to make it clear how VBA
sh ould calculate your expressions.

Many software developers can work for extended periods of time
without using mathematical functions . When we are programming
MicroStation, however, we are always using numeric functions. We can
add, subtract, multiply, and divide. We can use other functions that aid
in the location of elements in MicroStation or compute lengths,
angles, etc.

Standard VBA Calls

While introducing various concepts, we used a number of standard
VBA calls without discussing them, so let's cover them now. Again, you
can use these VBA calls with other VBA-enabled applications such as
Microsoft Excel.

We used MessageBoxes to display some text with an OK button . By
default, the code pauses until the user clicks the OK button.
Sub TestMessageBoxl ()
MsgBox "Your hard drive wil l now be formatted. "
End Sub

Your hard drive will now be formatted.

This is just what we all want to see: A

MessageBox informing us something
drastic is about to happen and all we have is
an OK button to click on.
You can specify the prompt of the
MessageBox (the text that shows up) as well
as which buttons display.



I Chapter 9: Standard VBA Calls I

Sub TestMessageBox2 ( )
Di m MsgResp As VbMsgB ox Result
MsgBox("Unab l e to open file.", vbAbortRetryIg~ore)
MsgBox( "F ormat Hard Dri ve?", vbOKCancel)
MsgBox( "New Level Added .", vbOKOnly)
MsgBox("Not Connected to Internet . ", vbRetryCancel)
MsgBox( "Do you want to continue? " , vbYesNo)
MsgBox( "Continue Reading File? " , vbYesNoCancel)
Select Case MsgResp
Case VbMsgBoxResult . vbAbort
' Place Code He re
Case VbMsgBoxResu l t . vbCancel
' Place Code Here
Case VbMs gBoxRe sult . vbIgnore
' Place Code Here
Case VbMsgBoxResult.vbNo
' Place Code Here
Case VbMsgBoxResult . vbOK
' Place Code Here
Case VbMsgBoxResult . vbRetry
' Pl ace Code Here
Case VbMsgBoxResu l t . vbYes
' Pl ace Code Here
End Select
End Sub

Unable to open file.

r '-Ab~it" ll [


Not Connected to Internet.



Do you want to continue?

Continue Reading File?

The ability to have more than an OK button makes the MessageBox

much more powerful. Now, however, you are asking a question of the
user. Yes? No? Retry? Abort? Ignore? Cancel? OK? When you ask a
question, you need an answer. So, we use the MessageBox as a function
and get its return value.

I MessageBoxes I

11 9

In the example above, which is only for illustration purposes, each

MessageBox returns a value into the variable Msg Resp that tell us which
button the user clicked. We use a Select Case statement to determine the
button pressed, then execute code based on the button. The Select Case
Statement is placed after the "YesNoCancel" MessageBox so previous
button clicks are not considered, only the "YesNoCancel" MessageBox.

If you change a file name extension, the file may become unusable.
Are you sure you want to change it?

Here is a Microsoft Windows MessageBox that appears when you

attempt to change a file's extension (say from .txt to .dgn). Note the Yes
and No buttons. We now know how to specify buttons but what about
the exclamation point in the triangle? How do we do that?
When you display a MessageBox, use constants that specify which
buttons to display, such as vbOKCance l, vbYesNo, and vbOKOnly.
These constants have numeric values. There are other constants that
specify which icon to display, such as vb Exciamation. When you add the
constant specifying the buttons to display with the constant for the icon
to display, VBA displays the buttons and the icon in the Mess ageBox.
Sub TestMes s ageBox3()
Dim Msg Resp As Vb MsgBoxResult
MsgResp = MsgBox("Unable to open file.", _
vbAbortRetryIgnore + vbCritical)
MsgResp = MsgBox("Format Hard Drive? " , _
vbOKCancel + vbExclamation)
MsgResp = MsgBox( "New Leve l Added .", vbOKOnly + vblnformation)
MsgResp = MsgBox( "Do you want to continue? " , vbYesNo + _
End Sub


I Chapter 9: Sta nda rd VBA Ca lls I

Unable to open file.

Format Hard Drive?

. ,.


New Level Added.






Do you want to continue?

r. y~~ ] I


When using these icon constants with button constants, Windows

displays the buttons and the icon and plays specific sounds,
Let's look at another available parameter when using the MessageBox.
Sub TestMessageBox4 ()
MsgB ox "Te st ing Title", vb Cri t ical, "Ti t le Goes He r e "
MsgBox "Test i ng Ti tle ", , "Ti t l e Goes Here "

End Sub


'''- ,

;rjtle ~9~~H~r~:.: ~

Testing Title

Testing Title

r OK]

The Title parameter displays at the top of the MessageBox. It is the third
parameter, The MessageBox only has one required parameter, the
prompt. So, to display a prompt and a title and the default button, place
a comma after the prompt, a space, another comma, and then the
prompt. When you bypass an optional parameter, leave the parameter
blank and use commas to indicate that you are providing the next
parameter( s).

InputBoxes let users enter text. If a user clicks the Cancel button or
enters nothing and clicks the OK button, the InputBox returns an empty
string, An empty string is denoted in VBA as two quotation symbols
with no other character between them ("") ,

IlnputBox I


Sub TestlnputBoxl ()
Dim InpRet As String
I np Re t = Inpu tB ox( "Enter Level Name :" )
Debug.Print "User entered" & InpRet
End Sub

Enter Level Name:

[ Cancel

The InputBox has additional parameters we can use. We will discuss four
of them here.
Sub Te s tlnputBo x2()
Dim InpRet As String
InpRet = InputBox( "Enter Level Name :" , _
"Level Creator " , "Str i ping " , D, 0)
Debug.Print "User entered" & InpRet
End Sub

Enter Level Name:



Cancel )



Looking at the code and the result reveals most of the new parameters.
After the prompt and title, a default value for the InputBox is provided,
then the X, Y location where the InputBox is displayed. The X and Y
values are in pixels and are system-dependent. This means if you use 0, 0
as your coordinates, the InputBox displays in the upper-left corner of
the monitor independent of where the MicroStation window is placed.
Be careful with the X and Y location parameters because it is possible to
place the InputBox entirely off screen. It would surely confuse the user if
he could not see the InputBox and the code is waiting for a click on a
button or the <Enter> key.


I Chapter 9: Standard VBA Cal ls I


512812005 11 :29:27 A~l

The Now function gives the current system date and time. This is useful
to make a date/time stamp. Now returns a Date type value.
Sub TestNow ()
MsgBox Now
End Sub

Now tells us the current date/time. DateAdd allows us to look into the

future or into the past. Here are a few examples of how to use DateAdd:
Sub Test DateAdd ()
Di m NowDate As Date
NowDate = Now
Debug.Print NowDate
Debug.Print NowDate
Debug . Print NowDate
Debug . Print NowDate
Debug.Pr i nt NowDate
Debug.Print NowDate
Debug .P rint NowDate
Debug . Print NowDate
End Sub


& vbTab & DateAdd( "d" , 4, NowDate) ' Day

& vbTab & DateAdd( "h" , 4, NowDate) ' Hour
& vbTab & DateAdd( "n" , 4 , NowDate) ' Minute
& vbTab & DateAdd( "s " , 4, NowDate) ' Second
& vb Tab & DateAdd( "m" , 4 , NowDate) ' Month
& vb Tab & DateAdd( "w" , 4 , NowDate) ' Week
& vbTab & DateAdd( " yyyy ", 4, NowDate) ' Year
& vb Tab & DateAdd( "q" , I, NowDate) ' Quarter

11: 40:54
11: 40:54


6/1/2005 11:40:54 AM
5/28/2005 3:40:54 PN
5/28/2005 11:44:54 AN
5/28/2005 11: 40:58 AM
9/28/2005 11: 40:54 ,A.Jv!
6/1/2005 11:40:54 AJI'!
5/28/2009 11:40:54AJ1'!
8/28/2005 11:40:54 AM

In the above example, we declare a variable as a Date then set its value to
Now. We could use the function Now in each DateAdd function. Because Now
changes from second to second, it is a good idea to set a variable to Now
and then use that variable throughout a procedure to make sure you are
basing all of your calculations on the same date/time. Use a positive

IlnputBox I


number as the second argument to move the result into the future. Use a
negative number to return a value in the past.

If you have two dates and want to know the time interval between them,
use Date Di ff . Use the same interval parameters with DateAdd and
DateDi ff.

Sub TestDateDiff ()
Dim NowDate As Da te
NowDate = Now
Debug.Print "Days " & vbTab & DateD i ff( "d" . NowDa t e . "1/ 1/3000 " )
Debug.Pr i nt "Hours " & vbTab & DateD i ff( "h". NowD ate . "1 /1/3000 " )
Debug.Print "Minutes" & vbTab & DateDiff( "n" . NowDate. "1/ 1/ 3000")
Debug . Print "Seconds " & vbTab & DateDiff( "s ". NowDate . "1/ 1/ 3000 " )
Debug.Print "Months " & vbTab & DateDiff( "m" . NowDate . "1 /1/30 00 " )
Debug . Print "Weeks" & vbTab & DateDiff( "w" . NowDate. "1/1/3000 " )
Debug . Print "Years " & vbTab & Date Di f f( "yyyy " . NowDate. "1/ 1/ 3000 " )
Debug.Print "Quarters " & vbTab & Date Diff("q". NowDate. "1/1/3000 " )
End Sub

The Y2K scare was nothing more

than a scare for most of us. This
example looks forward to Y3K.
How many days, hours, minutes,
seconds, months, weeks, years,
and quarters before the dreaded
January 1st, 3000 comes our way?
Only 31,386,398,632 seconds.

Minutes 5 2 3106644
seconds 31386398632
Quar-te r- s



The interval is the first parameter.

The next two parameters are the dates we are looking at. If the first date
comes before the second date, we get a positive return value. If the first
date comes after the second date, we are returned a negative value.

I Chapter 9: Standa rd VBA Ca lls I


The Time r function tells us how many seconds have transpired since
midnight. This can be useful when testing our applications to find
bottlenecks in the code. If you are working late at night, however, be
careful. At the strike of midnight, the timer function returns a value of 0
(zero) and starts counting seconds all over again.

Sub TestTimer ()
MsgBox Timer
End Sub

FileD ate Time gives the date/time the specified file was last modified.
Sub TestFileDateTime ()
Dim exeDate As Date
exeDate = Fil eDateTime
("C : \Program Files\Bentley\MicroStation\ustation.exe" )
MsgBox "Mi croStat i on Dat e/ Time : " & exeDate
End Sub



""'.~ ";''':''r~''''r!}''

~lcr,C)~~~!iC).n . ' .. ,:' ;~', ,.,,'~'


.'. ~

MicroStation Date/Time: 6/21/20057:49: 14 P~l


Fi 1eLen tells the size (in bytes) of a given file.

Size: 976896



Sub TestFileLen ()
Dim exeSize As Lo ng
exeSize = FileLen
( "C: \Progra m Files \Ben tley \ MicroStation \ustatio n.exe " )
MsgBox "MicroStation Size : " & exeSize
End Sub

rUse MkD i r to create a new directory. All parent directories must exist for
MkDi r to work. For example, to make a directory (also called a folder)

I ln putBox I


named c:\MicroStation VBA \Chapters\09\Samples but the Chapters

directory does not exist, you must create the "Chapters" directory, then
the "09" directory, then the "Samples" directory.
Sub TestMkDi r ()
MkD ir "c : \ MicroSta ti on V8A\So0 rce Code "
End Sub

RmDi r removes a directory from the file system. The directory must be
empty, otherwise an error occurs.
Sub TestRmDi r ( )
RmDir "c:\MicroStation V8A\Source Code "
End Sub

The Di r function allows us to look for files and folders (directories). The
first time you use it, specify a path and file name (wildcards are
acceptable). 0i r only returns one file/folder at a time. If you are looking
for a group of files or folders, call Di r again and leave the parameters
empty. When Di r returns an empty string (""), you know it has returned
all of the file or folder names requested. In addition to specifying a file
or folder path/name to look for, you can specify the type of filelfolder.
Since there is a great deal that you can do with the 0i r function, we will
look at several examples and the results of the code.
Sub TestD i rl ()
Dim RootPath As String
Dim DirReturn As String
RootPath = "C: \Program Fi le s\8entley "
DirReturn = Dir(RootPath & " \ *.* ", vbDirectory )
While DirReturn <> ""
Debug.Print RootPath & "\ " & DirReturn
We nd
End Sub


I Chapter 9: Sta ndard VBA Ca lls I


Files\Bentley\ ..

Our first example retrieves Directories under the "C:\Program

Files\8entley" directory. Notice how the first directory is named ":' and
the second, ".:'. This occurs with all calls when looking for directories
and they should be ignored in your code. They refer to the current
folder and the parent folder.
Sub Tes tDi r 2 ()
Dim Roo tP ath As St ri ng
Dim DirRet ur n As St ring
RootP ath = "C: \ Progra m Fil es\ Bent le y\ Mi c roStat i on"
Di rRet ur n = Di r( RootPat h & " \* . * " )
Whi l e DirR eturn <> ""
Debu g . Print Roo t Path & "\ " & Di r Ret urn
Di rR etu r n Dir
End Sub

C:\P r og ram
C: \P r og r am
C: \Program
C: \P r ogram
C: \ Pr ogram

Fi les\ Be nt l e y \ Mi c r o St at i o n\ a t l 71.dll
Fil es\ Be ntle y \ Mi cro Stat i o n\auto mationdgn . d l l
Fi l es\ Bentl ey\ Mic r oStat i on\bdti d oc .htm
Fi l es\ Bent l ey \ Mi c r oStation\ be nt le y .mi c r ostati i ng.dl l
Fi les\Bent l ey\Mic r ostat i on\ be nt l ey . mi c r ostat i on.textlib . d ll


v .


Now we are looking

folder for all files (*.*).


the "(:\ Program Files\8entley\MicroStation"

Writing file names to the Immediate window works well for

demonstration but is not practical. Let's do another variation this time
putting the file names into a variable.
Sub TestDi r3 ()
Dim RootPath As String
Dim DirReturn As String


IlnputBox I

Dim OgnFiles() As String

ReD i m DgnFiles(O) As String
RootPath = "C: \ Mi croStation VBA\Docs "
DirReturr = Dir(RootPat~ & "\*.dgn")
While DirReturn <> ""
DgnFiles(UBound(DgnFiles)) = RootPath & "\" & DirReturn
ReDim Preserve DgnFiles(UBound(DgnFiles) + 1)
DirReturn = Di r
ReDim Preserve DgnFiles(UBound(DgnFiles) - 1)
End Sub



VBA 'Docs'chapter04 .dgn"

VBA'Docs'chapter07 .dgn"
VB A'Docs'chapter08.dgn"

We look in the directory "C:\MicroStation VBA \Oocs" for files with the
extension .dgn. Place the paths of these files into a dynamic array
variable named DgnFiles. When the code gets to the "End Sub" line of
code, six files have been found and placed into the array. You could write
additional code to work with the files before "End Sub".

WARNING: The Ki 11 function is permanent. Files that are 'Killed' are
not sent to the recycle bin. They are destroyed totally and completely.
Use with extreme caution.
Sub TestKi 11 ()
Kill "C:\MicroStation VBA\Docs\killtest.txt"
End Sub

This code kills a file named C:\MicroStation VBA \Oocs\killtest.txt.

The ability to delete a file is useful and necessary but must be used with


I Chapter 9: Standard VBA Ca lls I

Beep beeps. It offers a quick, audible clue to the user as our code
executes. Although useful to draw the user's attention to the program, it
can become annoying to have an application beep every time a user does
Sub TestBeep()
End Sub

Working with the Windows registry can save settings the user has set in
our software. Microsoft has created a registry path for VBA program
settings that we can easily write to, edit, and delete.
Sub TestSaveSetting ()
SaveSet t i ng "Learning MicroStat i on VBA ", "Chapter 9 " ,
"S aveS etti ng", "I t Works "
End Sub


~dit . ~iew Fil.vorites t:!elp .

My Computer\HKEV _CURRENT _USER\Software\VB and VBI>. Program Settings\Learning ~llcroSta tion VBA\Chapter 9

After this code is run, the necessary registry folders are added and a
registry entry named "SaveSetting" is created with a value of "It Works".

When a setting is in the registry, we can get it by using Get Set tin g.
Sub TestGetSetting ()
Dim RegSetting As Str in g
RegSetting - GetSetting( "Learning


VBA" , "Chapter g", _

"SaveSetti ng")
Debug.Print "The Key SaveSetting value is """ & RegSetting & ""un
End Sub

IlnputBox I


We can save and get settings and we can delete them. As with any other
API call that deals with the removal of files or data, be careful with this
Sub TestDe l eteSettingl ()
DeleteSetting "L earn i ng Mic roStat i on VBA " , _
"Chapter 9", "SaveSetting "
End Su b

When the above code is run, the Key "SaveSetting" is deleted.

Su b TestDeleteSetting2 ()
De let eSe tt i ng "L ear ni ng Micr oS tati on VBA ", "Chapte r 9 "
End Sub

Te s t Oe1ete Sett i ng2 deletes the Registry Section "Chapter 9".

Sub Te s tDeleteSetting3 ()
Delet eSet ting "L ear ning Mic roS t ati on VBA "
End Sub

Test Oe1eteSetting3 deletes the entire "Learning MicroStation VBA"

Application Name from the Registry and all of its sub-entries.

GetA 11 Se tt i ng s , as the name implies, gets all keys under the specified
app name and section and places them into a multi -dimensional array.
Sub TestGetSetti ngs ()
Di m AllSetti ngs As Variant
AllSett i ngs = GetAllSett in gs( "L earn in g Mi croStation VBA " , _
"Chapter 9 " )
End Sub


I Chapte r 9: Sta nda rd VBA Ca lls I

"Save Setting"




Adding a watch to the AllSettings variable allows us to see the structure

and values of the results of the GetAllSettings call.



Give me a text file and I will move the world. Or is it supposed to be a

"Lever"? The ability to read and write ASCII text files gives us powerful
leverage in our programming efforts. Many programs and databases can
read and write these files. So, what are ASCII files?
An ASCII text file is composed entirely of ASCII characters. It can be
opened in Notepad or Wordpad and is readable by humans.
Sub TestWr it eASCIIA ()
Open "C: \ output.txt " For Output As #1
Pr i nt In , " Firs t 1 i ne . "
Print #1, "Second l i ne ."
Cl ose #1
End Sub

Here is our file in Notepad. Let's examine

the code now.
First, identify a file with which to work. The above example works with
the file C:\output.txt. You can use any file extension but be careful. If you
write a new ASCII file and supply a file extension of .dgn, Microsoft
Windows will think it is a MicroStation file and attempt to open it with
MicroStation when you double-click on it.
You have two options to use when writing files. Use the "Output"
keyword, which means the file will be created if it does not exist or it will

I Reading and Writing to ASCII Files I


be overwritten if it does exist. Or use "Append" if you want to add to the

end of an existing file or create a new file if one does not already exist.
When you open a file, a number is assigned to it. That number is then
used whenever you read from or write to the file. In the above example,
we are using a file number "#1". The number symbol must be in front of
the number each time it is used.
N ext, write some text to the file. In the above example we use the Pr in t
function. You can also use the Write function (an example appears
below) but it gives slightly different results.
Last, Close the file.
Sub TestWriteASCIIB ()
Open "C: \output.txt " For Output As #1
Write #1 . "Fi rst l ine. "
Write #1. "Second li ne. "
Close #1
End Sub

The Wri te function places quotation marks at the beginning and end of
each line which may be helpful if you need it.
Sub TestWriteASCIIC ()
Open "C:\output.txt " For Append As #1
Print #1. "Another line 1. "
Pr int #1. "Another line 2."
Cl ose #1
End Sub

Use "For Append" when opening a file to

add text to an existing file. The above
screen shot is the result of running TestWr ite ASCI IA and then running
Tes tWr i teASCI IC.

It is important to provide VBA a file number that points to the file in
which you want to work. In previous examples where I used "#1" as a
file number, the code works fine because the examples are simple. If
your programs open multiple files simultaneously, you could become


I Chapter 9: Standard VBA Ca lls I

confused as to which number should be used. This is where FreeF i 1e
comes in handy.
Sub TestWriteASCIID()
Dim FFileA As Long
Dim FFileB As Long
FFileA = FreeFile
Open "C:\outputa.txt" For Append As #FFileA
Print #FFileA. "Another line 1."
Pri nt #FFil eA. "Anot her li ne 2."
FF il eB = Fr ee Fil e
Open "C: \outp utb . txt " Fo r Appen d As # FFile B
Print #FFi l eA. "Ano t her lin e 3. "
Print #FFileB. "An ot her 1 i ne 3 . "
Pr int #FFi leA . "Anot he r line 4. "
Print #FFileB. "Another line 4. "
Cl ose #FFileB
Clo se #FFileA
End Sub

The above example works with two files at the same time. When you use
FreeFi 1e, assign the return value to a variable. In this example, I used
FFileA and FFileB as our variable names.
Be careful if you use FreeFi 1e for multiple variables as we have done
here. If you assign FFileA and FFileB file numbers with FreeFi 1e one
right after another, they will both hold the same value. FreeFi 1e returns
a different number only after a file has been opened. So, use Free Fi 1e,
open the file it was used for, then use it again to open the next file. This
keeps us from getting the same number and accidentally reading from
or writing to the same file when we meant to read/write to two separate
Here is a more advanced application of writing to ASCII Text Files. An
XML document is an ASCII text document with specific formatting.
Our next example creates a Microsoft Excel XML document that
contains all of the EXE files in the 1C:\Windows\System32" folder, as
''len as the date and time the file was last modified. After the XML file is
written, you can open it in Microsoft Excel.

I Reading and Writing to ASCII Files I


Sub TestWriteASCIIE ()
Dim FFileA As Long
Di m exe Fil e As Stri ng
FFileA = FreeFile
Open "c : \exefiles.xml " For Output As #FF i leA
Pr in t #FFil eA. "<?xm l ve rs i on= ""l. O""? >"
Print #FFi l eA. "<?ms o-appl i cati on progi d=""Excel.S heet""?>"""
Print #FFileA. _
"<Workbook xmlns=""urn:schemas-microsoft-" &
"com : off ice : spreads hee t"" >"
Print #FFileA. " <Worksheet ss:Name= "" EXE Files"">"
Print #FFileA. " <Table>"
exeFile = Dir("C:\Windows\System32\*.exe")
While exeFile <> ""
Print HFFileA. "
<Row> "
Print #FFileA. _
<Cell><Oata ss:Type= "" String""> " & exeF i le &
"</Data><ICe l l >"
Print #FFileA. _
<Cell><Oata ss :Type=""String"">" &
FileOateTime( "C:\Windows\System32\ " & exeFile) &
"</Data><ICell >"
Print #FFi l eA. "
exeFile = Dir
We nd
Print #FFileA." </Table> "
Print #FFileA. " </Worksheet> "
Print #FFileA. "</Workbook> "
Close FFileA
End Sub

Excel is particular about the

document, so if you encounter
problems copying this code
from this book, open and run
the code on the included CD.
Here is a portion of the results of
this XML file shown in Excel.

~~l~~", ~! .!!Jew " Insert ~~mat 10015 Qat

I Q l23Q

~ jll,~ ~ j ~ ~


' ''' '


E.~~!iT1!l P ex e

r.R~ ~~~~:f~~!;:

~ crdaemon exe


~ r:1

f,,-; accwiz. exe


313112003 60000 AM
- jI311Qo~- 6 oQ~'~ci .A~v1

;~~~~~; ~.~~.~~ ~~

3131120036:0000 AM
~ ",,~,c'~'i '~ EXE Files /."_~=c"c,.,_,.,."c~.,-,,-c __ ,,.


I Chapter 9: Standard VBA Calls I

Read ing from ASC II Files
It is easy to write to ASCII files. Reading them is just as easy. Let's take it
one line at a time.
Sub Re adA SC I IA()
Dim FFile As Long
Dim TextLine As String
FFile = FreeFile
Open "C: \MicroStation VBA\TextPoints . txt" For Input As #FFile
Whi le EOF(F File) = Fa l se
Line Input #F Fi le , TextL i ne
Debug . Pri nt Text Li ne
Cl ose #FFile
End Sub

1.5,2.5,O,Note 1
34.2,54.12,O,Note 2
43.2,1.43,O,Note 3
22.3,33.4,O,Note 4

The example above left uses the Immediate window to show each line in
the file we read. Above right is the file in Notepad. Use Line I nput and
the file number to read a text file one line at a time. Continue reading
until you reach the End Of File (EOF) . It's time to expand on this
Sub Rea dA SCIIB ()
Di m FF il e As Long
Di m Tex t Li ne As St r ing
Dim TextPoint As Point3d
Dim XSpl it As Var i ant
Di m Text Elem As Te xt Element
Dim Rot Ma t As Matr i x3d
FFile = Free Fi le
Open "C: \MicroStation VBA\TextPoints.txt " For Input As #FFile
While EOF(FFile) = False
Line Input #FFile , TextLine
XSplit = Split(TextLine, " ," )

I Controlling Code Execution I


TextPo i nt.X
TextPoin t .Y
Set TextElem

XSp l it(O)
XSplit(l )
= App l ication.CreateTextElementl(Nothing, _
XSplit(3), TextPoint, RoHlat)
ActiveModelReference .AddElement TextEle~

Close /fF Fi le
End Sub

We expanded "ReadASCIIA:' Now, instead of writing the information

from the text file to the Immediate window, let's create new text
elements at the X, Y, Z location specified in each line of text.

Note 2

Here are our

notes placed
exact ly where the
ASCII file

Note 4

Note 1

Note 3


It is essential that we know how to loop through code multiple times and

execute code based on certain conditions.


I Chapter 9: Sta ndard VBA Ca lls I

For ... Next
When you know how many times to loop through a particular block of
code, use a For ... Next statement. Here's a simple example:
Sub ForNextA()
Dim I As Long
For I = 1 To 10
ActiveDesignFile.AddNewLevel "NewLevel "& I
Next I
End Sub

After this code is run, 10 new levels are created named "NewLevel 1"
through "NewLevel10".
For ... Next requires a variable. This example uses a variable named I
declared as a long. The first time you create a new level, I holds a value of
1 (one). The next time, I holds a value of2 (two). This continues from 1
to 10. I eventually holds a value of 11 (eleven) which, since it is out of the
range specified, exits the For ... Next loop and then VBA continues to
execute the code below the For ... Next loop.
Sub ForNextB ()
Dim I As Long
For I = 1 To 10 Step 2
ActiveDes i gn Fi l e . AddNew Level "New LevelB " & I
Next I
End Sub

I added an optional parameter to our For ... Next statement. It is a Step

parameter. By default, For ... Next loops increase the index parameter
by a value 1 (one) each time it is run. When this code is run, however,
''1'' gets values of 1, 3, 5, 7, 9, then ends with a value of 11 and exits the
loop because we are using "Step 2".
Sub ForNe x tC ()
Dim I As Long
For I = 10 To 1 Step -1
ActiveDesignFile . AddNewLevel "N ewLevelC " & I
Next I
End Sub

I Controlling Code Execution I


I just changed our Step parameter to -1. This means ''1'' gets the
following values: 10, 9, 8, 7, 6, 5,4,3,2, 1 and then has a value of 0 and
exits the loop because 0 is outside the bounds of the loop.
Sub ForNe xtD ( )
Dir.1 X As Doub l e
Dim Y As Doub le
Di m In s Pt As Po int 3d
Di m Ce l lE l em As Cel lEl ement
For X = 0 To 10 Step 0 . 25
For Y = 0 To 10 Step 0 . 25
InsPt.X = X
InsPt.Y = Y
Set CellElem = App l i cat i on.CreateCellE l ement3( "Column ", _
InsPt, True)
ActiveModelReference.AddElement CellElem
Next Y
Next X
End Sub

' O'@C
88' O
. . . .... . . ': C
i ' , . , . , ', 'C:DC

. ..1._. ._. _... .. .. .._ _ . .... ( _........ . .....







. ...


. -



-~ ..


. "

This routine requires the definition of a cell named "Column" prior to

running it. Embed one For ... Next statement inside another. The Step
statement increases the variable by 0. 25 each time it is executed. Note
that I declared our index variables as double so they can hold decimal

While . . . Wend
When using Whi 1e ... Wend we are uncertain how many times we need to
repeat a block of code. The code between the While and Wend


I Chapte r 9: Standa rd VBA Ca ll s I

statements continues to execute as long as the While statement is true .
Here is a portion of a procedure we already looked at in this chapter.
While EOF(FFile) = False
Line Input /fFFi 1e , TextL i ne
XSplit = Split(TextLine , "," )
TextPoint.Y = XSplit(l)
TextPoint.Z = XSplit(Z)
Set TextElem = Application . CreateTextElementl(Nothing, _
XSpl it(3), TextPoint, RotMat)
Te xtE l em.TextStyle . He ig ht = 4
TextElem.TextStyle.Width = 4
ActiveModelReference.AddElement TextE lem

When we open a file to read it, we may find 1 line, 10 lines, 100 lines, or
any other number of lines to read in the file. So we keep looking at the
EOF (End of File) condition of the file. While we have not reached the
End Of File, we execute the code between the While and Wend lines.

Do . . . loop
Do ... Loop is very similar to the While ... Wend statement. However, it is
more versatile. Here is one example:
Sub TestDoWhileA ()
Dim CadMsg As CadInputMessage
Dim InsPt As Point3d
Dim CellElem As CellElement
Do While True
Set CadMsg = CadInputOueue . GetInput
Select Case CadMsg.InputType
Case msdCadInputTypeDataPoint
InsPt = CadMsg.Point
Exit Do
End Select
Set CellElem = Application.CreateCellElement3( "Column ", _
InsPt , Tru e)
ActiveModelReference.AddElement CellElem
End Sub

I Control ling Code Execution I


One of the great things about a Do ... Loop statement is we can use "Exit
Do" to get out of the loop at any time. This example allows the user to
select a point in MicroStation. When that happens, we capture the point
and exit the Do Loop. Then we use the captured point to insert a new
Here is another variation of the above procedure. In this next example,
the code will continue inserting cells until the user hits a key on the
Sub TestDoWhileB ()
Dim CadMsg As CadlnputMessage
Di m InsPt As Point3d
Dim CellElem As Cell Element
Do While True
Set CadMsg = CadlnputOueue . Getlnput
Select Case CadMsg.lnputType
Case msdCadlnputTypeDataPo i nt
In sPt = CadMsg.Point
Set Cel l Elem =
Appl i cat i on . CreateCellElement3( "Column ", _
I nsPt , Tr ue)
ActiveModelReference.AddElement CellElem
Case msdCad Inpu tTypeComman d
Ex it Do
En d Select
End Sub

When using "Do While True", we remain in the loop until we either
"E xit Do" or "E xi t Sub" or "Exit Function". "E xit Do" to get out of the
loop and continue to execute the code in the procedure or function. Use
"Exit Sub" and "Exit Functi on" to exit the procedure or function.
Sub TestDoWhileC ()
Dim TextToPl ace As Str i ng
Dim LineNumber As Long
Dim NotePt As Po in t3d
Dim CadMsg As CadInputMessage
Dim TextElem As TextElement
Dim RotMat As Matrix3d
Do ~!hi 1e True


I Chapter 9: Standard VBA Calls I

Set CadMsg = CadlnputOueue . Getlnput
Sel ec t Case CadMsg. Inp utType
Case msdCadlnputTypeDataPoint
NotePt = CadMsg . Point
Set TextElem

App 1 ication.Create T extElementl(Nothing. _

"0 Not e", Note Pt , Ro t Ma t )

Acti veM od elRefere nc e.AddEleme nt TextEle m
Exit Do
End Select

Loo p
TextToPlace = "The following notes supercede all prior notes."
LineNumber = 1
No t ePt. Y = NotePt.Y - 0 .375
Set TextElem = Applicat i on . Create Text El ementl(Nothing,
Li neNumber & ". " & TextToPl ace,
NotePt , RotMat)
Act i veModelReference.AddElement TextElem
LineNumber = LineNumber + 1
TextToPlace = InputBox("Enter Note :" )
Loop While TextToP l ace <> ""
End Sub

This procedure uses two separate Do Loop statements. Let's focus on the
second one. When using "Do" by itself, the code inside the loop executes
at least once. Then, rather than placing the conditional statement at the
beginning of the "Loop", place the conditional statement at the end of
the Loop:' This example allows the user to select a point. We
automatically enter # Note" where the user selected the point and enter
"I. The following notes supersede all prior notes:' below the # Note"
Now that we added a header and a standard note, we allow the user to
begin entering additional notes. Each additional note is placed 0.375
units below the prior note. When the user presses the OK button
without entering anything in the InputBox, the Loop completes because
TextToPlace is an empty string and the "Loop" condition is no longer

I Control ling Code Execution I


For Each ... Next

Some objects are in collections. For example, each document has a
Levels collection composed of Level objects. Use For Each ... Next
statements to look at each object in a collection.
Sub Tes t ForNe xtA ()
Dim dgnLevel As Level
For Each dgnLevel In ActiveDesignFile.Levels
Debug. Pri nt dgn Leve 1. Name
End Sub

When we use For Each ... Next, we

specify a variable to use for each
object and then the collection to
look in when we begin the For
Each .. . Next statement.

NewLevelC 3
NewLe vel C 2
NewLevelC 1

If ... Then
Use If ... Then statements to execute a particular block of code only if a
specific condition evaluates to a value of true.
Sub TestlfThenA ()
Di m LevelName As String
LevelName = InputBox( "En t er Level Name (3 lette rs on l y) " )
If Len(LevelName) = 3 Then
ActiveDesignFile . AddNewLevel LevelName
End If
End Sub

If the user enters something with three characters, add the new level. A
very simple implementation of an If ... Then statement.
Sub TestlfThenB ()
Di m LevelName As Str i ng
LevelName = InputBox( "Enter Level Name (3 letters on l y) " )
If Len(LevelName) = 3 Then
ActiveDesignFile . AddNewLevel Leve l Name


I Chapter 9: Sta ndard VBA Ca lls I

MsgB ox Le velName & " ha s" & Len( Le vel Name) & _
" characters ."
End ! f
End Sub

In this example, look at the number of characters and create the new
level if it is three characters in length. Also add an "Else" statement to
handle situations when the length is not equal to three. Display a
MessageBox showing the number of characters entered wh en the entry
has anything other than three characters in it.
Sub TestlfThenC ()
Di m Level Name As String
LevelName = InputBox( "Enter Level Name (3 letters only) " )
If Len(LevelName) = 3 Then
ActiveDesignF i le . AddNewLevel LevelName
ElseIf Len(Leve l Name) > 3 Then
Act i veDesign Fi le . AddNew Leve l Left(LevelName . 3)
MsgBox Leve l Name & " has " & Le n( LevelName) &
" characters ."
End If
End Sub

TestI fT he nC introduces an El seIf statement. You can use El se l f

statements inside If ... Th en statements and provide a secondary If
statement. You can use multiple E1s elf statements before an E1s e

Select Case
Imagine asking a user to enter a level name then looking at the first
character of the level name. You could use an If ... Then statement with
multiple E1s el f statements or a Se 1ect Ca s e statement.

Se 1ect Ca se lets us provide the condition and then multiple possible

matches for the condition.
Sub TestSel ectCas eA()
Di m LevelName As String
LevelName = InputBox( "Enter Level Name :" )
Select Case UCase(Left(LevelName. 1))
Case "A"

I Contro lling Code Execution I


Act i ve Des i gnFil e. AddNewL eve l "A_ " & LevelName

Case "B"
ActiveDesignF'le.AddNewLeve' "B B " & ~evelName
Case "( ", "0", "E"

ActiveDesignFile . AddNewLeve' "CDE_" & ~evelName

Case Else
MsgBox "Not a valid level name."
End Select
End Sub

In this example, look at the fi rst character of the level name entered.
There are multiple possible blocks of code we may want to execute based
on the first character. If the first character is not A, B, C, D, or E, display
a MessageBox and do not add a new level. If the first character does
meet our criteria, prep end characters to the entered level name as you
add the level name.

Error Handling
In a perfect world with perfect developers, errors would never occur.
We, however, are not perfect, so errors do pop up once in a while. VBA
gives us some tools to deal with errors.
Sub TestErrorHndA ( )
On Error GoTo errhnd
Di m Li ne Le ngth As Doub l e
LineLen gth = CObl(In put Box( "Enter Lin e Le ngt h: " ))
Exit Sub
errhnd :
Select Case Err . Number
Case 13 ' Type Mi smatch
MsgBox "L i ne Lengths must be numeric ."
End Select
End Sub

In TestErrorHndA, ask the user for a line length. As you write code
assume the user knows to enter a numeric value but if the user enters


I Chapter 9: Standard VBA Calls I

something like "10 meters", you run into problems. If you don't handle
the error, the user sees this:

Run-time error '13';

Type mismatch



As you become more experienced in programming, you are better able

to anticipate potential data entry problems and other issues that cause
So, if you do not handle the Type Mismatch
error when asking the user to enter a length, he
sees an unhandled error MessageBox. If the
users click the Debug button, he is taken to the
code that shows the line where the error
occurred. However, if you handle the error as
shown in the above macro, the user sees this:

Line Length s must be numeric.


When handling errors, you can display MessageBoxes to let the user
know an error occurred, or you can handle the specific error so the user
does not know anything happened.
Now, let's take another look at the code in detail. The first thing to do in
the procedure is state:
On Error GoTo errhnd

This tells VBA that if an error is encountered jump to the area of code
labeled "errhnd". Here it is:
errhnd :
Select Case Err . Number
Case 13 ' Type Mi smatch
MsgBox "Line Lengths must be numeric ."
Err . Clear
End Select

I Controlling Code Execution I


Each error has a number associated with it. Use a Select Case statement
to handle different types of errors differently. In this example, only look
at error number 13. If any other error occurs, it is not handled by our
Se 1ect Case statement and the procedure finishes with End Sub.
So, how do we know what error numbers we need to deal with? This is
an excellent question. Let's go back to TestSe l ectCaseA covered a few
pages ago. Run that macro and enter "aaa". Everything should run fine.
Run it again and enter "aaa': What happens?

Run-time error '-2147221504 (80040000)':

Level name is duplicat e



: )1 [


If you enter the same level name twice, the code attempts to create a
duplicate level. We see this MessageBox which gives us some good
information. First, it tells us the error number. -2147221504 and a
description that "Level name is duplicate". That is good to know because
we can add that number in the error handling portion of our code. We
can also hit the Debug button to go to the line of code in question to see
exactly where the error occurs.
Sub Te s tEr r orHndB ()
On Error GoTo errhnd
Dim LineLength As Dou ble
LineLength = CDbl (InputBox( " Enter Line Length :" ))
Exit Sub
Select Case Err.Number
Case 13 ' Type Mi smatch
MsgBox "Line Lengths must be numeric. "
Err.C l ear
Resume Next
End Select
End Sub


I Chapter 9: Sta ndard VBA Ca lls I

TestE r rorHndB is identical to TestErrorHnd Aexcept for one line. Adding a
Resume Nex t statement in TestErrorHndB executes our procedure to
continue the line after the error occurred.
Sub TestError HndC()
On Error Go To errhnd
Dim LineLength As Double
LineLength = CDbl (InputBox( "Enter Line Length: " ))
Exit Sub
errhnd :
Select Case Er r.Number
Ca s e 13 ' Type Mismatch
MsgBox "Line Lengths must be numeric . "
Er r . Clear
Resum e
End Select
End Sub

Here is another slight modification that uses a Re sume statement instead

of Res ume Next. Resume asks VBA to again try the line of code where the
error occurred, whereas Resum e Next ignores the line of code where the
error occurred and moves to the next line.
Su b TestError Hn dO ()
On Error Resume Next
Di m Li neLength As Doub l e
Li ne Length = CDb l (InputBox( " Enter Li ne Length :" ))
End Sub

Instead of attempting to trap errors as they occur, you can tell VBA to
ignore errors altogether and move to the next line using "On Error
Resume Next".
Although "On Error Resume Next" appears to be somewhat sloppy (and
it can be), it can be useful. Consider this next procedure:
Sub TestErrHndE C)
On Error Resume Next
Dim MyExcel As Object
Set MyExcel = GetObject( . "Excel.Application " )
If Err . N mber <> 0 Then

I Controlling Code Execution I

Err . Clear
Set MyExcel


CreateObject ( nExcel.Application " )

End If
On Error GoTo errhnd
MyExcel.Visible = True
"1sgBox MyExce 1. Act i veSheet . Narr.e
Exit Sub
errhnd :
MsgBox "E rror " & Err.Number & " has occurred. " & voCr &
Err.Descr i pti on . vbCritical . "E rror In TestErrHndE "
Err . Clear
End Sub

In this example, we use On Error Resume

anticipating the potential for a specific error.
Set MyExce l

Next because we are

Ge t Object(. " Excel . App li ca ti on " )

GetOb j eet assumes the object we are getting has already been created by

starting Excel (in this example). If Excel is not started, however, we

normally get an error when using GetObj ee t. Use the Err object to get
error numbers and descriptions. After calling GetOb j eet, check the
Err.Number value. If it is non-zero, an error occurred. Use Err.Clear to
clear the previous error from memory.
Then we move to the next method of working with Excel, using
CreateObjeet which launches the Excel application. On Error GoTo
errhnd tells VBA to move to the "errhnd" area if an error is encountered.
Set the Visible property of the Excel application to true. This can cause
an error if Excel did not start, most often because it isn't installed. If
Excel is running and visible, display the name of the active sheet in a
MessageBox. This can throw an error because even though Excel is
running, an Excel workbook (.xls file) file is not open.
Now to the errhnd section of our code. A review of the above suggests
that a number of things could cause errors. Showing the error number
and description in a MessageBox lets us know which error has been


I Chapter 9: Standard VBA Cal ls I

Sub Tes tErrorHn d F()
On Error GoTo errhnd
Dim LineLength As Double
Or Error GoTo 0
LineLength = CDbl (InputBox( "E nter Line Length: " ))
Ex it Sub
errhnd :
MsgBox "Error" & Err.Number & " has occurred ." & vbCr &
Err.Description, vbCritical, "Error In TestErrHndE"
End Sub

"On Error Goto 0" (that's a zero after Goto) tells VBA to ignore the
previous "On Error" statements and continue as if there is no error
handling. This comes in handy because you will see an error dialog box
showing the error number and description plus a Debug button.
Clicking Debug takes you to the line of code that has the problem. Once
you find and fix the bug, you can comment out the "On Er ror Goto 0"
line so your Error Handling code is at work again.
We covered a number,
comprehensive list, of
useful and commonly
used VBA calls. You can
use the Object Browser
in VBA to display all
available to us. We will
Browser later in this
book. Here is a snapshot
of what you will see if
you filter on the VBA
Reference, the DateTime
Class, and the DateD i ff

Function DateDiff(lnleNal As String, Date1, Date2,

[FirstDayOtweek AsVllDavOfWeek = vbSunday],
[FirstWeekOfYear AsVIlFirS1WeekOfYe,n = vbFi rstJan1])
Member of VBA.OateTime

I Review I


After selecting an item in the Object browser, you can get additional
information and, at times, sample code, by pressing the <Fl> key.

DateDiff Function
See Also


:':, ..

Returns a Variant ( Long ) specifying the number of time interva ls between two specified
DateDiff(interval, date1, date2[, firstdayof,,'eek[. firstweekofl1ear]J)

The DateDiff function syntax has these named arguments :




Required. String expression that is the inter'val of time you

use t o calculate th e difference betwee n date1 and date2 .

date 1 , date2

Required ; Variant (Date). Two dates you want to use in the

cal cu lation.

firstda vofwe ek

Optio nal. A constant that specifies the first day of the week. If
not specified, Sunday is assumed .

firstw eekofy ear

Option al. A constant that specifies the first week of the year .
If not specifie d, the first week is assumed to be t he week in
which January 1 occurs.

The interval argument has these settings:

Many procedures and functions are built into VBA. You do not need to
write a function that tells the current date and time because we have the
Now function. Similarly, you do not need to write complex code that
stores your application information in the Windows registry as you can
use the Sa veSet t i ng and GetSet t i ng procedures.


I Chapter 9: Standard VBA Calls I


Visual Interface
It is time to begin working with the
-- Insertion Point
1 X 10



--.-------------.--..--... --.-;

\ Column

"Visual" side of VBA. Let's consider the

form shown. It is composed of labels
(Level, Cells, X, Y, and Z), two combo
boxes (with SIDEWALK and column
selected), three text boxes (for X, Y, and
Z values), and two CommandButtons
labeled Insert and Cancel.


Creating a good graphical user interface

(GUI) can be one of the most
challenging elements of software development. Anyone can throw
buttons on a form, but making the interface user friendly and intuitive
takes thought, effort, and being open to the ideas of others.
Let's discuss the form shown above. The goal is to allow the user to
insert cells on a specific level at a specific point. Which should come
first? The Cell Name ComboBox? If we know which cell we want to
insert, perhaps it should appear first. What if the list of cells is
dependent on the selected level? This would keep us from inserting a
cell on the wrong level. So, perhaps the level should be first. Then we
have the insertion point. Perhaps it would be best if the insertion point
appeared after the level and cells combo boxes. And perhaps we should



I Chapter 10: Visual Interface I

allow the user to pick the insertion point in addition to being able to
enter the insertion point by hand. Let's move things around a little.
How does this look? Better? OK.
We have the visual elements
arranged now. After you create
the basic interface, you can
begin writing code behind the
interface. We'll get into that a
little later. First, let's talk about
the controls we can add to our
user forms.


,~- A

abl ~ [ffij


"",~'J(T.i'~" )~""7"'-""'~'r j\"''''''''''''''~'''" ."'.......-'l:~~,

Ce"iLlnsertion ,



.. "

I Column

" l'8J

Insertion Point



The toolbox shows us the controls we can place

on our forms. Except for the pointer arrow, each
of the items shown are visual elements we can use
in our interface design .



Controls have properties, methods, and events. A property describes
how a control looks or behaves. Methods tell controls to do something.
For example, using the ComboBox "AddItem" method adds an item to
its list. Most events occur when the user interacts with our GUI. For
example, when a user clicks a button, the click event of the button is
triggered and executes any code we place in that event.

You can set properties at design time (that is while you are designing
your interface and writing code) or at run-time (when the program is
being run). Control properties are modified at design time by using the
Properties window.

I Properties, Methods, and Events I

You can display or hide the Properties

window. If a CommandButton is
selected but you cannot see the
Properties window, right-click on the
button and select "Properties" from
the context menu or press the <F4>

CommandButton 1

: 0




1 - fmBackStyleOpaque
CommandButton 1


At run-time, properties can be set as follows:

CommandButtonl . Enabled


We enabled the control named "CommandButtonl" by setting its

Enabled property to true.
We can also get property values at run-time.
MsgBox CheckBox l .Value
New LevelName = txtLevelName . Text

Notice how we begin by addressing the control by name, typing a

period, and then typing the property to work with. After you press the
period key, VBA shows a list of the available properties and methods.

Priva t e sub
MsgBo x Ch ec kB ox l . Val u8
NewLe v e lN a me
t xtLevel Na me .t e

@, i.II.E'tlt.J~.
@i' TextAlign
M;...,;;;;;;;;;...._ _ _......._ _ _ _ _ _ _ _ _ @ , TextLength

E nd Su b
< i

@i' Top
@, Value
@i' Vi sible

In the above example, I typed the name of a TextBox txtLevelName and

the period key so the helper window displays the available properties
and methods. As you begin typing the name of the property or method,
VBA automatically selects the first matching item in the list. When the
"txtLevelName.te" has been typed, if you press the <Enter> VBA fills in
the rest of the text and moves to the next line. If you press the <Tab>,
VBA fills in the rest of the text and moves to the end of the current line


I Chapter 10: Visua l Interface I

of code. Letting VBA finish our sentences lets us develop applications
very rapidly.

Control Events
Now, let's look at using control events. You write code for control events
in the form's code area, which looks identical to a code module but is a
little different. All controls currently inserted into the form are itemized
in the left-hand ComboBox. When a control is selected in the left
ComboBox (CommandButton 1 is selected below), we can then select
an event in the right-hand ComboBox (the click event is selected

To work with a different event, simply drop down the procedure

Combo Box (on the right) and select another event. As we can see here,
there are quite a few events from which to choose.


I Common Control Properties I


When you select an event not previously selected, VBA fills m the
framework of the event for us.

L~_on~_n~~~~~u~.~ n1. ____ .

~J t.~_ ~~.r e~~

. _. ___._._. .


_._ .


pdvate Sub CommandButt o nl KeyPress (ByVal KeyAscii As MSForms. ReturnInteger) -;::

End Sub

This is KeyPress event occurs when a key is pressed. Some events pass
parameters we can look at, such as the KeyPress event passing the
"KeyAscii" parameter. We can use this parameter as a variable to see
which key was pressed.


(ByVa l Button As Int eger ,

ByVal Shift As Integer, _
ByVal X As Sing le, ByVal Y As Single)

CommandButtonl_~l ouseDown

Here the MouseDown event tells which mouse button was pressed (The
button parameter), the state of the <Shift>, <Control>, and <Alt> keys
when the button was pressed down (the Shift parameter), and the
location on the mouse (X, Y parameters) when the mouse button was
In addition to supplying us with values, some parameters can be
modified. For example, the KeyAscii parameter in the KeyPress event
can be assigned a value of 0 (zero) inside the event to cause our program
to act as though no key was pressed.


Before discussing each control individually, let's talk about properties
and events that nearly all controls have in common.


I Chapter 10: Visu al Inte rface I

What's in a name? We work with controls by addressing them by name.
We then identify the property we want to get or set, or the method we
want to use. The name and property (or method) are separated by a
period. Take a look:
Labell . Caption

"Enter Level


Change the Caption property of the control named "Labell" by calling

the control by name, typing a period, and then typing "Caption" which
is the property we want to modify. When you want to SET a property
value, the "Control. Property" is on the left-hand side of the equal sign
and the value you are assigning it is on the right-hand side, as shown. To
GET the "Control. Property", put it on the right-hand side of the equal
sign and place a variable on the left-hand side of the equal sign like this:
XVa l

= t x t XVa l ue .Te x t

The variable XVal now holds the value of the Text property of the
txtXValue control.
Since we are discussing control names, we should say a word or two
about naming conventions. Control names follow the same rules as
variable names. They must begin with a letter, cannot contain spaces,
etc. Some naming conventions suggest that TextBox names should begin
with "txt", Labels should begin with "lbl", ComboBoxes should begin
with "cmb", etc. As with variable naming, if a convention needs to be
followed, you should follow it. If not, at least name the controls
something that makes sense. By default, controls are named such as
"TextBoxl", "TextBox2", "TextBox3" and so on.

Left, Top
All controls have a Left property and a Top property. These properties
dictate where to place the control on the form. The top left corner of the
form is (0, 0). So, if a TextBox is given a Left value of 0 and a Top value of
0, it appears in the upper left corner of the form.

I Common Control Properties I


Width, Height
All controls have Width and Height properties. These properties
determine the size of the control. We should consider the size and shape
of the controls we use. Just because a TextBox can have a width of 20 and
a height of 20 doesn't mean it should. If a TextBox is a set to be a singleline TextBox, it may make little sense to have its height greater than is
necessary to display a line of text. If on the other hand, you want to
display a square CommandButton, make the width and height
properties the same.

Why would you want to place a control on a form and then set its Visible
property to false? Controls are to be seen, right? There are times when
you may want to make a control visible or invisible based on other
conditions. Setting a control's Visible property to false makes it invisible
at run-time but it is still visible at design-time. The Visible property can
be changed at run-time between true and false as needed.

When a control has its Enabled property set to true, you can interact
with the control at run-time. When Enabled is false, the control turns
gray and you we are unable to interact with it. The Enabled property
does not affect the visibility of the control, only the interaction.

Pressing the <Tab> key at run-time moves from control to control. If
the TabStop property of a control is true, the control receives focus in its
turn. If TabStop is false, the control does not receive focus during

The TabIndex property determines the order in which controls receive
focus as you Tab from control to control.


I Chapter 10: Visual Interface I

Use the Tag property to do as you see fit. One thing you can do with the
tag is assign it a default value for a TextBox and give the user the ability
to click a "Load Defaults" button, causing the Tag property to populate
the Text property.

The ideal interface gives the user the controls necessary to perform the
proper functions without needing to refer to a user manual each time
the program is used. You could place a lengthy Label next to each
control explaining details about why the control is there and how to use
it, but this would clutter the interface. Hold the mouse cursor over a
control for more than a second or two to make VBA display the control
tip text.
Level Name:


IType in a Level Name that is 4 characte rs long . I

This example shows the ControlTipText property value. An experienced

user learns that the level name should be four characters long, while new
users benefit from having a little help on what they should enter.
Enough with the common control properties. Let's talk about each of the
standard controls one at a time, highlighting its primary properties,
methods, and events. Also shown will be its icon in the toolbox.

Labels help users know what to enter or select. Properties of note are as


Captio n

The text displayed to the user.


The font used to display the Caption.

The TextBox allows users to enter text or display text, often in singleline mode so text is displayed in one line. You can stretch a TextBox
vertically to display multiple lines.

I Common Control Properties I



The text disp layed in t he TextBox.


When "Fal se'; users ca n type into a TextBox; wh en

"True'; users can not. Wh en set to "True'; text in th e
TextBox can be selected and copied to the Windows
Clipboard even thoug h the text ca nnot be changed by
the user. When Locked, you change the text property
w ith code.


The maxim um number of characters t hat ca n be

entered. Default is 0, which means no maximum. If
asking for a Canadian postal code, you could set t he
MaxLength property to 6,to enter "V4A5M2'; but
would be prohibited from entering "85 302-1234".

Multiline and

When True, text inside the TextBox is wrapped to the

next line. When False, text is scro lled on one line.


To type in a TextBox without others being able to see

what is being typed, supp ly a password character.
Enter an asterisk (*) in this property to make an asterisk
appear each time a keyboard key is pressed . This can
keep others from seeing what is being typed (if they
are looking over our shou lder), but is not high ly secure.
It takes very little code for a seasoned developer to find
out exactly what is behind t he password characters
displayed on screen.


I Chapter 10: Visual Interface I


Gives the ASCII code of the character pressed on

the keyboard. Use this to restrict users from
entering specific characters, such as allowing only
numeric va lues to be entered.

Key Down, KeyU p

Gives th e keyboard key pushed down and released

as wel l as the state of th e <Shift>, <Control>,
and <Alt> keys. For examp le, we know if the <Fl>
key was hit by using the KeyDown or KeyUp event
but cannot see the <Fl> key in the KeyPress event
because it is not an ASCII character. This differs
from the KeyPress event which tells which
character was pressed (A or a).

~ (OMBOBox
Use ComboBoxes to allow users to drop down a list of items to choose
from, or depending on the Style property, users can type into a
ComboBox if the item is not listed.


Text selected or entered in the ComboBox.


O=Combo (select from li st or type into a ComboBox)

or 2=List (user must select from list).


Index of the selected item in ComboBox.

-1 = Nothing selected. 0 = First item in list is selected.
1 = Second item in list is selected and so on.


Number of items in the ComboBox.


Number of columns per row in the list.

I ListBox I



Adds a List Item to the ComboBox.


Removes an item from the ComboBox. Specify the list

index of the item to remove.


Clears all items from the ComboBox list.


Cli ck

Occurs when user cli cks on an item in t he ComboBox.


Occurs when the selected item changes. Th is is different

from the Cli ck event because it is possib le to change t he
selected item in the ComboBox by using arrow keys and
other keyboard keys.

Use ListBoxes to allow one or more items to be selected from a list.
ComboBoxes are similar but limit selection to one item at a time and, of
course, ListBoxes do not "drop down".


Text of th e Selected Item in the List.

Mu ltiSelect

Listlndex - Index of se lected item in list.

Se lected

True or Fa lse returned when we specify the List Index

of t he item w e wa nt to check on.

Co lumnCount

The number of columns per row in the list.

Add ltem

Same as ComboBox


Same as Combo Box


Same as ComboBox

I Chapter 10: Visual Interface I



Same as ComboBox


Same as ComboBox

CheckBoxes allow us to specify the selection of an item. Multiple
CheckBoxes can be on one form and behave independently from one
another. Using a pizza order analogy, you could use a CheckBox to
specify each topping.


Text displayed next to the CheckBox.


True or False. When true, CheckBox has possible values

of true, false, or null. When TripleState is false, possible
values are either true or false.


True, False, or Null.




Wh en user cl icks on a CheckBox, the value is set to either true

or false. Click events do not fire when the user clicks the
CheckBox and the value is set to Null (in Trip leState mode).


Value of the CheckBox changes.

Use OptionButtons when you want the user to make a single choice
between several possible items, such large, medium, or small. You could
use three OptionButtons for each selection.

ITogg le Button I



Text that is disp layed next to the Option Button.

Group Name

Option Buttons work in groups where only one can be

selected. To allow a user to select Large'; Medium';
Sma II" for a size and to allow them to select Red';
White'; IfBlue for the color, use two group names for
each group of OptionButtons.







See CheckBox Click Event.


See CheckBox Change Event.


This event is triggered when a user double-clicks the


The toggle button looks like a CommandButton but it behaves more like
a CheckBox. When selected, it looks indented. You typically see toggle
buttons used to specify whether a font is bolded, underlined, or


Text disp layed on the Toggle Button.


See CheckBox.


See CheckBox.


See Option Button.


See Option Button.


See Option Button.

I Chapter 10: Vi sua l Interface I




Frames are control containers. This means that controls can be placed
inside of them. If a frame's Visible property is set to false, all controls
inside it become invisible. When a frame is moved, all controls in it
move with it. Use frames to organize groups of controls.


The Caption shown in the upper left-hand corner of the



True or False.

Use CommandButtons to give users something to click on, such as these
(commonly captioned): "OK", "Cancel", "Print", "Open", and "Close".

Captio n

Text displayed in the Button.


Determines whether the Button receives focus

when the user clicks the button or if focus remains
with the previous ly selected contro l.


Triggered when the user clicks the button.


Use tab strips to present "Tab" selections. Do not confuse these with the
MultiPage Control even though they look alike. Tab strips are not
control containers. Rather tab strip buttons are a cross between toggle
buttons and OptionButtons. Only one tab on a tab strip can be selected
at any given time.

I MultiPage I



When set to True, multiple rows are displayed when

the number of tabs exceeds the width of the Tab Strip.
When set to False, the Tabs are all displayed on one

Selected Item

Which tab is selected?


0= Tabs, 1 = Buttons. Style controls how the Tab Strip

is to be displayed.


0= Top, 1 = Bottom, 2 = Left, 3 = Right.


Used to add Tabs to the Tab Strip.



Triggered when the active Tab changes.

The MultiPage control is a control container where each page has its
own collection of controls. Right-click a tab and select a function to add,
rename, delete, and reorder pages.


0= First Tab, 1 = Second Tab, etc.


The Number of Pages.


See TabStrip.


Used to add pages to the MultiPage control.

I Chapter 10: Vi sual Interface I




Triggered when the Active Page changes.

Scroll Bars allow the user to select and change numeric values. The
rectangle that moves as the value changes is called the Thumb.


The amount the Value property chang es wh en t he

user cl icks inside the Scro ll Bar area .

Sma liChange

The amount the Va lue property changes when the

user clicks on the outside Scroll Bar Arrows.


The Minimum Va lue the Scroll Bar can have when the
Scro ll Bar is Horizontal.


The Maximum Va lue the Scro ll Bar can have.


The Numeric value of the Scroll Bar.



Triggered when the value changes by clicking in the Large

Change area or on the Small Change arrows and after the
Thumb is released.


Tri ggered as t he Thumb is d ragged. If w e are allow ing t he

user to enter a number and are displaying the number in a
Label, for example, and do not implement the Scroll event,
the Label wil l not change until t he Thumb is dropped. The
Scroll event all ows us to see the number change as the
Thumb is dragged from one end of the Scro llBar to the other.

Use the spin button to allow users to change numeric values. It is similar
to the scroll bar but does not have a Thumb.

I Image I



Time in mill iseconds after the user begins ho lding

down a button before t he val ue beg ins scroll ing up or


Minimum Spin Button Value.


Maximum Spin Button Value.

Small Change

Amount value changes as use r cl icks butt ons.


The Value ofthe Spin Button.




Trigge red when value changes.

Spin Up

Triggered when the value increases.


Triggered when the value decreases.

Use the image control to display images in your interface. Acceptable file
formats are .bmp, .gif, .jpg, .wmf, and .ico.


0= None, 1 = Single.


The file to display in the Image Control.


0= Clip, 1 = Stretch, 3 = Zoom.


True to tile pictures inside the Image Control, False

for no tiling.


We started this chapter by discussing the importance of creating a useful
and intuitive interface, then introduced the standard controls. Now let's
create a few interfaces to demonstrate the properties, methods, and
events we covered. To accomplish this, you will insert a few new forms


I Chapter 10: Visual Interface I

in a new project. Begin by inserting one new form and working with it.
After it is finished, insert another new form, and so forth.


('{~"'~-"~""';:<"""7~C?.,..,."J, .... -;;,'"!:"'~'f'-"""'"

:~~!1n~~!tiqn ;,' :.,_.;":.,~.,,,.. /,'.;.. ~

level I



Here is the first interface you are going

to work with. Add the controls
beginning at the top and working down.

- Insertion Point

: r-1:----- ~I

The first controls are two ComboBoxes.

By default they are inserted with the
ComboBox 1 and ComboBox2 .
Change the names to cmbLevels and
cmbCelis. Next, insert two labels and
place them on the left-hand side of the
ComboBoxes. Make the caption properties for these labels "Level" and


The next section is a group of controls inside a frame. Insert the frame
and change the frame caption to "Insertion Point". Then insert the text
boxes, labels, and CommandButton. Change the TextBox names to txtX,
txtY, and txtz. Name the CommandButton c mdPic k and the caption
The last controls you will add are two CommandButtons named
cmdlnsert and cmdCancel with captions of "Insert" and "Cancel".
Placing controls in the form is only the beginning. The Levels
ComboBox needs to be filled with all of the levels in the active drawing
and the Cells ComboBox needs to be filled with all of the cells available.
Fill these ComboBoxes before the user sees the form. To accomplish
this, go to the Initialize Event of the User form .
Right-click on the form and select View Code in the pop-up menu. By
default we are taken to the Click event of the form. Select In i t i ali zein
the Procedure ComboBox.
Pr i vate

Sub UserForm_InitializeC)
MyLevel As Leve l
MyCellEnum As CellInformationEnumerator
MyCell As Cell Information
Each MyLevel In ActiveDesignFile.Levels
cmbLevels.AddItem MyLevel.Name

Set MyCellEnum = _
Application . GetCellInformationEnumeratorCTrue. True)

I User Interface Exercises I


While MyCellEnum .M oveNext

Se t MyC el l = MyCell En um . Current
cmbCells.Addltem ~yCell .Na~e
End Sub

Here is the code in the Initialize Event of the UserForm. It should look
like this:

Di m
Di m
Fo r

Sub UserFo r m In iti al ize()

My Le v e l As L;vel
MyCellEnum As Ce llI nfo r mat i o nEnume r ator
MyCe 11 As Ce llI nfo r mat i on
Each MyLevel In Act i veDesignFile . Levels
cmbLeve l s.AddItem MyLevel . Name

Se t MyCellEnum = Appl ication . GetCellInfo rmationEnumerat o r (True,
Whi l e MyCel l En um . Move Ne xt
Set My Ce 11 = MyC e llEnum .C urrent
cm bCel l s . Add I tem MyCe ll.Na rne
End Sub

Tr ue)

We are using the AddItem method to populate the Levels and Cells
ComboBoxes with the names of the levels and cells in the
Now that code is in place to populate the ComboBoxes, press <FS> to
run the code and make sure everything works. The ComboBoxes should
have the names of the levels and cells in them. Click the "X" in the upper
right -hand corner of the form to close it and go back into VBA.
One more thing needs to be done to the ComboBoxes. We want the user
to select the level or cell but we do not want the user to be able to typ e
anything into these two ComboBoxes. To accomplish this, change the
Style properties of the ComboBoxes to 2 - fmStyleDropDownList.
The next thing is to write some code so only numeric values can be
entered into the text boxes. Do this by working with the KeyPress event
of the text boxes.


I Chapte r 10: Vi sual Inte rface I

Right -click on the top TextBox and select View Code. This takes us to
the Change event of the TextBox by default. Selecting KeyPress in the
Procedure ComboBox takes us to the KeyP r ess event .

Private Sub txtX_KeyPress(ByVal _

KeyAscii As MSF orms.R eturnInteger)
Select Case KeyAs cii
Case Asc("O") To Asc("9")
Case Asc (". ")
If InStr(l, txtX.Text, ".") > 0 Then
KeyAscii = 0
End If
Case Else
KeyAscii = 0
End Select
End Su b

"KeyAscii" is passed to us in the KeyPress event. It tells us the ASCII

value of the character that was pressed. If the ASCII code is for the
numbers 0 through 9, the code will do nothing to the KeyAscii
parameter. This allows the value to be entered into the TextBox just as it
was typed.
Next look at the period symbol. If there is already a period in the
TextBox, set Key Ascii to zero which keeps another period from being
entered. If a period is not in the TextBox, do nothing to the KeyAscii
parameter so the period can be added.
If any other KeyAscii value is encountered, set the KeyAscii parameter
to zero (0) which causes the event to act as though nothing was pressed.

We need to put the same code into the KeyPress events of the txty and
txtZ controls. Simply copy and paste the code. One little change is all it
takes. When looking for a period, use the control name. After copying
and pasting the Select Case code, change the name of the control in the
InStr function to match the control of the KeyPress event.
Private Sub txtX_KeyPress(ByVal _
KeyAscii As MSForms.ReturnInteger)
Select Case KeyAscii
Case Asc( "O" ) To Asc( "9 " )
Case Asc ( "." )
If InStr(l. txtX . Text . "." ) > 0 Then

I User Interface Exercises I

KeyAsci i
End If
Case Else
End Select
End Sub


Private Sub txtY_KeyPress(ByVal _

KeyAscii As MSForms.ReturnInteger)
Select Case KeyAscii
Case Asc( "0") To Asc( "9" )
Case Asc( "." )
If InStr(l, txtY.Text , "." ) > 0 Then
KeyAsci i = 0
End If
Case Else
KeyAsci i
End Select
End Sub
Private Sub txtZ_KeyPress(ByVal _
KeyAscii As MSForms.ReturnInteger)
Select Case KeyAscii
Case Asc( "O" ) To Asc("9")
Case Asc( " . " )
If InStr(l, txtZ.Text, ". " ) > 0 Then
KeyAsci i = 0
End If
Case Else
End Select
End Sub

Let's handle the Cancel button next. When the user clicks the Cancel
button, we want to close the form. We have been right-clicking on
controls and selecting View Code to get into the events of the controls.
Double-click on the Cancel button now. This is another way to get into
the form's code area.
Private Sub cmdCancel CllCk()
Unload Me
End Sub


I Chapter 10: Visual Interface I

One line of code is all it takes to close the form.
That's it for the easy functionality; now for something more difficult.
When a user clicks the CommandButton, insert the selected cell on the
selected level at the entered insertion point.
Private Sub cmdInsert_Click ( )
If cmbLevels . Text = "" Then
MsgBox "Please select a level. "
Exit Sub
End I f
If cmbC el l s . Text
"" Then
Msg Box "Pl ease se l ec t a cel l."
Ex it Su b
End If
Dim InsPt As Point3d
Dim CellElem As CellElement
InsPLY = CDbl(txtY.Text)
InsPt.Z = CDbl(txtZ.Text)
Set Cel l Elem = CreateCe l lE l ement3(cmbCe l ls.Text . I nsPt. True)
CellElem.Leve l = ActiveDesignFi l e.Levels(cmbLevels . Text)
ActiveMode l Reference . AddElement CellElem
End Sub

Before inserting, make sure the user selected a level and a cell to insert.
Use the txtX, txtY, and txtZ text boxes to get X, Y, Z values for the cell
origin. After creating the cell element, set its layer to the value of the
cmbLevels ComboBox Text property. The last thing to do is add the
element to the active model.
We have only one button left, the "PICK" button used for selecting the
cell origin. What do we want it to do? The button should be used to
allow the user to select a point in MicroStation instead of entering the X,
Y, and Z values by hand. To make the program work even better, if the
user has already selected the level and cell, we will insert the cell at the
selected point automatically. This keeps the user from needing to click
___ e "T_nser t"
_ h_u tt on aft er Cl'1Ck'mg th e "Plr'K"
\., _ h. ut ton.

I User Interface Exercises I


In short, we want the user to click inside MicroStation. This could

present a problem because, by default, VBA forms are modal. That is,
the form is active and thus prevents us from interacting with
MicroStation until the form is unloaded. To get around this potential
problem, display the form as modeless.
Sub DoCel i Insertio n( )
frmCell Insertion.Show vbModeless
End Sub

Place "DoCellInsertion" in a code module where it will be used to

display our form.
Now for the "PICK" CommandButton, we want the user to pick a point.
If the Level and Cell ComboBoxes are not empty, insert the selected cell
on the selected level at the selected point.
Private Sub cmdPick_Click()
Dim MyMsg As CadInputMessage
Dim MyOue As CadInputOueue
Dim SelPt As Point 3d
Di m CellE l em As Cell Element
On Error GoTo errhnd
Set MyOue = Application.CadInputOueue
Set MyMsg = MyOue . GetInput
Select Case MyMsg.InputType
Case msdCadInputTypeDataPoint
SelPt = MyMsg . Po i nt
txtX . Text
SelP t. X
t xtY .T ext = SelPt . Y
t xtZ . Text = Sel Pt.Z
And cmbCells . Text <>
I f cmb Levels . Tex t <>
Set Ce llElem =
Cr eate Cel l Element3(cmbCells.Tex t, _
SelPt , True)
CellElem . Leve l =
ActiveDesignF i le . Levels(cmbLeve l s . Text)
ActiveModelReference . AddElement CellElem
End If
Exit Do
Case Else
Ex it Do



I Chapter 10: Vi sua l Inte rface I

End Select
Exit Sub
Err. Clea r
End Sub

Let's look through the code slowly. First, we declare some variables.
That's the easy one. Second, we begin listening to the Input Queue. If the
user picks a point, we do the following:

Place the selected X, Y, and Z point elements into the three text


Ifboth the Levels ComboBox and Cells ComboBox are not

empty, insert the selected cell at the selected point and then
change its Level property to reflect the selected level.

If any other Input occurs or an error occurs, we exit the procedure.

If there is any concern about typing in all of the code shown for this
project, the VBA Project named ChapterlO.mvba can be found on the
CD included with this book.


This program concentrates on the ListBox control. Use the AddItem
method to add items to the ListBox. Then use the List property to place
values in the other columns of the ListBox. Use "Remove" to allow the
user to manually remove items from the ListBox.
We read a text file to get the points
into the ListBox of our interface.
The text file looks like this:

I Point List Reader I


Each line in the text file gives us the X, Y, Z elements of the text insertion
as well as the label we want placed at the X, Y, Z point.

Here is the interface.

Point List File: !c:\MicroStation VBA\points,txt

Points In File




Remove Selected


Plot Points





We are using two labels, one TextBox named txtPointFile, a ListBox

named IstPoints, and four CommandButtons named btnRead,
btnRemove, btnPlotPoints, and btnCancel.
ListBoxes, by default, use only one column. We want four columns, so
set the 'Column Count' property to 4. Specify the width of each column
in the ColumnWidths property using the value "60 pt;60 pt;60 pt;60 pt".
Set the last property, "MultiSelect", to "2 - fmMultiSelectExtended". This
allows the user to select multiple items in the list by using the <Shift>
and <Control> keys while clicking on items in the listbox.
It's time now to look at the code beginning with the Read button.
Private Sub btnRead_Click()
Dim PointText As String
Dim PointSp li t As Variant
Dim FFile As Long
FFile = FreeFile
Open txtPointFile.Text For Input As #FFile
Whi le EOF(FFile) = False
Line Input #FFile , PointText
If PointText <> "" Then
PointSplit = Split(PointText , ",H )


I Chapter 10: Vi sual Inte rface I

lstPoints . AddItem PointSplit(O)
1. 1)
1. 2)
lstPoints . List(lstPoints.ListCount - 1 . 3)


End If
End Sub

When the user clicks the Read button, we open the file specified in the
TextBox "txtPointFile" for input (this means we are going to read the
file), Since we have not reached the End Of File, we read the next line
from the file, split it into its elements, and add the elements to the
ListBox. Notice how we use "Addltem" to add the X component of the
point. AddItem is only used to add items to the first column of the
ListBox. Each additional column's value is set by using the List property.
When using 'List: specify the line index and the column, then give it the
value you want to put into the column.
The Remove button is meant to remove any items selected in the
ListBox. Since multiple items can be selected at once, be careful as you
remove the items.
Priv a te Sub btnRemove_Cl i ck()
Di m I As Long
For I = lst Point s. Li stCo unt To 1 St ep - 1
If ls t Poi nt s . Se l ected(I - 1 ) Then
lstPoints.RemoveItem I - 1
End If
Next I
End Sub

By beginning at the last item in the list and working to the first, you
avoid potential problems as you remove selected items.

Sub btnPlotPoints_Click()
TextIns As Point3d
TextVal As String
I As Long
PT As TextElement
RotMat As Matrix3d
I = 1 To lstPoints.ListCount
TextIns . X lstPoints . ListCI
TextIns.Y = lstPo i nts .List (I

1 . 0)

1. 1)

I Write Out File I


Textlns.Z = lstPoints .List (I - 1. 2)

Set PT = Application.CreateText Eleme nt1(Nothing . _
lstpoints.List(I - 1. 3). TextIns. RoWat)
ActiveModelReference.AddEle~ent PT
End Sub

The 'btnPlotPoints' button looks at each item in the list and from it we
get the X, Y, and Z elements of the text origin as well as the text to
When the user clicks the 'Cancel' button, execute the following code:
Private Sub btnCancel_Click()
Unload frmPointList
End Sub

That's it for the buttons in the form. Now, how do we display the form in
the first place? In a code module, we place the following code:
Sub DoPointListReade r()
frmPointList . Sho w
End Sub

The macro DoPo i ntL i stReader is now used to display the form and all of
the great functionality we have just put in.


The next macro we are
going to write utilizes
OptionButtons. Here's the
visual interface:




Items To Write

"" File Format

rv Levels

P' Line Styles

I P' Text Styles

II P'Views
! .


r. :~~siij~:~:tl

""C_~~~~__"""_" __ """"_


i P' Subject
_ _ _ _ _ _ _l



I Chapte r 10: Vi sual Interface I

We have seven CheckBoxes named chkLevels, chkLineStyles,
c hkTextStyles, c hkViews, chkAuthor, c hkSubject, and c hkTitle. We
also have two OptionButtons named optASCIl and optHTML. To round
things out, we have two CommandButtons named cmdOK and

We want to allow the user to select any of the "Items To Write".

CheckBoxes are perfect for this.
As for the file format, only one selection should be made. This is why we
use OptionButtons.
Befo re we get into the code, it is important to understand that this
program is not as simple as the previous ones. Let's plan before we jump
We want to write to two file formats: ASCII and HTML. This meets our
needs today but what about tomorrow? We should think about future
uses as we develop applications to allow for scalability. We could place
all of the code in the C1i ck eve nt of the c mdOK button, however,
breaking the code into more manageable chunks makes it easier for us to
add file formats tomorrow.
Let's look at three procedures for writing the file sections: headers, lines,
and footers .
Sub PrintHeader (Header I n As Str i ng , FileNum As Long, _
Opti on al Col umns As Lo ng 1)
If optASCII.Value = True Then
Print #FileNum, "[" & HeaderIn & " J"
ElseIf optHTML.Value = True Then
Print #FileNum, "<table width=660> "
Print #FileNum , "<tr><td co l span= " & Columns &
" align =center><b> " & HeaderIn & "</td></tr> "
End If
End Sub

Use an I f and E1s elf statement to handle the two file formats for today.
Another E1s e If statement is all it takes to add another file fo rmat
Sub PrintLin e ( Lineln As String, FileNum As Long)
If optASC I I. Val ue = True Then
Print #FileNum, LineIn

I Write Out File I


Elself optHTML . Val ue = True Then

Dim XSplit As Variant
Dim I As Long
XSplit = Split(LineIn. vbTab)
Print #FileNum. "(tr)"
For I = LBound(XSplit) To UBound(XSplit)
Print #FileNum. vbTab & "(td)" & XSplit(I) & "(ltd)"
Next I
Print #FileNum. "(/tr)"
End If
End Sub

Use the procedure Pri ntL i ne for each of the selected items found. Use
another I f and E1s elf statement for the file formats.
Sub PrintFooter(F il eNum As Long)
If optHT ML. Value = Tr ue Then
Print #F i leNum. "(/table) " & vbCrL f
End If
End Sub

We only need to print a footer if the HTML option is selected.

Now it is time to look at the C1 ick Eve nt of the cmdOK button. There
are two sections in the C1 i ck Event. The first sets up the export. The
second section is a series of If ... Then statements, each directly related
to a CheckBox. Here it is:
Private Sub cmdOK_Click()
Dim My Fi 1e AsS t r i ng
Dim FFile As Lo ng
Dim my Level As Level
Dim myLStyle As LineStyle
Dim myTStyle As TextStyle
Dim myView As View
FFile = FreeFile
If optASCII . Value = True Then
MyFile = "c:\output.txt "
ElseIf optHTML . Value = True Then
MyFile = "c : \output . htm "
End If
Open MyFile For Output As #FFile
PrintHeader "FILE NAME " . FFile. 1


I Chapter 10: Visual Interface I

PrintLine Act i veOesign File .F ul lName , FFile
PrintFooter FFile
If chkLeve l s.Value
True Then
PrintHeader "LEVELS", FFi l e, 3
For Each myLeve l In ActiveOesignF i le . Levels
PrintL i ne myLevel.Name & vb Tab & _
my Leve l . 0esc r i pt i on & vb Tab &
myLevel . El ementCol or , FFi 1e
PrintFooter FFile
End If
If chkLineStyles.Value = True Then
PrintHeader "LI NE STY LES " , FFi l e, 2
For Each myLStyle I n Act ive Oes i gnFile.Li neStyles
PrintLin e myLStyle.Name & vbT ab & _
myL Style.Number, FF i le
PrintFo oter FFile
End If
If chkTextSty l es.Va l ue = True Then
PrintHeader "TEXT STYLES " , FFi l e, 3
For Each myTStyle In ActiveOesignFile . TextStyles
Pr i nt Li ne myTSt yle.N ame & vbT ab & _
myTStyle.Color & vbTab & _
myTStyl e. Backg r oundFi 11Co l o r, FFi 1e
PrintFooter FFile
End If
If chkViews.Value = True Then
Pr i ntHe ader "VIEWS " , FFile, 5
For Each myView In ActiveOesignFile . Views
PrintLin e myView . Orig i n.X & vbTab &
myView.Orig i n.Y & vb Tab &
my View.O r igi n .Z & vbTab &
myView.CameraAngle & vbTab & _
my View.C ameraF ocalLe ngth, FFil e
Nex t

I Write Out Fil e I

Pr i ntF oo t e r FFi le
End If
If chkAuthor.Value = True Then
PrintHeader " AJT~OR " . FFile
Print Line ActiveOesignFile . Author . FFile
Pri ntF oo t er FFile
End If
If chkSub j ect . Value = True Then
Pr i nt Header "SUBJECT ". FFil e
Prin t Line ActiveOes i gnFile . Subject. FFi l e
PrintFooter FF il e
End If
If chkT i tl e . Va lu e = Tr ue Then
Print Hea der "TI TLE " . FFi l e
Pr intLi ne Act i veOes i gn Fil e . Tit l e . FF ile
Pr in tFooter FFi l e
End If
Cl ose /l FFi l e
End Sub

We have saved the easiest event for last.

Pri va te Sub cmd Ca ncel_Cl i c k()
Unload frmWr i teOgnSettings
End Sub

We add the following procedure to a Module to display the Form .

Sub OoWr i te OutF il e()
frm Wr i teOgnSettings . Show
End Sub



I Chapter 10: Vi sua l Interface I

Here is a little program that provides real-time interactive panning and
zooming of the views of the ActiveDesignFile.





IPan I











I use the MultiPage control here. This provides tabs and unique
interfaces on each tab. I also use a few labels, a ComboBox, and three
scroll bars with Min values of -500 and max values of 500. When the
form is initialized, I populate the ComboBox with the View indexes. I
also set an initial value for the Pan scroll bars.
When you right-click on an existing tab in the MultiPage, you access
controls to add tabs (select "New Page"), to rename, delete, or move the
order of the pages.


.... .


~ ::..

. .. . . .. .



This is what the

MultiPage looks like
when a rig ht-click is
performed on an
existing page.

: ::::: :. ::::::
: ::;:: : :::: : .. ..
..... . .. .


.. .....

. ........ .

Let's start with the Initialize event of the User form.

Pr ivate Sub User Form_ In i t i al ize()
Dim ViewCen As Point3d
Di m MyView As Vie w
For Each MyView I n ActiveDes i gnFi l e . Views
cmbViews . AddItem MyView.Index

I Zoom And Pan I


cmbViews.Listlndex = 0
ViewCen = Act i veDes i gnF i le.V i ews(l) . Center
Vi ewCen.X
scrX.Va l ue
scrY.Value = ViewCen.Y
End Sub

Here is the Initialize event of the UserForm. We add each View's Index
to the ComboBox named cmbViews. Select the first element by
assigning the ListIndex value to O. The last step is to get the current
center of view 1 and apply the X and Y values to the scroll bars srcX and
Scroll bars have two events with which we will be working. The first,
Change event, is triggered each time the value of the scroll bar changes
except for when the Thumb is being scrolled. The scroll event is
triggered as the Thumb is dragged between the min value and max
We are going to create two procedures for performing the zoom and pan
Sub SetZoo m(Zoo mValue As Long. OldZoomValue As Long)
ActiveDesignFile.Views(cmbV i ews . Tex t ) . Zoom 1 + _
(ZoomValue - OldZoomValue) / 100
Act i veDesignFile.Views(cmbViews.Text).Redraw
En d Sub
Sub SetPan (XP an As Long . YPan As Lo ng)
Dim ViewOrigin As Point3d
ViewOrigin . Y = YPan
ViewOrigin.Z = 0
Act i veDesignFile . Views(cmbViews.Text).Center
ActiveDesignFile.Views(cmbViews .T ext) . Redraw
End Sub

ViewO r igin

When we use the zoom method of a view, providing a number greater

than 1 zooms in. A number less than 1 zooms out. The Zoom Method
zooms relatively. If we provide a zoom factor of 1.1 three times, the view
zooms in each time. Subtract the previous value from the current value
and divide the result by 100. Add that value to the number 1. This allows
us to zoom in and out as we move the scroll bar left and right. After
performing the zoom, issue a Redraw to see the result of the zoom.


I Chapter 10: Vi sual Inte rfa ce I

Panning is performed by adjusting the view's center.
You can see the code that is used to zoom in and out. Now let's look at
the events that call these procedures.
Private Sub scrZoom_Change()
SetZoom scrZoom.Value. scrZoom.Tag
scrZoom.Tag = scrZoom.Value
End Sub
Private Sub scrZoom_Scroll()
SetZoom scrZoom.Value. scrZoom.Tag
scrZoom . Tag = scrZoom.Value
End Sub

The Change and Scroll events for the scroll bar named scrZoom is
shown above. The code inside these events is the same. The Tag
property (as discussed previously) is there for whatever use we have for
it. Here is one way: use the tag to store the previous value. After we call
SetZoom, we set the tag value.
Now, let's talk about panning. We are using two scroll bars to set the X
and Y elements of the view's center.
Pr i vate Sub scrX_Change()
SetPan scrX .V al ue . scrY . Value
End Sub
Private Sub scrX_Scroll()
Set Pan scrX.Value. scrY.Value
End Sub
Private Sub scrY_Change()
Set Pan scrX.Value . scrY.Value
End Sub
Private Sub scrY_Scroll()
SetPan scrX . Value. scrY . Value
End Sub

I Review I


We will use user interfaces in a number of areas in the remainder of this
book as we learn more about MicroStation VBA. Keep the following
points in mind:
IB All controls have properties, methods, and events.
IB Address a control's properties and methods by the control
name, typing a period, typing the property or method, and then
providing parameters when required.
IB At run-time, even ts are triggered as the user interacts with your
IB Display user forms using the Show method.
IB Use the Initialize event to set values and populate controls prior
to the fo rm being displayed.


I Chapter 10: Visual Interface I


The MicroStation Object

Model - Objects
Objects are the basis fo r much of our VBA programming. Object
Models are hierarchal structures of objects. Rather than examine in this
chapter all of the objects, we will look at the tools available to work with
the MicroStation Object Model. After we look at the tools, we will look
at some of the Objects frequently used when working with MicroStation

In th is chapter:

The Object Browser


Auto List Members


The MicroStation VBA Help File


Adding Watches


The MicroStation Object Model


I Chapter 11: The Mic roStation Object Model- Objects I







One of the best tools to work with Object Models is the Object Browser.
Click on the Object Browser toolbar button to display the Object
Browser. The Object Browser can also be displayed by using the VBA
menu Vi ew > Object Browser or by pressing the <F2> key on the

<globals >
~ AccuD rawHints
~ ACS Manager

Members of 'Application '

,hI! @i'


~ Gt!!jllliiMi~!i!_


@i' ActiveDes ignFile

@i' AcliveM odelReferen ce

~ App li calion ObjectConnector

@i' AcliveSetting s
@i' AcliveWorkspace
,,~ AddAttachme ntEventsHandler

~ Arc Element

"'~ AddChangeTra ckEventsHand ler

~ AreaPattern

,,~ AddLevel ChangeEvents Hand ler

~ Attac hment

,,~ AddM odalDialogEventsHandler

~ Attachments
~ Auxilia ryC oordinateSystemE lement

"'~ AddModelAclivateEventsHand ler

.,,~ AddModelChangeEventsHandler

~ 8spline
~ 8spiineCurve

"'~ AddSaveAsEventsHandl er
.~~ AddViewUpdateEvents Handler

~ 8spiineCurveElement
~ 8sp ii neSurface
~ 8spiineSurfa ceE lement
~ Cad lnputM essage
li.'tiJ Cadln putQueue

" '~ AppendXDatum

"'~ App lyHorizontalSca lingFixForEMF
"'~ App lyVerli calScalingFixForEMF
,~~ As s emble Comp lexString sAndShape s
,,~ Atn2

~ APP li cationBelT1f1(


Class AI)plicalion
Member of MieroStatiollDGtl

The Object Browser has two combo boxes at the top. The top -most
combo box allows us to narrow the classes to a specific Library. In the
image above, the MicroStationDGN Library has been selected. The only
classes now shown belong to the MicroStationDGN Library.
When we select "Application"
in the Classes ListBox, the
"Members of 'Application'"
show up in the Members
Properties, Methods, and
Events of the selected Class.

IMembe rs of 'App lication'

@I Name


fJ OnOes ignF ileC losed

fJ OnOes ignFil eOpened

OpenOesignF ile

":0% Op enO es i gnFi IeF 0 rP ro gram

@I PaU1
:,=.% Pi

v ;

IThe Object Browser I


Three primary member types are shown in the Members ListB ox.
First are Properties. "Name" and "Path" are properties of the
Application Object.
Methods "OpenDesignFile", "OpenDesignFileForProgram" and "Pi"
belong to the Application Object.
Events "OnDesignFileClosed" and "OnDesignFileOpened" also belong
to the Application Object.

When we select a member in the list, we are shown the Declaration for
the selected member at the bottom of the Object Browser.
Function OpenDesignFile(OeslgnFileName As String. (ReadOml,'As Booleanl.
IV7Action As MsdV7Action msdV7 ActionAskUserl) As Desi'UlFile
Member of MicroSt~tionDGtI.A!l!l l ic~tion

The Declaration shows us the Parameters for the Function or Procedure

as well as the return value type of Functions.
In addition to clicking on the Classes and Members we are familiar with,
we can search Object Models using the Object Browser.


~"' i

I~.I It\

Iitll .~


.- .. Search Resu lts - ..... -.-.... ..... :. Jh
(de-=s-ea-:rch-:R-es-:ultc-ls ..- .......--..... -.-........_-........... _.-.-..

, Class


, Member


J. . . .
..,~ ,
1\ MicroStationOGN ~ Appli cationElement
~. AsTextElement .='=') I
1\ Mi croStati onOGN @! ArcEl em ent
@i' AsTextElement
1\ Mi croStationOGN !el1l.uxiliarICoordinateSvstemEIE~ AsTextElement :"-: "
Members of 'Text Node Element'


@! TagOefinition

:'SI . ,~ AddOa tabaseLink

\i!a TagOefinitions

" ,~


@! TagElernent


\i!a TagSet

~, ."~[A(j(jTeXiLine

~ TagSets



" '~ AddUse rAttributeData

Sub AddTextline(NewTextLine As String)

Member of Micr oStationDGtl.Texttlo<leElement

Notice the cursor over the Hide/Show Search Results button in the
Object Browser. A search for "text" in the MicroStationDGN Type
Library results in numerous results. So, if we do not know the specific
Class or Member we need, we can use the Object Browser to search
for it.


I Chapter 11: The MicroStation Object Model- Objects I

VBA gives us help as we write our code at design time.

End Sub


@i' iA.$.~~~~g~r:
~ ActiveDes ign Fil e
@i' ActiveMo delRefere nce
@i' ActiveSellin gs
I1j1 ActiveWorkspace
""~ AddAllachme ntEventsHandler
,,~ Ad dC ha ngeTra ckEventsH and ler

The "List Members" list displays as we work in VBA. Once the list
displays, we can use the arrow keys and page upl down keys to scroll
through the list. If we select "ActiveDesignFile" at this time and press the
period key, we see the following:

~!~ :~~~~ ~s A;~i~~~~1~~n

. . :.".'.~i

l'lsgBo}{ myapp. Acti veDesignF ile.

End Sub




@i' Author
@i' Client
.,,~ Close
@i' Comments
@' Company

The ''Auto List Members" list allows us to 'drill down' through an Object


If we see something in the Object Browser and would like to see more
detail on it, we can select it in the Object Browser and press the <Fl>
key on the keyboard. We are then presented with information about the

I MicroStation VBA Help Fi le I


Object, Property, Method, or Event that was selected in the Object


~~.--~~- ~?'r-;-:~

;-M'J""iRe,. ... ..,

"'~<-~-,...~"'-..;,.'" ~>




h fl


-~,""""'f'''' "~~',;:






"< ""'~


&1 MicrC!SIi!tiop VB V}sual Basic for AppiicaticlV5 Help.,;.,., .,,,,,~\;,~;,,,, ...":.,:'",< ;:{~.'~h"""" :"'o>~2{,","""~ ,;,' ~l1~




-~~--~ LOC~~ _ _8_ac_k__ _ _ _ _ _ _- - - - - - - - - - - - - - - - - - - : -1

,kontents Index l .2earch I Faili :
Ap pli cati o n object structu re
T~pe in Ihe ke~l:!ord 10 find:

Th e structure of the app li cation object is sho wn belo w:

IApplication Strueture

Application Object



ApplicationElement Object
..... :,
App!icationlO Property
ApplicationObiectConnector Obje

,e.ctl 'leDeslgnF I I e {OasignF i IS}


Act i vel,'l ode I Reference {M ode I Refe r enc e}

A ppl~ H o ri20nlaIScalingF i, F o rE M f


Ac ti v ~S e ttin~s { Sett i ngs}

ApplyRotation Property
ApplyS aveaViewE!ement Methoc


Act ; v eWurl\ sp ,Jce (WQ~kspcce)

Ap p l~Verl icaIS ca ling Fi,Fo r EM F ~

Appro:'limateWitMrcs Method
ArcE lement Object

Area Melhod
AreaModeHofe Property
AreaPaUem Object
AreaPatternDelta Property
ArePolesCollinear !vi elhod
ArrowSymbolCeliName Propert!,'
Allow TerminatorChalSymbol PIO~
Arrow TelminatorFont Ploperty
Allow TerminatorSymbolT ype Pial
As.AppficationElement Propelty
As.AlcElement PlOperty
AsAttachment Properly

A tt'Jched Ca ll L ib"cry {Ce II Librcry }

Ccd I n" utOu eue { CadI nputQueue f

t-- Caption ( String )

Appl ica t i on


AsBsplineCurveElement Properl!,'
AsBsplineSurfaceElement Proper
AsCeUElement Properl!,'
AsChainableElement Property
AsClosedE lement PlOperl!,'
AsComplexE lemenl Propelly
AsComplexShapeElemenl Ploper
AsComplexStringE!ement Properl!
AsConeE lement Ploperl!,'
AsCul veElement Ploperly
AsD imensionElement Property
AsOroppableElement Propelty
AsE liipseE lement Properly
AsE liiplicalE lemenl Property
AslntersectableElement Property
AsLineE lement Properly
AsMulliLineElement Property
Qi spla~



Corrmar.,j 5tate


C u'TsntCr aph i c.Grcup { L ong}

(ComrnandSt a te)

r-- F u I I Noms ( St~ i no f

-r-- HosActlveDesi gnFl l e

{Boo l)


Has A ctl v eMod~ I Refe r'e nG" {Bcal)


He ight {LonQ )


[s Acaderr,i eV er- s i on (800 I )


IsCel IL i br-or" y Attoch"d (Bao l


IsRegis tered ( Boo l


I s Ser i (] I i


Il ey inArguments {String)



{8 00 I }

t-- LaftFcs i t i on {Long)



Na ma {String}



) ,

Once in the MicroStation VBA Help File, we can click on the Index tab
and type "Application Structure" in the 'Search' box. Selecting
''Application Structure" from the Index list displays the MicroStation
Application Object structure. Select ''Application Object" from the list to
display a description of the object with hyperlinks to Properties,
Methods, Events, Example Code, and See Also which displays a list of
associated objects.


I Chapter 11: The MicroStation Object Model - Objects I

We have introduced adding Watches previously. Adding a watch to a
variable is an excellent way to see its Properties. Some of the Properties
are actually other objects that we can continue to traverse by expanding
the item in the tree. Others in th e list are Collections of Objects that we
can examine in the Watch window.

<No cell library>

"chapter11 .dgn (2D - V8 DGN) - Microstation V8 XM


ModelReferenceiModelRef, I
CadlnputQueue/CadlnputQ' I'
CursorlnformationlCursorlr \'



Is Serialized

"C: 'Program FileslBeniley'Microstation'J.Jstation .exe"


LeftPo s~ion



"C:'Program FileslBeniley'MicroStation"


User Name
W eith


RasterManager lRasterMan I
standard sCheckerConirolh I
ObjectN BE


"Version 08 .09 .00.92 W ndows x86"


Let's begin looking at the I\1icroStation Object Model by examining the
Application Object.

IThe MicroStation Object Modell


Application Object
The Application Object points to the MicroStation Application.

Sub TestApplicationA ()
Dim MyApp As New Application
MsgBox MyApp . Path
End Sub
Sub TestA pplicationB ()
Dim MyApp As Appl i cat i on
Set MyApp = Applicat i on
MsgBox MyApp . Path
En d Sub

Both examples shown here result in the variable MyApp pointing to the
MicroStation Application Object. Once a variable is pointing to the
Application, we can use that variable to manipulate the Application
Object through its Properties and Methods.
The Application Object is always available through the exposed Object
named "Application". This means when we are in VBA, we can use the
Object named Application at any time.
In addition to accessing the Application's properties and methods,
additional objects and collections under the Application object can be
accessed by traversing the object model. Do this by typing "Application",
the period key, and then the next level of the Object Model.
Sub TestApp l icationC ()
MsgBox Appl ic at i on.Pat h
End Sub

In this example, we have not declared any variables or set any variables.
We just use the Object named "Application" because it is always exposed
to us.
A comprehensive list of objects in the MicroStation Object Model is
available on the CD that accompanies this book. It is not feasible to give
the entire Object Model here in print but you will get an understanding
as to how large the Object Model is. Let's take a look at a selection of the
Properties and Methods of a few of the Objects we deal with on a regular


I Chapter 11: The MicroStation Object Model - Objects I

basis in MicroStation. Some read-only properties are marked with

~ Property ACSManager As ACSManager (read-only)
~ Property Active Des i gnF i l e As Design File (readonl y )
~ Property ActiveModelRefe r ence As
Mode l Refere nce {read-on l y}
~ Property ActiveSettings As Set t ing s {r eadonl y }
~ Property Act i veWorkspace As Workspace {readon l y}
~ Sub AddAttachmentEventsHandler(EventHandler As
IAttachm ent Event s)
~ Sub AddChangeTrackEventsHandler(EventHandler
As IChangeTrackEvents)
~ Sub AddLeve l Cha ng eEven t sHandler(Event Hand l er
As ILevelChangeEvents)
~ Sub AddModalDialogEventsHandler(EventHandler
As IModalDialogEvents)
~ Sub AddModelActivateEventsHandler(EventHandler
As IModelActivateEvents)
~ Sub AddModelChangeEventsHandler(EventHandler
As IMode l ChangeEvents)
~ Sub AddSaveAsEventsHand l er(EventsHandler As
~ Sub AddV i ew UpdateEventsHand l er(EventHandler As
IV ie wUp dat e Ev ents)
~ Sub Appe ndXDatum(X Data() As XDatum, Type As
MsdXDatumType, Value As Var i ant)
~ Functio n
Ap~ l y H orizontalScal i ngF i xForEM F (P i xelCo o rdi n at

e As Doub l e) As Long
Funct i on
ApplyVerticalSca li ngF i xForE MF(P i xe l Co ordinate
As Double) As Long
Funct i on
AssembleComplexStr i ngsAndShapes(Cha i nableEleme
nt s () As ChainableElement, [GapTo l er ance As
Double = - lJ ) As Ele men t Enu me ra tor

I The MicroStation Object Model I





Function Atn2(Y As Doub l e , X As Double) As

Sub AttachCellLibrary(CellLibrary Name As
String, [ConvertFromV7 As MsdConversionMode
msdConversion ModeA l waysJ)
Prop ert y Att ac hedCe l l Lib r ary As Cell Library
( read-onl y )
Pro perty Bsplin e As Bs plin e ( r ead- only)
Fun cti on ByCellColor() As Long
Funct i on ByCellLineStyle() As LineStyle
Function ByCellLineWeight() As Long
Function ByLevelColor() As Long
Function ByLev elLineStyle() As LineStyle
Function ByLevelLineWeight() As Long
Property CadlnputQueue As CadlnputQueue
Property Caption As String
Property CommandState As CommandState (readonl y)
1 As Element, Element2 As Element, Element3 As
Element, Template As Element, [OutputType As
MsdTangentElementOutputType =
msdTangentC ir cle s J,
[SamplesCount As Long = 10J) As
Sub CopyDesignFile(ExistingDesignFileName As
String, New Des i gn FileName As String,
[Overwrite As BooleanJ)
CreateApp l icationE l ement(ApplicationID As
Long, ApplicationData As DataBlock) As
Function CreateArcElement1(Template As
Element, StartPoint As Point3d, CenterPoint As
Point3d , EndPoint As Point3d) As ArcElement
Function CreateArcElement2 (Template As
Element, CenterPoint As Point3d, PrimaryRadius
As Double, SecondaryRadius As Double, Rotation


I Chapter 11: The MicroStation Object Model - Objects I

As Matr i x3 d . St art Angle As Doubl e . SweepAng l e

As Double) As ArcEleme nt
Function CreateArcElement3(Template As
Element. StartPoint As Point3d. PointOnCurve As
Point3d. EndPoint As Point3d) As ArcElement
Function CreateArcElement4(Template As
Element. StartTangent As Ray3d. EndPoint As
Point3d) As ArcElement
Function CreateArcElement5(Template As
Element. Chord As Segment3d. Arclength As
Doub l e. Plane Point As Poi nt3d) As ArcElement
Function CreateAreaPattern(RowSpacing As
Doub l e. ColSpac i ng As Double. Angle As Doub l e.
Ce l l Name As St r ing. Scale As Doubl e) As
Function CreateBsplineCurveElementl(Template
As Element. Curve As BsplineCurve) As
Function CreateBsplineCurveE l ement2(Template
As Element. Curve As InterpolationCurve) As
Function CreateBsplineSurfaceElementl(Template
As Element. Surface As BsplineSurface) As
Function CreateCellElementl(Name As String.
Elements() As _Element. Origin As Point3d.
[IsPointCell As Boolean]) As CellElement
Function CreateCellElement2(CellName As
String. Origin As Point3d. Scale As Point3d.
TrueSca l e As Boo l ean. Rotat i on As Matrix3d) As
Function CreateCellEle ment3(CellName As
String. Orig i n As Poi nt3d. TrueScale As
Boolean) As Cell Element
CreateCo mplexShapeElementl(ChainableElements()
As ChainableE l ement. [FillMode As MsdFi ll Mode =
msdFillMode UseAct i ve]) As ComplexShapeElement
As ChainableElement. [FillMode As MsdFillMode =

I The MicroStation Object Modell


msdFillModeUseActiveJ, [GapTolerance As Double

= -lJ) As Comp l exShapeElement
) As ChainableElement) As ComplexStringElement
CreateComplexSt r ing El eme nt2(Cha i nab l eEle ments(
) As Ch ai nableE l ement, [ GapT olera nce As Doubl e
= - lJ) As ComplexStringElem ent
Funct i on CreateConeEleme nt1(Tem pla t e As
Element, BaseRadius As Double, BaseCenterPoint
As Point3d, TopRadius As Doub l e, TopCenterPoint
As Point3d, Rotation As Matrix3d) As
Function CreateConeElement2(Template As
Element, Radius As Double, BaseCenterPoint As
Point3d, TopCenterPoint As Point3d) As
Function CreateCrossHatchPattern(Space1 As
Double, Space2 As Double, Angle1 As Double,
Angle2 As Double) As CrossHatchPattern
Fu nction CreateCurveElement1(Template As
Element, Points() As Point3d) As CurveElement
Function CreateDatabaseLink(Mslink As Long,
Entity As Long, LinkType As MsdDatabaseLinkage,
IsInformation As Boolean,
DisplayableAttributeType As Long) As
Function CreateDesignFile(SeedFileName As
String, NewDesignFileName As String, Open As
Boolean) As DesignFile
Function CreateDimensionElement1(Template As
Eleme nt, Rotation As Matrix3d, Type As
MsdDimType, [TextOrientationView As ViewJ) As
DimensionE l ement
Function CreateEl lip seElement1(Template As
Element, Per i meterPoint1 As Point3d,
PerimeterPoint2 As Point3d, PerimeterPoint3 As
Point3d, [FillMode As MsdFillMode =
msdFillModeUseActiveJ) As EllipseElement
Function CreateEllipseElement2 ( Templa t e As
El ement , Orig i n As Poin t 3d, PrimaryRadius As


I Chapter 11: The MicroStation Object Model - Objects I


Doubl e . Secondary Radius As Double . Rot ati on As

Matr i x3d. [F ill Mode As Msd Fill Mode =
msdFillModeUseActive]) As EllipseElement
Function CreateEllipticalElementl(Template As
Element. Ellipse As Ellipse3d. [FillMode As
MsdFillMode = msdFillModeUseActive]) As
Function CreateHatchPatternl(Space As Double.
Ang l e As Double) As HatchPattern
Function CreateLineElementl(Template As
El ement. Ver ti ces() As Po i nt3d) As LineE l ement
Funct i on CreateLineElement2(Te mplate As
El ement. StartPoint As Point3d, EndPoint As .
Point 3d) As Li ne Elem ent
Function CreateObjectlnMicroStation(ProgID As
String) As Unknown
Function CreatePointStringElementl(Template As
Element. Vertices() As Point3d, Disjo in t As
Boolean) As POintStringElement
Function CreateSavedViewEleme nt(ViewSpecifier
As Variant. Name As String. [Description As
String]) As SavedViewElement
Funct i on CreateShapeElementl(Template As
Element, Vertices() As Po in t3d, [FillMode As
MsdF il lMode = msdFillModeUseActive]) As
Function CreateSharedCellElementl(Name As
String. Elements() As _Element. Origin As
Point3d, [IsPo i ntCell As Boolean]) As
Share dCel l Eleme nt
Function CreateSharedCe l lElement2(Ce ll Name As
String. Origin As Point3d. Scale As Point3d.
TrueScale As Boo l ean, Rotat i on As Matrix3d) As
Funct i on CreateSharedCellElement3(CellName As
String. Origin As Point3d. TrueSca l e As
Boo l ean) As SharedCe l lElement
Function CreateTextElementl(Template As
Element. Text As String. Origin As Point3d.
Rotation As Matrix3d) As TextElement

I The MicroStation Object Modell







Fun ction CreateTextNodeElementl(Template As

Ele ment, Origin As Poin t3 d, Rotat i on As
Matrix3d) As TextNodeElement
Function CreateTextNodeElement2(Template As
Element, Origin As Point3d, Rotation As
Matrix3d, [IncrementNodeNumber As Boolean =
True], [Reserved As Unknown]) As
Property CurrentGraphicGroup As Long (readonly)
Property CursorInformation As
CursorInformation (read-on l y)
DataEntryRegionFromCriteria(StartPosition As
Long, Length As Long, Justification As
MsdDataEntryRegionJustification) As
Fun ction Deg rees(Ra dian s As Double ) As Double
Sub Delete XDa t um(X Data() As XDatum, Index As
Sub DetachC ellLi brary()
Function DLongAbs(Value As DLong) As DLong
Function DLongAdd(Terml As DLong, Term2 As
DLong) As DLo ng
Fun ct ion DLongComp(Valuel As DLong, Value2 As
DLong) As Long
Function DLongDivide(Numerator As DLong,
Denominator As DLong) As DLong
Function DLongFromDouble(Value As Double) As
Function DLongFromHexString(Value As String)
As DLong
Function DLongFromInt64(Value As Empty) As
Funct ion DLongFromLong(Value As Long) As DLong
Function DLongFromString(Value As String) As
Function DLongMod(Numerator As DLong,
Denominator As DLong) As DLong


I Chapter 11 : The MicroStation Object Model- Objects I



Fu nc tion DLongMultiply(Fact or 1 As DL ong,

Facto r2 As DL ong) As DLo ng
Function DLongNegate(Value As DLong) As DLong
Funct i on DLongS ubtract(M i nuend As DLong,
Su bt ra hend As DLong) As DLong
Function DL ongT oHexSt ring ( Val ue As DLong) As
Sub DLongTol nt64(Va l ue As DLong)
Function DLongToLong(Va lu e As DLong) As Long
Funct i on DLongToString(Value As DLong) As
Stri ng
Fu nct i on
Ell i pse3dFromEllipt i ca l El ement(Element As
El l ip t ica l Eleme nt) As Ellip se3d
{readPr operty Exec uti ngVBProjec t As Ob jec t
onl y }
Pr ope rt y FullN ame As Strin g
{r ead -on l y}
Fun ction
GetCe lllnf orm ationE nu mera t or(I ncl ud eSharedCell
s As Boo l ean , I nc lud e Ful lPa th As Boolean) As
Ce ll Inf or ma ti onEnum erato r
Fun ct i on GetCE xp r ession Valu e(C Exp r ess i on As
St ring , [Mdl Appli ca ti onN ame As St rin gJ ) As
Va ria nt
Fu nc tion
GetC Exp r ess i on Val ueAs DLong(C Exp r ession As
String, [MdlApp l icat i onName As StringJ) As
Function GetF l oodBoundary(Cand i dateElements()
As _Ele ment, Temp l ate As Element, SeedPoint As
Po i nt3d , [V iewSpecifier As VariantJ,
[FindHo l es As Boolean = Tr ue J, [ To l erance As
Doubl e = - lJ , [ Fill Mode As Msd FillM ode =
msdFillModeUseActiveJ) As Element
Funct i on GetReg i onD i fference(RegionSolid() As
_Ele men t, Reg i on Holes() As _Element, Templa t e
As Element, [FillMode As MsdFi l lMode =
msd FillModeUseActiveJ) As ElementEnumerator
Func t ion GetRegionlntersection(Reg i on1() As
_Element, Region2() As _Element, Template As

IThe MicroStation Object Modell








Element. [FillMode As MsdFil l Mode =

msdFil lM ode UseAct ive]) As El emen tEnumera tor
Function GetRegionUnion(Regionl() As _Element.
Region2() As _Element. Template As Element.
[FillMode As MsdFillMode =
ms dF i l lM odeUseAct i ve ]) As ElementEnumerator
Property HasAct ive Des i gnFile As Boolean
Property Ha sAc tiveModelReferen ce As Boolean
Property Hei ght As Lo ng
Sub InsertXDatum(XData() As XDatum. Index As
Long. Type As MsdXDatumType. Value As Variant)
Property IsAcade mi cVersion As Boolean {readonly}
Property IsCe llLibraryAttached As Boolean
Property IsRegistered As Boolean {read-only}
Propert y IsSe rialized As Boo l ean {read-only}
Property KeyinArguments As String
Property LeftPosit i on As Long
Function Matrix3dAdd2Scaled(MatrixO As
Matrix 3d. Matrixl As Matr ix 3d. Sca lel As
Double, Matrix2 As Matrix3d. Sca le2 As Doub l e)
As Matrix3d
Function Matrix3dDeterminant(Matrix As
Matrix3d) As Double
Function Matrix3dEqual(Mat rixl As Matrix3d.
Matrix2 As Matrix3d) As Boolean
Function Matrix3dEqualTolerance(Matrix l As
Matrix3d. Matrix2 As Matrix3d. To lerance As
Doub le ) As Boolean
Function Matrix3dFromAxisAndRotationAng le(Axis
As Long. Radians As Double) As Matrix3d
Function Matrix3dFromDirect i onAndScale(Vector
As Point3d. Scale As Double) As Matrix3d
Function Matrix3dFromMatrix3dTimesMatrix3d(A
As Matrix3d. B As Matrix3d ) As Matrix3d
Matr ix3dFromMatrix3dTimes Matrix3d TimesMatrix3d


I Chapter 11: The MicroStation Object Model - Objects I










(A As Mat r i x3 d, B As Matri x3d , C As Matr ix3 d )

As Mat ri x3d
Function Matrix3dFromPoint3dColumns(XVector As
Point3d, YVector As Point3d, ZVector As
Point3d) As Matrix3d
Function Matrix3dFromPoint3dR ows(XVector As
Point3d, YVector As Point3d, ZVector As
Point3d) As Matrix3d
Matrix3dFromRotationBetweenVectors(VectorO As
Po i nt3d, Vectorl As Po i nt3d) As Matr i x3d
Function Matrix3dFromRowValues(XOO As Double,
XOI As Doub le, X02 As Double, XI O As Double,
XII As Doubl e , Xl 2 As Doubl e , X20 As Do uble ,
X21 As Double, X22 As Double) As Matrix3d
Function Matrix3d FromScale(Scale As Double) As
Function Matrix3dFromSca l eFactors(Xscale As
Doub l e, Yscale As Double, Zscale As Double) As
Function Matrix3dFromTransform3d(Transform As
Transform3d) As Matrix3d
Matrix3dFromVectorAndRotationAngle(Axis As
Point3d, Radians As Double) As Matrix3d
As Double, YAxisSkewAng l e As Double, Xscale As
Double, Yscale As Double, Zscale As Double) As
Matr i x3d
Funct i on
Matrix3dGetComponentByRowAndColumn(Matrix As
Matrix3d, Row As Long, Co l As Long) As Doub l e
Function Matrix3dHaslnverse(Matrix As
Matr i x3d) As Boolean
Funct i on Matrix3d l dent i ty() As Matr i x3d
Function Matrix3dInverse(Forward As Matrix3d)
As Matrix3d
Funct i on Matrix3dlsldentity(Matrix As
Matrix3d) As Boolean

IThe MicroStation Object Modell







Fun ct ion Ma t ri x3 dlsOrth ogon al (Ma tri x As

Matr i x3 d) As Boo l ea n
Function Matrix3dlsRigid(Matri x As Matrix3d)
As Boolean
Function Matrix3dlsRotateScaleRotate(Matrix As
Matrix3d, Rotationl As Matrix3d, ScaleFactors
As Point3d, Rotation2 As Matrix3d) As Boolean
Function Matrix3dlsSignedPermutation(Matrix As
Matrix3d) As Boolean
Mat ri x3d l sXRotatio nYRotat i onZRotat i onScale(Mat
rix As Matrix3d , RadiansX As Double, RadiansY
As Double, RadiansZ As Doub l e, Scale As Doub l e)
As Boolean
Func t ion Matrix3d l sXYRotation (Ma t rix As
Matrix3d, XYRotationRadians As Double) As
Matrix3dlsXYRotationSkewAndScale(Matrix As
Matrix3d, XAxisAngle As Double, YAxisSkewAngle
As Double, Xscale As Double, Yscale As Double,
Zscale As Double) As Boolean
Function Matrix3dMaxAbs(Matrix As Matrix3d) As
Function Matrix3dMaxDiff(Matrixl As Matrix3d,
Matrix2 As Matrix3d) As Double
Fu nc ti on Matri x3dRotat i on FromCo l umnZ(Normal As
Point3d) As Matrix3d
Funct i on
Matr i x3dRotationFro mPoint3dOr i ginXY(Origin As
Poi nt3d, XPo i nt As Po i nt3d , YPoint As Point3d)
As Matrix3d
Fu nct i on Matrix3dRotat i onFromRowZ(Normal As
Poi nt3d) As Matr i x3d
Sub Matrix3dSetComponentByRowAndColumn(Matrix
As Matrix3d , Rowlndex As Long, Columnlndex As
Long, Value As Doub l e)
Funct i on Matrix3dSumSquares(Matrix As
Matrix3d) As Double
Function Matrix3dTranspose(Matr ix As Matrix3d)
As Matrix3d


I Chapter 11: The MicroStation Object Model - Objects I






Fun ction Ma t ri x3dZ ero () As Matri x3 d

Functi on
P As Long) As El ement
Md l GetDes i gnFi l eFromMode l RefP(ModelRefP As
Long) As Des i gnFil e
Functi on
Mdl GetModelReference Fro mMode l RefP(Mode l Ref P As
Long) As ModelReference
Pro perty MessageCenter As MessageCe nter
{read-on l y}
Property Name As String {read - only}
Sub OnDesign~ileClosed(DesignFileName As
Str i ng)
Sub OnDesignFileOpened(DesignFileName As
Function OpenDes i gnFile(DesignFileName As
String, [ReadOnly As Boolean], [V7Action As
MsdV7Action = msdV7ActionAskUser]) As
Fu nction
OpenDesignFileForProgram(DesignFileName As
String, [ReadOnly As Boolean]) As DesignFile
Property Path As String {read-only)
Funct i on Pi () As Doub l e
Pl ane3dlntersectsP l ane3d(Inter s ectionRay As
Ray3d , Pl aneO As Pla ne3d , Planel As Pl ane3d) As
Boo l ean
Pl ane3d ln te r sectsRay3d( IntersectionPo i nt As
Po i nt3d, Pa r amete r As Double, Pl ane As Pl ane3d,
Ray As Ray3d) As Boo l ean
Fu nction Po i nt2dAdd(Pointl As Point2d, Point2
As Point2d) As Po i nt2d
Funct i on POint2dAdd2Scaled(Orig i n As Point2d,
Ve ctorl As Po in t2d , Scalel As Double, Vec t or 2
As Po i nt2d, Scale2 As Double) As Point2d
Fun cti on Poin t 2d Add3 Sca led (Or i gi n As Poi nt 2d ,
Vecto rl As Poi nt2 d, Sca l el As Doubl e , Vector2

IThe Mi cro$tation Object Modell






As Point2d, Scale2 As Double, Vector3 As

Point2d, Scale3 As Double) As Point 2d
Function Point2dAddScaled(Origin As Point2d,
Vector As Point2d, Scale As Double) As Point2d
Function Point2dAreVectorsParallel(Vectorl As
Point2d, Vector2 As Po i nt2d) As Boolean
Fun ct i on
POint2d AreVect orsPerpend i cular ( Vect or l As
Point2d, Vector2 As Point2d ) As Bo olean
Funct i on Point2dCrossP r oduct( Vectorl As
Point2d, Vector2 As Point2d) As Double
Function Point2dCrossProduct3Points(Origin As
Point2d, Targetl As Po int 2d, Target2 As
Point2d) As Double
Function Point2dDistance(Poin tO As Point2d,
Pointl As Point2d) As Double
Function Point2dDistanceSquared(Pointl As
Point2d, Point2 As Point2d) As Double
Function Point2dDotDifference(TargetPoint As
Point2d, Origin As Point2d, Vector As Po int 2d)
As Double
Function Point2dDotProduct(Vectorl As Point2d,
Vector2 As Point2d) As Double
Function Point2dDotProduct3Points(Origin As
Point2d, Targetl As Point2d, Target2 As
Point2d) As Double
Function POint2dEqual(Vectorl As Poin t 2d,
Vector2 As Po in t2d) As Boolean
Function Point2dEqualTolerance(Vectorl As
Point2d, Vector2 As Point2d, Tolerance As
Double) As Boolean
Function Point2dFromXY(X As Double, Y As
Double) As Point2d
Function Point2dGetComponent(Point As Point2d,
Index As Long) As Double
Function Point2dInterpolate(PointO As Point2d,
S As Double, Pointl As Point2d) As Point2d
Function Point2dMagnitude(Vector As Po i nt2d)
As Double


I Chapter 11: The MicroStation Object Model - Objects I





Function Point 2dMag nitudeSquared(V ector As

Point2d) As Double
Function Point2dMaxAbs(Vector As Point2d) As
Fu nct ion Poi nt2 dNegate( Vector As Point2d) As
Point 2d
Fu nction Point2dNormalize(Vector As Point2d)
As Point2d
Fun ction Point2dO ne( ) As Point 2d
Point2dSignedAngleBetweenVector s(Vectorl As
Point2d, Vector2 As Point2d) As Double
Function Point2dSubtract(Pointl As Point2d,
Point2 As Point2d) As Point2d
Function Point2dZero() As Point2d
Function Point3dAdd(Pointl As Point3d, Point2
As Point3d) As Point3d
Function POint3dAdd2Scaled(Origin As Point3d,
Vectorl As Point 3d, Scalel As Double, Vector2
As Poi nt3d, Scale2 As Double) As Point3d
Function Point3dAdd2ScaledVector3d(Origin As
Point3d, Vectorl As Vector3d, Scalel As Doub l e,
Vector2 As Vector3d, Scale2 As Double) As
Function POint3dAdd3Scaled(Origin As Point3d,
Vectorl As Point 3d, Sca lel As Double, Vector2
As Point3d, Scale2 As Double, Vector3 As
Point3d, Scale3 As Doub l e) As Point3d
Function Point3dAdd3ScaledVector3d(Origin As
Point3d, Vectorl As Vector3d, Scalel As Double,
Vector2 As Vector3d, Scale2 As Double, Vector3
As Vector3d, Sca l e3 As Double) As Point3d
Function Point3dAddAng l eD i stance(Pointl As
Point3d, AngleRadians As Double, DistanceXY As
Double, Dz As Double) As Point3d
Function Point3dAddPoint3dVector3d(Base As
Po int 3d , Vector As Vector3d) As Point3d
Function Point3dAddScaled(Origin As Point3d,
Vector As Point3d, Scale As Double) As Point3d

I The MicroStation Object Modell








Fu nction Poi nt 3dA ddS ca l ed Vec t or 3d(Or igi n As

Poi nt 3d, Vector As Vector3d, Sc ale As Double)
As Point3d
Function Point3dAngleBetweenVectors(Vectorl As
Point3d, Vector2 As Point3d) As Double
Function Point3dAngleBetweenVectorsXY(Vectorl
As Point3d, Vector2 As Point3d) As Double
Function Point3dAreVectorsParalle l(Vectorl As
Point3d, Vector2 As Point3d) As Boolean
Po i nt3 dAreVec t orsPe rp end i cular(Vectorl As
Point3 d , Vecto r 2 As Point3d) As Boolean
Function Poin t 3dCrossProduct(Vectorl As
Point3 d , Vector2 As Point3d) As Po i nt3d
Function POint3dCrossProduct3Points(Origin As
Point3d, Targetl As Point3d, Target2 As
Point3d) As Point3d
Function Point3dCrossProduct3PointsXY(Origin
As Point3d, Targetl As Point3d, Target2 As
Point3d) As Double
Function Point3dCrossProductXY(Vectorl As
Point3d, Vector2 As Point3d) As Double
Function Point3dDistance(Pointl As Point3d,
Point2 As Point3d) As Double
Function POint3dDistanceSquared(Pointl As
Point3d, Point2 As Point3d) As Double
Function Point3dDistanceSquaredXY(Pointl As
Point3d, Point2 As Point3d) As Double
Function Po i nt3dDistanceXY(Po i ntl As Point3d,
Point2 As Poin t 3d ) As Double
Function Point3dDotD i fference(Target As
Point3d, Origin As Point3d, Vector As Po i nt3d)
As Double
Function Point3dDotD i fferenceVector3d(Target
As Point3d, Origin As Point3d, Vector As
Vector3d) As Double
Function Point3dDotProduct(Vectorl As Point3d,
Vector2 As Point3d) As Double
Function Point3dDotProduct3Points(Origin As
Point3d , Targetl As Point3d, Target2 As
Point3d) As Double


I Chapter 11: The MicroStation Object Model - Objects I

[8 Fun ct io n Point 3d DotPr oduct 3Points XY( Or i gin As










Poi nt3 d , Targetl As Poi nt 3d , Tar get 2 As

Point3d) As Double
Function Point3dDotProductXY(Vectorl As
Point3d, Vector2 As Point3d) As Double
Function Point3dEqual (Vectorl As Point3d,
Vector2 As Point3d) As Boolean
Function POint3dEqualTolerance(Ve ctorl As
Point3d, Vector2 As Point3d, Tolerance As
Double) As Boolean
Fu ncti on Poi nt 3dFromAngle Di stance(A ngl eRad i ans
As Doub l e, Di s t anceXY As Doub l e, Z As Doub l e)
As Point3d
Fun ct ion Poin t3d Fr omMatr i x3dCo lum n(Matrix As
Ma t ri x3 d, Col As Lo ng ) As Poin t3 d
Fun ction
Point 3dF r omM at r i x3dl nve r seT im esPo i nt 3d( Mat ri x
As Matri x3d , Po int As Poin t3d ) As Poin t3 d
Fun ct i on
Point 3dFromMat ri x3 dln ve r se Tr anspose Ti mes Point 3
d(M at r i x As Mat r ix3d, Po in t As Po int 3d) As
Point 3d
Fun cti on Po i nt 3d Fr omM atri x3 dRow(Matri x As
Matri x3 d, Row As Long) As Poi nt 3d
Fun ct i on
Poi nt3d From Matr i x3d Tim es Poin t3d( Matr i x As
Matrix3d, Point As Point3d) As Point3d
Fu nction Point3dFromMatrix3dTimesXYZ(Matrix As
Matrix3d, X As Do uble, Y As Double, Z As
Dou bl e) As Po in t3d
Funct i on
Point3d From Matrix3dTransposeTimesPo i nt3d(Matri
x As Ma t r i x3d, Point As Point3d) As Point3d
Point3dFromMatrix3dTranspose TimesXYZ(Matrix As
Matrix3 d , X As Do ubl e, Y As Double, Z As
Doub l e) As Po i nt3d
Function Point3dFromRay3dFractionParameter(Ray
As Ray3d, Fraction As Double) As Point3d
Functio n POint3d FromRay3dTangent(Ray As Ray3d)
As Point3d

I The MicroStation Object Modell






Fun ction
Point 3d Fr omSeg ment3dFra ct io nParameter(S egm ent
As Segment3d , Fracti on As Double) As Point3d
Function Point3dFromSegment3dTangent(Segment
As Segment3d) As Point3d
Function Point3dFromTransform3d(Transform As
Transform3d) As Point3d
As Transform3d, Point As Point3d) As Point3d
Po i nt3dFromTransform3d TimesXYZ(Transform As
Tr ansform3d, X As Do ubl e, Y As Double, Z As
Do uble) As Point3d
Function Point3dFromVector3d(Vector As
Vector3d) As Point3d
Function Point3dFromXY(Ax As Double, Ay As
Double) As Point3d
Function Point3dFromXYZ(Ax As Double, Ay As
Double, Az As Double) As Poin t 3d
Function Point3dGetComponent(Point As Po i nt3d,
Index As Long) As Double
Function Point3dInPolygonXY(Point As Point3d,
Po l ygonVertices() As Point3d, [Tolerance As
Doub l e = -1J) As Long
Function Point3dInterpolate( PointO As Point3d,
FractionParameter As Double, Point1 As Point3d)
As Point3d
Function Point3d IsPointInCCWSector(TestPoint
As Poin t3d, Or i gin As Poi nt3d, Ta rgetO As
Point3d, Target 1 As Point3d, UpVector As
Point3d) As Boolean
Fun ct i on
Point3dIsPointInSmallerSector(TestPo i nt As
Point3d, Origin As Point3d, Target1 As Point3d,
Target2 As Point3d) As Boolean
Function Point3dIsVectorInCCWSector(TestVector
As Point3d, VectorO As Po i nt3d, Vector1 As
Point3d, UpVector As Point3d) As Boolean
Po i nt3dIsVectorInSmallerSector(TestVector As


I Chapter 11: The MicroStation Object Model- Objects I





Po in t3 d, Vecto r O As Point3d , Vecto r l As

Po i nt3d) As Boo l ean
Function Point3dMagnitude(Vector As Point3d)
As Doub l e
Function Point3dMagnitudeSquared(Vector As
Point3d) As Double
Function Point3dMaxAbs(Vector As Point3d) As
Function Point3dNegate(Vector As Point3d) As
unction Point3dNormalize(Vector As Point3d) As
Function Point3dOne() As Point3d
Point3dPlanarAngleBetweenVectors(Vectorl As
Point3d, Vector2 As Point3d, PlaneNormal As
Point3d) As Double
Function Po int 3dPolarAngle(Vector As Point3d)
As Double
Function Point3dProjectToPlane3d(Point As
Point3d, Plane As Plane3d, [ViewSpecifier As
Variant], [ UseAuxil i aryCoordinateSystem As
Boo l ean = False]) As Point3d
Funct i on Point3dProjectToRay3d(Paramete r As
Doub l e, Point As Point3d, Ray As Ray3d,
[Vi ewSpecif i e r As Va r i ant],
[UseAuxiliaryCoordinateSystem As Boolean
False]) As Point3d
Function Point3dRotateXY(Vector As Point3d,
Theta As Double) As Point3d
Function Point3dSca l e(Vector As Point3d, Scale
As Doub l e) As Point3d
Sub POint3dSetComponent(Point As Point3d,
Index As Long, Value As Double)
Funct i on
Point3dSignedAngleBetweenVectors(Vectorl As
Point3d, Vector2 As Point3d, OrientationVector
As Point3d) As Double
ctorl As Point3d, Vector2 As Point3d) As Double

I The MicroStation Object Modell













POint 3dSm al l e r AngleB etw ee nUn ori ent edV ecto r sX Y(
Vector l As Po i nt3d, Vector2 As Po i nt3 d ) As
Function Point3dSubtract(Pointl As Point3d,
Point2 As Point3d) As Point3d
Functi on Point3dSubtractPoint3dVector3d(Base
As Point3d, Vector As Vector3d) As Point3d
Function Point3dTripleProduct(Vectorl As
Point3d, Vector2 As Point3d, Vector3 As
Point3d) As Doub l e
Function PO i nt3dTripleProduct4Points(Origin As
Po in t3d, Ta r getl As Po i nt3d, Target2 As
Poi nt3d, Target3 As Po i nt3d) As Doub l e
Fun ction Point 3dZero() As Point 3d
Funct i on PointsToPixelsX(PointCoordinate As
Double) As Long
Function PointsToPixelsY(PointCoordinate As
Double) As Long
Property ProcessID As Long
Sub Quit()
Function Radians(Degrees As Double) As Double
Function Range3dContainsPoint3d(Range As
Range3d, Point As Point3d) As Boolean
Function Range3dContainsXYZ(Range As Range3d,
X As Doub l e, Y As Double, Z As Double) As
Funct i on Range3dEqua l (Rangel As Range3d,
Range2 As Range3d) As Boolean
Function Range3dE qua l To l erance(RangeO As
Range3d, Rangel As Range3d , Tolerance As
Doub l e) As Boolean
Funct i on Range3 dEx t entSq uare d(Range As
Range3d) As Double
Functio n Range3dFromPoint3d(Point As Point3d)
As Range3d
Funct i on Range3dFromPoint3d Point3d(PointO As
Point3d , Po i ntl As Point3d) As Range3d
Funct i on
Range3dFromPoint3dPoint3dPoint3d(PointO As


I Chapter 11: The MicroStation Object Model- Objects I





Point 3d, Pointl As Point3d, Point2 As Poi nt3d)

As Range3d
Function Range3dFromRange3dMargin(Range As
Range3d, Margin As Double) As Range3d
Function Range3dFromXYZ(X As Double, Y As
Doub le, Z As Double) As Range3d
Function Range3dFromXYZXYZ(Xl As Double, YI As
Double, Zl As Double, X2 As Double, Y2 As
Double, Z2 As Double) As Range3d
Func tio n Range3dlnitC) As Range3d
Function Range3dlntersect(Rangel As Range3d,
Range2 As Range3d) As Range3d
Function Range3dlntersect2(ResultRange As
Range3d, Rangel As Range3d, Range2 As Range3d)
As Boolean
Range3dlsContainedlnRange3d(InnerRange As
Range 3d, OuterRange As Range3 d ) As Boolean
Function Range3dlsNull (Range As Range3d) As
Function Range3dScaleAboutCenter(Rangeln As
Range3d, Scale As Double) As Range3d
Function Range3d Union(R angeO As Range3d,
Rangel As Range3d) As Range3d
Function Range3dUnionPoi nt3d(Range As Range3d,
Point As Point3d) As Range3d
Function Range3dUnionXYZ(Range As Range3d, X As
Double, Y As Double, Z As Double) As Range3d
Property RasterManager As RasterManager
Sub Ray3dClosestPoint(Ray As Ray3d, SpacePoint
As Point3d, ClosePoint As Point3d,
CloseFraction As Double)
Sub Ray3dClosestPointBounded(Ray As Ray3d,
SpacePoint As Point 3d, ClosePoint As Point3d,
CloseFraction As Double)
Sub Ray3dClosestPointBoundedXY(Ray As Ray3d,
SpacePoint As Point3d, ClosePoint As Point3d,
CloseFraction As Double)

IThe MicroStation Object Modell






Sub Ray3 dClos es tPointXY(R ay As Ray3 d,

SpaceP oi nt As Poi nt 3d, Close Poi nt As Po i nt 3d ,
Cl oseFraction As Double)
Function Ray3dFromPoint3dStartEnd(PointO As
Point3d, Pointl As Point3d) As Ray3d
Function Ray3dFromPoint3dStartTangent(PointO
As Point3d, Tangent As Point3d) As Ray3d
Function Ray3dFromSegment3d(Segment As
Segment3d) As Ray3d
Ray3d FromTra nsform3dTimesRay3d(Transform As
Transform3d, Ray As Ray3d) As Ray3d
Funct i on Ray3dFromXYZXYZStartEnd(XO As Doub l e,
YO As Double, ZO As Doub l e, Xl As Double, YI As
Double, Zl As Double) As Ray3d
Function Ray3dLength(Ray As Ray3d) As Doub l e
Function Ray3dLengthSquared(Ray As Ray3d) As
Function Ray3dPlane3dlntersect(Ra y As Ray3d,
Pl ane As Plane3d, Point As Point3 d , Fraction As
Double) As Boolean
Function Ray3dRay3dClosestApproach(RayO As
Ray3d, Rayl As Ray3d, PointO As Point3d,
FractionO As Double, Pointl As Point3d,
Fractionl As Double) As Boolean
Functio n Ray3dRay3dlntersectXY(RayO As Ray3d,
Rayl As Ray3d, PointO As Point3d, FractionO As
Doub l e, Pointl As Point3d, Fractionl As Double)
As Boolean
Sub RedrawAllV i ews([ Ora wMode As Ms dOrawingMode
= msdOrawing ModeNo rm al ])
Sub RegisterV8ToV7F il ter(Handler As
IConve rtV 8ToV7)
Sub RemoveAttachmentEventsHandler(EventHandler
As IAttachmentEvents)
RemoveChangeTrackEventsHandler(EventHandler As
RemoveLevelChangeEventsHandler(EventHandler As


I Chapter 11: The MicroStation Object Model- Objects I

[B Sub











RemoveModal Di alogEve ntsHandler(EventHandler As

As IModelActivateEvents)
RemoveModelChangeEventsHandler(EventHandler As
Sub RemoveSaveAsEventsHandler(EventsHandler As
I SaveAs Eve nts)
Sub RemoveViewUpdateEventsHand l er(EventHand l er
As IViewUpdateEvents)
Sub Res et Dis pl ay Set( Show Ev eryt hin g As Boo l ean)
Sub SaveSe ttin gs()
Sub Segment3dC l osestPoint(Segment As
Segment3d, SpacePoint As Point3d, ClosePoint As
Point3d, Cl oseFraction As Double)
Sub Segment3dClosestPointBounded(Segment As
Segment3d, SpacePoint As Point3d, ClosePoint As
Point3d, CloseFraction As Double)
Sub Seg ment3dClosestPo in tBoundedXY(Segment As
Segment3d, SpacePo i nt As Point3d, ClosePo int As
Point3d, CloseFraction As Double)
Sub Segment3dClosestPo i ntXY(Seg ment As
Seg ment3d , Spac ePoin t As Point 3d, Cl os eP oint As
Po i nt3d, CloseFraction As Double)
Function Segment3dFromPoint3dStartEnd(PointO
As Point3d, Pointl As Point3d) As Segment3d
Segment3dFromPoint3dStartTangent(PointO As
Poi nt3d, Tangent As Po i nt3d) As Segment3d
Functio n Segment3d FromRay3d(Ray As Ray3d) As
Fu nction
rm As Transform3d, Segment As Segment3d) As
Function Segment3dFromXYZXYZStartEnd(XO As
Double, YO As Double, ZO As Double, Xl As

I The MicroStation Object Modell





Do ub le , Yl As Dou ble , Zl As Doub l e) As

Seg ment 3d
Funct i on Segment3dLength(Segment As Segment3d)
As Double
Function Segment3dLengthSquared(Segment As
Segment3d) As Double
Function Segment3dPlane3dlntersect(Segment As
Segment3d, Plane As Plane3d, Point As Point3d ,
Fraction As Double) As Boolean
Seg ment3dSegme nt3dClosestApproach(SegmentO As
Segme nt3d, Segment l As Segment3d, PointO As
Po i nt3d, FractionO As Doub l e, Pointl As
Point3d, Fract i onl As Doub l e) As Boo l ean
Segment3dSegment3dlntersectXY(SegmentO As
Segment3d, Segmentl As Segment3d, PointO As
Point3d, FractionO As Double, Pointl As
Point3d, Frac t ionl As Double) As Boolean
Sub SetCExpressionValue(CE xpre s sion As String,
NewValue As Variant, [MdlApplicationName As
Sub SetCExpressionValueAsDLong(CExpression As
String, NewValue As DLong, [MdlApplica t ionName
As String])
Sub ShowCommand([Command As String])
Sub Show Er r or ( [Err or As Str i ng])
Sub ShowPrompt([Prompt As String])
Sub ShowStatus([Status As String])
Sub ShowTempMessage(Area As MsdStatusBarArea,
Message As Str i ng, [De t ai l s As String])
Property StandardsCheckerController As
StandardsC heckerContro l ler
Sub StartBusyCursor()
Sub StopBusyCursor()
Property TopPosition As Long
Function Transform3dEqual (Transforml As
Transform3d, Transform2 As Transform3d) As
Boo l ean


I Chapter 11: The MicroStation Object Model - Objects I




Function Transform3dEqualTolerance(Transforml
As Transform3d, Transform2 As Transform3d,
Matrix Tolerance As Dou ble, Point Tol era nce As
Do ubl e) As Boo l ea n
Fun ction Transform3dFactorMirror(Transform As
Transform3d, ResidualTransform As Transfor m3d,
Mirror Transform As Transform3d, Fi xedPo i nt As
Poi nt 3d, Pl ane Norma l As Poi nt3d) As Boo l ean
Function Transform3dFromLi neAndRotation
Angle(PointO As Point3d, Pointl As Point3d,
Radians As Double) As Transform3d
Function Transform3dFromMatrix3d(Mat r ix As
Matrix3d) As Trans f orm3d
As Matrix3d, Origin As Point3d) As Transform3d
Function Transform3dFromMatr i x3dPoint3d(Matrix
As Matrix3d, Translation As Point3d) As
Function Transform3dFromMatrix3d
TimesTransform3d(Matrix As Matrix3d, Transform
As Transform3d) As Tran sform3d
Function Tra nsfor m3dFromMirrorPlane (O rigin As
Point3d, Normal As Point3d) As Transform3d
Function Transform3dFromPlane3dToWorld(Plane
As Plane3d) As Transform3d
Fu nc ti on Tran s form3dFromPo i nt 3d(Tran s la t ion As
Point3d) As Transform3d
Function Transform3dFromRowValues(XOO As
Double, XOI As Double, X02 As Double, Tx As
Double, XIO As Double, XII As Double, Xl2 As
Double, Ty As Double, X20 As Double, X21 As
Double, X22 As Double, Tz As Double) As
Tr ansform3d
Transf orm3dFromSqu a redTran s form3d(Tran sf orm As
Transform3d, Pr i maryAxis As Long,
Secunda ['yAx is As Long) As Trans form3d
Transform3dFromTra nsform3dTimesMa t rix3d(T r an sf
orm As Transfo r m3d , Matri x As Ma tr i x3d) As
Tra nsfo rm 3d

I The MicroStation Object Modell


[B Function






Tra nsform3dFro mTransfor m3d TimesT ransform3d(Tra

nsforml As Transform3d, Transform2 As
Transform3d) As Transform3d
sTransform3d( Transfo rml As Transform3d,
Tra ns f or m2 As Tr ans f orm3 d , Tr ansform3 As
Tran s f or m3d) As Tr ansf or m3d
Fu nct i on Transfo r m3d From Wor l dToP l ane3d(Plane
As Pl ane3d) As Trans f orm3d
Function Transform3dFromXYZ(X As Double, Y As
Double, Z As Double) As Transform3d
Transform3dGetMatrixComponentByRowAndCo l umn(Tr
ansform As Transform3d, Row As Long, Col As
Long) As Double
Transform3dGetPointComponent(Transform As
Transform3d, Row As Lo ng) As Double
Function Transform3dHaslnverse(Transform As
Transform3d) As Boolean
Function Transform3dldentity() As Transform3d
Function Tra nsform3dl nverse(In As Transform3d)
As Transform3d
Function Transform3dIsldentity(Transform As
Transform3d) As Boole an
Transform3dIsMirrorAboutPlane(Transform As
Transform3d, Pl anePoint As Point3d,
Pl aneNormal As Point3d) As Boolean
Function Trans f orm3dIsPlanar (Transform As
Transform3d, Normal As Point3d) As Boolean
Function Transform3dIsRigid(Transform As
Transform3d) As Boolean
Transform3dIsRotateAroundLine (Transform As
Transform3d, Fi xedPoint As Point3d,
Directi onVector As Point 3d, Rad i ans As Double )
As Boolean


I Chapter 11: The MicroStation Object Model - Objects I



Fun ction Tran s fo rm3dlsTranslate CTr ans form As

Tran sf orm3d, Tr ans l at i on As Poin t3 d ) As Boo l ean
orm As Transform3d, Translation As Point3d,
Rotat i onl As Mat r i x3d , ScaleFactors As Po int 3d,
Rotation2 As Ma trix3d) As Boolean
Function Transform3dlsUniformScaleCTransform
As Transform3d, FixedPoint As Point3d , Scale As
Double) As Boolean
Fu nct i on
Tra nsform3 dl sU ni formSca l eA ndRotateAro undL i neCT
ransform As Transform3 d , Fi xed Point As Poi nt3d,
Direc ti on Vector As Po i nt3d, Rad i ans As Doub l e,
Scale As Double) As Boolean
ansform As Transform3d, Rowlndex As Long,
Co lumnlnde x As Long, Value As Double)
Sub Transform3dSetPointComponentCTransform As
Transform3d, Rowlndex As Long, Value As Doub l e)
Function Transfor m3dZeroC) As Transform3d
Function UpdateGraphicGroupNumberC) As Long
Property UserNam e As Str ing
Property VBE As Object
(r ead-on l y )
Function Vector3dAddCVectorl As Vector3d,
Vector2 As Vector3d) As Vector3d
Funct i on Vector3dAdd2Sca l edCOrig i n As
Vector3d, Vectorl As Vecto r 3d, Scalel As
Do uble, Vec t or2 As Vec t or 3d, Sca l e2 As Double)
As Vec t or3d
Function Vector3dAdd3Sca l edCO r igin As
Vec t or 3d, Vecto r l As Vec t or 3d , Sca l el As
Doub l e, Vector2 As Vector3d, Scale2 As Doub l e,
Vector3 As Vector3d , Scale3 As Doub l e) As
Funct i on Vector3dAddScaledCOr i gin As Vector3d,
Vector As Vector3d, Scale As Do uble) As

IThe MicroStation Object Modell









Fu nc ti on Vect or 3dA ngleB etw eenV ecto r s( Ve ct or l

As Vec t or 3d. Ve cto r2 As Vector3d) As Doub l e
Function Vector3dAng l eBetweenVectorsXY(Vectorl
As Vector3d. Vector2 As Vector3d) As Double
Function Vector3dAreVectorsParallel (Vectorl As
Vector3d. Vector2 As Vector3d) As Boolean
Fun ct ion
Vector 3dAreVe ct or s Perp endicular(Ve ctorl As
Vec t or3d. Vector2 As Vector3d) As Bo olean
Function Vector3dCrossP rod uct( Vectorl As
Vector3d . Vector2 As Vector3 d ) As Vec t or3d
Function Vector3dCrossProd uct 3Points(Orig i n As
Point3d. Targetl As Poi nt3d. Target2 As
Po i nt3d) As Vec t or3d
Function Vector3dCrossProductXY(Vectorl As
Vector3d. Vector2 As Vector3d) As Double
Function Vector3dDistance(Vectorl As Vector3d.
Vector2 As Vector3d) As Double
Function Vector3dDistanceSquared(Vectorl As
Vector3d. Vec t or2 As Vector3d) As Double
Function Vector3dDistanceSquaredXY(Vectorl As
Vector3d. Vector2 As Vector3d) As Double
Function Vector3dDistanceXY(Vectorl As
Vector3d. Vector2 As Vector3d) As Double
Function Vector3dDotProduct(Vectorl As
Vector3d. Vector2 As Ve ctor 3d) As Doubl e
Function Vector3dDotProductXY(Vectorl As
Vector3d. Vector2 As Vector3d) As Double
Function Vector3dDotProductXYZ(Vector As
Vector3d. Ax As Doub l e. Ay As Doub l e. Az As
Doub l e) As Double
Function Vector3dEqual (Vectorl As Vector3d.
Vector2 As Vector3 d) As Boo l ean
Function Vector3dEqualTolerance(Vectorl As
Vector3d. Vector2 As Vector3d. Tolerance As
Double) As Boolean
Funct i on Vector3dFromMatrix3dColumn(Matrix As
Matrix3d. Col As Long) As Vector3d
Function Vector3dFromMatrix3dRow(Matrix As
Matrix3d. Row As Long) As Vector3d


I Chapter 11: The MicroStation Object Model - Objects I

[B Function











Vector3dFromMatrix3d TimesVector3d(Matrix As
Matrix3d, Vector As Vector3d) As Vector3d
Function Vector3dFromMatrix3dTimesXYZ(Matrix
As Matrix3d, X As Double, Y As Double, Z As
Doubl e) As Vector3d
Fun ction
Vector 3dFromMat r i x3d Tran spos eT i mes Ve ctor 3d(Ma t
ri x As Matr i x3 d , Vector As Vecto r 3d) As
As Matrix3d, X As Double, Y As Double, Z As
Double) As Vector3d
Function Vector3dFromPoint3d(Point As Point3d)
As Vector3d
Vector3dFromTran s form3dCo1umn(Transform As
Transform3d, Col As Lo ng) As Vector3d
Function Vector3dFromTransform 3d Row(Tran sfo rm
As Trans fo rm3d, Row As Long) As Vec tor3d
Vector3dFromTransform3dTimesVector3d (T ransform
As Transform3d, Vector As Vector3d) As Ve cto r3d
Vector3dFromTransform3dTimesXY Z( Transform As
Transform3d, X As Double, Y As Double , Z As
Double) As Vector3d
As Transform3d) As Vector3d
Transform As Transform3d, Vector As Vector3d)
As Vector3d
form As Transform3d, X As Double, Y As Double,
Z As Double) As Vector3d
Function Vector3dFromXY(Ax As Double, Ay As
Double) As Vector3d

I The Micro Station Object Modell






Func t i on Ve ct or 3d From XYAngleAndM agnitu de(T het a

As Doubl e , Magnitude As Doubl e) As Vecto r 3d
Function Vector3dFromXYZ(Ax As Double, Ay As
Double, Az As Double) As Vector3d
Function Vector3dGetComponent(Vector As
Vector3d, Index As Long) As Double
Function Vector3dInterpolate(VectorO As
Vector3d, FractionParameter As Double, Vectorl
As Vector3d) As Vector3d
Vector3d I sVector InCCWSector(TestVector As
Vector3d, VectorO As Vector3d, Vectorl As
Vector3d, UpVector As Vector3d) As Boolean
Vect or 3d IsVectorInCCW XYS ector(T es tVec t or As
Vector3d, VectorO As Vector3d, Vectorl As
Vector3d) As Boolean
Vector3dIsVectorInSmallerSector(TestVecto r As
Vector3d, VectorO As Vector3d, Vectorl As
Vector3d) As Boolean
Function Vector3dMagnitude(Vector As Vector3d)
As Doub l e
Function Vector3dMagnitudeSquared(Vector As
Vector3d) As Double
Function Vector3dMagnitudeSquaredXY(Vector As
Vec t or3d ) As Doub l e
Function Vector3dMagn i tudeXY(Vector As
Vector3d) As Double
Function Vector3dMaxAbs(Vector As Vector3d) As
Funct i on Vector3dMaxAbsDifference(Vectorl As
Vector3d, Vector2 As Vecto r 3d) As Double
Funct i on Vector3dNegate(Vector As Vector3d) As
Function Vector3dNorma l ize(Vector As Vector3d)
As Vector3d
Function Vector3dOne() As Vector3d
Vector3dPlanarAngleBetweenVectors(Vectorl As


I Chapter 11: The MicroStation Object Model- Objects I




Vector3d , Vec t or2 As Vec t or3d, PlaneNorma l As

Vector3d) As Doub l e
Funct i on Vector3dPo l arAng l e(Vector As
Vect or3 d) As Doub l e
Function Ve ctor3dR ot ateXY ( Vector As Vector3d ,
Theta As Double) As Vector3d
Fun ction Vector3dSca l e(Vector As Vector3d,
Sca l e As Doub l e) As Vector3d
Vector3dSignedAngleBetweenVectors(Vectorl As
Vector3d, Vector2 As Vector3d,
OrientationVector As Vector3 d) As Double
Vector3dSma l lerAngleBetweenUnorientedVectors(V
ectorl As Vector3d, Vector2 As Vector3d) As
(Vectorl As Vector3d, Vector2 As Vector3d) As
Function Vector3dSubtract(Vectorl As Vector3d,
Vector2 As Vector3d) As Vector3d
Function Vector3dSubtractPoint3dPoint3d(Target
As Point3d, Base As Point3d) As Vector3d
Function Vector3dTripleProduct(Vectorl As
Vector3d, Vector2 As Vector3d, Ve cto r3 As
Vec t or3d) As Do ub l e
Function Vector3dUni t Perpendicu l arXY(Vec t or As
Vector3d) As Vector3d
Funct i on Vector3dZero() As Vector3d
Property Version As String {read-only}
Property Visible As Boolean
Pro perty Width As Long


Pro per t y Application As Application {read on1y}


Sub Act iv at e ()

I The MicroStation Object Modell










Sub AddElement(Element As Element)

Sub AddE l emen t s( El eme nts() As _E lemen t)
Function AddNewNamedGroup([Name As String],
[Description As String]) As NamedGroupElement
Sub AddUserAttributeData(AttributeID As Long,
AttributeData As DataBlock)
Property AnyElementsSelected As Boolean
Property AsAttachment As Attachment (readonly)
Property AttachmentOr igi n As Point3d (readonly)
Property Attachments As Attachments (readonly)
Property Attac hName As Str ing (read -only)
Property CanBePlacedAsCell As Boolean
Prop erty CellType As Ms dCellType
Property Contro lElemen tCache As ElementCache
Function Copy() As Attachment
Function CopyElement(Element As Element,
[CopyContext As CopyContext]) As Element
Sub DeleteAllXData()
Function DeleteUserAttributeData(AttributeID
As Long, Index As In t eger) As Integer
Sub DeleteXData(ApplicationName As String)
Property Description As String
Property DesignFile As DesignFile
Property DisplayAsNested As Boolean
Property DisplayFlag As Boolean
Property DisplayPriority As Long
Prop er ty DisplaysRasterReferences As Boolean
Function DoubleToWorkingUnits(Value As Double)
As String
n As Long, [CacheIndex As Long] ) As


I Chapter 11: The MicroStation Object Model - Objects I



Property Element1D As DLong

(read-only )
Pr operty ElementsLocatable As Boolean
Property ElementsSnappable As Boolean
Property ElementsVisible As Boolean
Function GetElementBy1D(Element1D As DLong) As
Function GetElementBy1D64(Element1D64 As
Empty) As Element
Function GetLastValidGraphicalElement() As
Function GetMasterToReferenceTransform() As
Function GetNamedGroup(GroupName As String) As
Function GetReferenceToMasterTransform() As
Function GetSelectedElements() As
Function GetSheetDefinition() As
Function GetUserAttributeData(Attribute1D As
Long) As DataBlock()
Function GetXData(ApplicationName As String)
As XOatum()
Function GetXOataApplicationNames() As
Property GlobalLineStyleScale As
MsdGl oba 1 Li neStyl eSca 1e
Property GlobalOrigin As Point3d
Property Graphi cal El ementCache As El ementCache
Function HasAnyXOata() As Boolean
Function HasXOata(ApplicationName As String)
As Boolean
Property 1s30 As Boolean
Property IsActive As Boolean
Property IsAttachment As Boolean
Property IsElementSelected As Boolean

I The MicroStation Object Mod ell







Pr op e rt y IsLo cked As Boole an

Pr operty I sMi ss in gF il e As Boo l ean
{r ead -onl y}
Prope r ty I s Mi ssing Mode l As Boo l ean
{r ead only }
Property IsReadOnly As Boolean
Property IsTrueScale As Boo l ean
Pro pe r ty Level As Leve l
Pr ope rt y Level s As Leve l s
{r ead-on l y}
Property Li neStylesScaled As Boolean
Property LogicalDesc ri ption As St r ing
Pr operty Log i ca l Name As Stri ng
Property ManipulateAsElement As Boo l ean
Property MasterOrig i n As Po i nt3d
Property MasterUnit As MeasurementUnit
Function MdlModelRefP() As Long
Sub Move(Offset As Point3d, ApplyToClipElement
As Boolean)
Property Name As String
Property NamedGroup As String
Property NestLeve l As Long
Property NestOverrides As MsdNestOverrides
Property New LevelDisplay As MsdNewLeve l Oisplay
Property ParentModelReference As
{read - only}
Property Pl ot3d As Boolean
Property Presentation As MsdRenderingMode
Property PrintColorAdjustment As Boolean
Sub PropagateAn notationScale()
Function Range(Inc l udeAttachments As Boolean)
As Ra nge3d
Function Reattach(Fi l eName As Strin g ,
ModelName As String) As Attachment
Sub Redraw ( [ OrawMode As MsdOrawingMode
msdOrawi ngModeNorma 1])
Sub RemoveElement(Element As Element)
Sub ReplaceE l ement(OldElement As Element,
NewElement As Element)


I Chapter 11: The MicroStation Object Model - Objects I








Prope r ty Rev i s i onNu mber As String

Sub Rewrite()
Sub Rotate(Pivot As Po i nt3d, AboutX As Double,
Abo ut Y As Doub le, Ab outZ As Double,
Vi ewSp ec i fi er As Var i ant )
Property Rotation As Matrix3d {read-only}
Property ScaleFactor As Double
Property ScaleMasterUnits As Double {readonly}
Sub ScaleUniform(Origin As Point3d,
ScaleFactor As Double, ApplyToClipElement As
Function Scan([ScanCriteria As
Element Scan Cr i teria] ) As Elem entEnu merat or
Sub SelectElement(E l ement As Element,
[D i splayAsSe l ected As Boolean = True])
Sub SetAttachNameDeferred(FileSpecifica t ion As
Sub SetSheetDefinition ( NewDef i nition As
SheetDe f inition)
Sub SetXData(App l icationName As String,
NewXData() As XDatum)
Property Storage Un it As MeasurementUnit
Property SubUnit As MeasurementUnit
Pr ope r t y SubUnitsP e rMa st e rUni t As Doubl e
Property Transparency As Doub le
Property Type As MsdModelType
Sub UnselectAllE l ements()
Sub UnselectElement(Element As Element)
Property UORsPerMasterUnit As Double {readon 1y}
Property UORsPerStorageUnit As Double
Property UORsPerSubUn i t As Double (read-only)
Property UpdateOrder As Long
Proper ty UsesLights As Boolean
Function WorkingUnitsToDouble(Value As String)
As Double

IThe MicroStation Object Model l


~ Fu nct i on Add( Fi l eS pec if icat i on As St r i ng,
Mode l Name As String, LogicalName As String,
Description As String, MasterOrigin As Point3d,
ReferenceOr i gin As Point3d, [ TrueScale As
Boo l ean = True], [D i splayImmed i ately As Boolean
= True ] ) As At t ac hme nt
~ Functi on AddCoi nc ident(FileSpe c ifi cati on As
Str i ng, ModelN ame As String, Lo gi calName As
St r i ng, Descr i ption As String,
[Disp l ay Immediately As Boo l ean = True ] ) As
At tachment
~ Fu nction AddCoinc i dentl(FileSpecif i cation As
String, ModelName As Str i ng, Logical Name As
String, Des cri ption As St ring, Fl ags As
MsdAddAttac hm ent Fla gs) As Attachment
~ Function AddUs i ngNamedView(FileSpecification
As String, LogicalName As String, Description
As String, ViewName As String, CenterPo i nt As
Point3d, [DisplayImmediately As Boolean =
True]) As Attachment
~ Function AddUsingNamedViewl(F i leSpecification
As String, ModelNa me As String, Logica l Name As
String, Description As String, ViewName As
String, CenterPoint As Point3d, Flags As
MsdAddAttachmentFlags) As Attachment
~ Property Count As Long
{read- only}
~ Function FindByLogicalName(LogicalName As
String) As Attachment
~ Property Item As Attachment
~ Sub Remove(AttachmentSpecif i er As Variant)
Cad lnputMessage
~ Pr operty CommandKey in As St r i ng {read-on l y}
~ Property CursorButton As Long {read-only}
~ Property I nputType As MsdCadInputType {readonly}
~ Property Keyin As String {read-only}
~ Property Point As Point3d {read-only}
~ Property ScreenPoint As Point3d {read-only}
~ Property View As View {read-only}


I Chapter 11: The MicroStation Object Model - Objects I

[B CadlnputQu eue
[B Function Get l np ut ( [Type l As MsdCa dln putType =





msdCadlnputTypeAnyJ. [Type2 As
MsdCadlnputTypeJ. [Type3 As MsdCadlnputTypeJ.
[Type4 As MsdCadlnputTypeJ) As CadlnputMessage
Sub Se ndCommand (Co mm an d As St ri ng .
[ Tr eatLikeKeyboardlnput As Bo olean J)
Sub SendDataPoint(Da t aPoint As Point 3d.
[Vi ewSpec i f i er As Varia nt J . [Q ualifi er As
Sub Sen dD at aPo intF orLocate( El eme nt ToLocate As
El ement. DataPo i nt As Poin t3d. [ViewSpecif i er
As Var i antJ. [Qual ifier As LongJ)
Sub SendDragPoin t s(DownPoint As Po i nt 3d .
UpPoint As Point3d. [ViewSpecifier As VariantJ.
[Qual ifie r As LongJ)
Sub SendKey in(Keyin As String)
Sub SendLast lnput()
Sub Sen dM essage To Appl i cationCMd l App li cation As
Str in g. Message As String)
Sub SendResetC)
Sub SendTentativePointCDataPoint As Po int 3d .
ViewSpeci f i er As Varia nt)

[B Fu nc ti on Ad dN ew Leve l CLevelN ame As Str in g) As
[B Sub Attac hCo l orTab l eCCo l orTable As ColorTab l e)
[B Prope r ty Aut hor As String
[B Prope r ty Clie nt As Stri ng
[B Sub Cl oseC)
[B Proper t y Co mm en t s As St ri ng
[B Property Co mpany As Stri ng
[B Funct i on CustomPropertyEx i stsCNa me As Str i ng)
As Boo l ean
[B Property DateC r eated As Date {read-on l y}
[B Property DateLastP l otted As Date {read-only}
[B Property DateLastSaved As Date {read-only}

I The MicroStation Object Modell











Proper t y Defau l tModelReference As

Mode l Reference
{read-on l y }
Sub De l et eLevel(Leve l As Leve l )
Property Di mensionStyles As Di mensionStyles
{read -o nly }
Property Editor As String (read-on l y)
Function ExtractColor Table() As ColorTable
Pr oper t y Fe nce As Fence {read-on l y}
Function FindSavedV i ew(NamePattern As Stri ng .
[ Prev i ous l yFo undSave dView As
SavedViewE l ement]. [Namespace As String]) As
Property Fonts As Fonts (read-only)
Property For mat As MsdDes i gnFi leFormat (readon l y)
Property FormatMajorVersion As Long {readonly}
Property FormatM i norVers i on As Long (read only)
Property Ful l Name As String (read - only)
Funct i on GetCustomProperty(Name As String) As
Function Get El ementBy I D(E l ementID As DLong) As
El ement
Fun ct i on GetElementByID64(ElementID64 As
Empty) As Element
Funct i on Get LargestElementID() As DLo ng
Sub Get LargestEleme ntID64()
Property IsActive As Boolean
Property Keywords As String
Property LastSavedBy As String
Property Levels As Levels
Property LineSty l es As LineS t yles
( read-only)
Property Manager As String
Functio n MdlFileObjP() As Long
Functi on Mdl ModelRefP( ) As Long
Property Mode l s As ModelRe f erences (read-only)
Property Name As Str i ng (read-o nl y )


I Chapter 11: The MicroStation Object Model - Objects I





Property NonM ode lEl ementCache As Elemen tCac he

Property Path As String (read-only)
Property RevisionNumber As String (read-only)
Sub RewriteLevels()
Sub Save()
Sub SaveAs(NewFileName As String. [Overwrite As
Boolean = FalseJ. [NewFormat As
MsdDesignFileFormat =
Sub SetCustomProperty(Name As String. Value As
Property Subject As String
Property TagSets As TagSe t s (read-on l y )
Property TextStyles As TextStyles (read-on ly)
Property Title As String
Property TotalEditingTime As Long (read -only)
Proper t y ViewGroups As ViewGroups {read -o nly}
Property Views As Views (read-only)

~ Sub AddDatabaseLink(LinkToAdd As DatabaseLink)
~ Function AddTag(TagDefinition As
TagDefinition) As TagElement
~ Function Add Tags(TagSet As TagSet) As
~ Sub AddUserAttributeData(AttributeID As Long.
AttributeData As DataBlock )
~ Funct i on ApparentColor(View As View) As Long
~ Function ApparentLineStyle(View As View) As
Lin eStyle
~ Function ApparentLineWe i ght(View As View) As
~ Property AsApplicationElement As
ApplicationElement (read-only)
~ Property AsArcE l ement As ArcElement (readonly)
~ Property AsAuxiliaryCoordinateSystemElement As
AuxiliaryCoordinateSystemElement (read-only)

I The MicroStation Object Modell







Prop e rty AsBsplineCurveElement As

BsplineCurveElement {read-on l y}
Property AsBsplineSurfaceElement As
BsplineSurfaceElement {read-only}
Property AsCellE l ement As Cell Element {readon 1y }
Property AsChainableElement As
ChainableElement {read-only}
Property AsClosedElement As ClosedElement
Property AsComp l exElement As Comp l exElement
{read-on ly}
Property AsComplexShapeElement As
Comp l exS hapeEl eme nt {read-o nly}
Prop erty AsComplexStringElement As
ComplexStringElement {read-only}
Property AsConeElement As ConeElement {read only}
Propert y AsCurveE l ement As CurveElement
Property AsDi mensionElement As
DimensionE l emen t {read-only}
Prop erty AsDro ppableEl ement As
DroppableElement {read -on l y}
Proper ty AsE llip se Element As Ellip se Element
{read-on l y}
Property AsEllipticalElement As
Ell i pti cal El ement {read-only}
Property AsIntersectableElement As
In tersecta bleElement {read-only}
Property AsLineElement As LineElement {readonl y}
Property AsMultiLineElement As
MultiLineE l ement {read-on l y}
Property AsNamedGroupElement As
NamedGroupElement {read -o nly}
Property AsPlanarElement As PlanarElement
Property AsPointStr i ngElement As
PointStringElement {read-only }


I Chapter 11: The MicroStation Object Model - Objects I






Pro per t y AsP ossi bl y Plan a rEl ement As

Possib l yP l anar El ement {r ead-o nl y}
Property AsSavedViewElement As
SavedViewElement {read-only}
Property AsShapeElement As ShapeElement
Proper t y AsS ha r edCel lD efi ni tio nEl ement As
Sha r edCel l Def in i ti onEl eme nt {r ead-on l y}
Property AsSharedCellE l ement As
SharedCellElement {read-only}
Property AsTagE l ement As TagElement {readon l y}
Property As TextElem ent As TextE l ement {r eadonly}
Property AsTextNodeElement As TextNodeElement
Property AsTraversableElement As
TraversableElement {read-only}
Property AsVertexList As VertexLi s t {readonly}
Property Cache As ElementCache {read - only}
Property Cachelndex As Long {read-only}
Property Cl ass As MsdElementClass
Function Clone() As El ement
Proper t y Co l or As Lon g
Function ConstructVe r texList( Toleran ce As
Double) As Point3d()
Property Date LastModif i ed As Date {read - only }
Sub DeleteA llT ags()
Sub DeleteAl l XData()
Sub DeleteTag(Tag As TagElement)
Sub DeleteTagSet(TagSet As TagSet )
Func t ion DeleteUserAttr i buteData ( Attr i bu t eID
As Long, Index As Intege r ) As Integer
Sub DeleteXData(ApplicationNam e As St r ing)
Sub DrawToFile(Fi l eName As String, Width As
Long, Height As Long, [DrawBackGround As
Boo l ean = False])
Pr ope rt y Fil e Pos itio n As Lon g {r ea d- on l y }

I The MicroStation Object Modell










Fun ct i on GetContaining NamedGroups() As

Na medG r oupElement()
Function GetDatabaseLinks([DatabaseType As
MsdDatabaseLinkage], [EntityNumber As Long])
As DatabaseLink()
Funct i on GetP i cture( Wi dth As Lo ng, He i ght As
Lo ng, [Draw Bac kGr ound As Boo l ean = False ] ) As
Unknow n
Fu nct i on Get Rel atedE l ements( Locked As Boolean,
[ Tr averseType As Msd Memb erTraverseType =
msdMe mberTraverseCopy], [NewTraversal As
Boolean = True]) As ElementEnumerator
Function GetTag(TagSet As TagSet, TagName As
String) As TagElement
Function GetTags() As TagElement()
Function GetUserAttributeData(AttributeID As
Long) As DataBlock()
Function GetXData(Applica t i onName As Str ing)
As XDatum()
Funct i on GetXDataAppli cationNames() As
Property GraphicGroup As Long
Function HasAnyDa t abaseLinks([Database Type As
MsdDatabaseLinkage], [EntityNum ber As Long])
As Boolean
Prope rt y HasA nyTags As Boo l ea n {read -on l y}
Function HasAnyXData() As Boolean
Function HasXData(ApplicationName As Stri ng)
As Boolean
Property 1D As DLong {read-only}
Property 1D64 As Empty {read-on l y}
Property 1nD i splaySet As Boolean
Property 1sApplica ti onElement As Boolean
Property 1sArcElement As Boolean (rea d -only)
Property 1sAuxiliaryCoordinateSystemElement As
Boolean (read-only)
Property 1sBsp l ineCurveElemen t As Boolean


I Chapter 11: The MicroStation Object Model - Objects I





Pr ope rt y Is Bs plin eS urfa ceE l ement As Boo l ea n

{read-o nl y}
Property IsCellElement As Boolean {read-only}
Pr operty IsChainableElement As Boolean {readonly}
Prop er t y I sC l ose dE lement As Boo l ean {re ad onl y }
Pr ope rt y IsCo mpl ex El ement As Boo l ea n {re adon l y}
Property IsComp l exShapeE l ement As Boo l ean
{read-on l y}
Property IsComp l exStr i ngElement As Boo l ean
Property IsComponentElement As Boolean {readon l y}
Property IsConeElement As Boolean {read-only}
Property IsCurveElement As Boolean {read-only}
Property IsDimensionElement As Boolean {readonl y}
Property IsDroppableElement As Boolean {readonly}
Property IsEllipseElement As Boolean {readonly}
Property I sEl lipti calE l ement As Boolean
{read -o nly}
Property IsGraph i cal As Boolean {read-only}
Pr operty I sHidden As Boolean
Property I s I ntersectableElement As Boolean
{r ead-on l y }
Property IsLineElement As Boolean {read-only}
Property Is Locked As Boo l ean
Property IsModified As Boolean {read-only}
Property IsMultiLineElement As Boolean {readonl y}
Property IsNamedGroupElement As Boolean
{read-on l y}
Property IsNew As Boo l ean {read-only}
Property I s PlanarElement As Bool ean {readonly }

I The MicroStation Object Modell







Property Is PointStringElement As Boolean

Propert y IsPo ss ibl yP l anarElement As Boo l ean
Property IsSavedViewElement As Boolean (readonly)
Property IsShapeElement As Boolean (read-only)
Property IsSharedCellDefinitionElement As
Property IsSharedCellElement As Boolean
(read-o nl y)
Property IsSnappable As Boolean
Property IsTagElement As Boolean
Property IsTextElement As Boo le an
Property IsTextNodeE l ement As Boolean
(readon 1y)
Property IsTraversa bleElement As Boolean
(rea d-o nly)
Property IsValid As Boolean
(read-on l y)
Property IsVertexL ist As Boo l ean
Property Leve l As Level
Property LineStyle As LineStyle
Property LineWeight As Long
Function MdlElementDescrP([Detach As Boolean ] )
As Long
Sub Mirror(Pointl As Point3d, Point2 As
Sub Mirror3d(PlanePointl As Point3d,
PlanePoint2 As Point3d, PlanePoint3 As Po i nt3d)
Property ModelReference As ModelReference
Sub Move(Offset As Point3d)
Sub PartialDelete(Partiall As Element,
Partia12 As Element, Pointl As Point3d, Point2
As Point3d, Se l ector As Point3d, ViewSpecifier
As Variant)
Property Range As Range3d
(read-on ly)
Sub Redraw([DrawMode As MsdDrawingMode =


I Chapter 11: The MicroStation Object Model - Objects I




Sub RemoveAllDataba s e Li nks()

Fun ct i on Re moveD atabas e Li nk(Datab as eTyp e As
MsdDatabaseLinkage, [M SLinkNumber As Long],
[EntityNumber As Long]) As Long
Sub Rewrite()
Sub Rotate(Pivot As Point3d, AboutX As Double,
Abou t Y As Doub l e, AboutZ As Double)
Sub RotateAboutZ(Pivot As Po i nt3d, Angle As
Sub ScaleAll(Origin As Po i nt3d, XFactor As
Doubl e , YFac t or As Doubl e, ZFac t or As Doub l e)
Su b ScaleU ni form(Origi n As Po i nt3d,
Sca l e Facto r As Dou bl e )
Sub Se t XDat a(App li cat i onName As Str i ng,
NewXData( ) As XDatum)
Property Subtyp e As MsdElement Subt ype {read onl y}
Sub Tran s form(Transf orm 3d As Tran s form 3d )
Prop e rty Type As Ms dElementTy pe {r ead -o nly }
Prop e rt y URL As String {r ead- only }
Pro pe rty URLTitl e As Strin g {r ea d-o nl y }

~ Function BuildArrayFromContent s () As Ele me nt ( )
~ Fun ct i on Cl one() As El ement Enu me r ato r
~ Property Current As El ement {read-on l y}
~ Function M
oveNext() As Boolea n
~ Sub Reset()
~ Sub Excl udeAll Cl asses()
~ Sub Exc lu deAllColors()
~ Sub Exc l ud eA ll Leve l s()
~ Sub Ex c 1 ude Al l Li ne Sty 1e s ( )
ei ghts ()
~ Su b Excl udeA 11 Li neW
~ Sub Exc lu deA l lSubtypes()
~ Sub Exc l udeA 11 Types ( )
~ Sub Exc l udeGraphica l ()

IThe MicroStation Object Modell










Sub Excl ud eNonGr aphi ca l ()

Sub In clu deC la ss( El em Cl ass As Ms dEl ementClass)
Sub IncludeColor(Colorlndex As Long)
Sub IncludeLevel (Level As Level)
Sub IncludeLineStyle(LineStyle As LineStyle)
Sub IncludeLineWeight(LineWeight As Long)
Sub Inc l udeOnlyCell (CellName As String)
Sub IncludeOnlyFilePositionRange(Min As Long,
Max As Long)
Sub IncludeOnlyGraphicGroup(GraphicGroupNumber
As Long)
Sub IncludeOnly Hole()
Sub Inc lu deOn l ylnv i s i bl e()
Sub IncludeOnlyLocked()
Sub IncludeOnlyModified()
Sub IncludeOnlyModifiedRange(Min As Date, [Max
As Date])
Sub Incl udeOnlyNew()
Sub IncludeOnlyNonPlanar()
Sub IncludeOnlyNonSnappable()
Sub Incl udeOnlyOl d()
Sub Incl udeOnlyPl anar()
Sub IncludeOnlySnappable()
Sub Incl udeOnl ySol id()
Sub IncludeOnlyUnlocked()
Sub IncludeOnlyUnmodified()
Sub IncludeOnlyUserAttribute(UserAttribute I D
As Long)
Sub I ncludeOn l yVis i bl e()
Sub Inc l udeOn l yWi t hi nRa nge(Range As Range3d)
Sub I nc lu deSubtype( Long As Long)
Sub IncludeType(Type As Msd ElementType)
Sub Reset()


Sub AddUserAttributeData(Attr i buteID As Long,

AttributeData As DataBlock)


I Chapter 11: The MicroStation Object Model - Objects I







Function Del eteUse r At tributeDa ta(Attr ibut e ID

As Long, Ind ex As Integer) As Intege r
Property Description As String
Property ElementAccess As
Msd LevelElementAccess
Property ElementColor As Long
Property ElementLineStyle As Li neStyle
Property ElementLineWeight As Long
Function GetUserAttributeData(AttributeID As
Long) As DataBlock()
Property 10 As Long {read-only)
Property 1sActive As Boolean
Property IsDisplayed As Boo l ean
Property IsDisplayedlnView As Boolean
Property I sEffective l yDisp l ayed lnVi ew As
Bo ol ean
Property I sFrom LevelLibrary As Boo l ean {readonly)
Property IsFrozen As Boolean
Property Is1nUse As Boolean {read -only)
Function IsInUseWithinModel(Model As
ModelReference) As Boolea n
Property 1sLocked As Boolean
Property Name As String
Property Number As Long
Property OverrideCo l or As Long
Property OverrideLineStyle As LineStyle
Property OverrideLineWeight As Long
Property ParentLevel As Level
Property Plot As Boolean
Property UsingOverrideColor As Boolean
Property UsingOverrideLineStyle As Boolean
Property UsingOverrideLineWeight As Boolean

~ Sub Activate()
~ Sub AddElement(Element As Element)

IThe MicroStation Object Modell









Sub AddElem ent s( Element s ( ) As Ele ment )

Fun ct i on Add Ne wN ame dG ro up([ Name As Strin g],
[Descr i ption As String]) As Nam edGroupE l ement
Sub AddUserAttributeData(AttributeID As Long,
AttributeData As DataBlock)
Property AnyElementsSelected As Boolean
Property AsA tt achment As Attachment {readonly}
Property Attachments As Attachments {readon l y}
Property CanBeP l acedAsCel l As Boo l ean
Pro perty CellType As MsdCe l l Type
Property Con tro lEl emen t Cache As Ele mentCac he
Function CopyElement(Element As Element,
[CopyContext As CopyContext]) As Element
Sub De l eteAl l XData()
Funct i on DeleteUserAttributeData(AttributeID
As Long, Index As Integer) As Integer
Sub DeleteXData(ApplicationName As Str i ng)
Property Description As String
Property DesignF i le As DesignFile {read-only}
Function DoubleToWorkingUnits(Value As Double)
As String
Funct i on
ElementCacheContainingFi l ePosition(FilePositio
n As Lo ng, [Cac he I ndex As Long]) As
ElementCac he
Funct i on Get ElementByID( Ele mentID As DLong) As
Function GetE l eme ntByID64(Element I D64 As
Empty) As El ement
Func t ion Ge t LastValidGraph i calElement() As
Function GetName dGroup(GroupName As String) As
Function Ge t SelectedE le ments() As


I Chapter 11: The MicroStation Object Model- Objects I








Function GetSheetDefinition() As
Function GetUserAttributeData(AttributeID As
Long) As DataBlock()
Function GetXData(ApplicationName As String)
As XDatum()
Fun cti on GetXDa t aA ppl ication Names() As
String ( )()
Property GlobalOrigin As Point3d (read-only)
Property Graphi cal El ementCa che As El em ent Cach e
Function HasAnyXData() As Boolean
Function HasXData(ApplicationName As String)
As Boolean
Property 1s3D As Boolean {read-only}
Property 1sActive As Boolean (read-only)
Property IsAttachment As Boolean {read-only}
Property 1sElementSelected As Boolean {readonly}
Property IsLocked As Boolean
Property 1sReadOnly As Boolean (read-only )
Property Levels As Levels {read-only}
Property MasterUnit As MeasurementUnit
Funct io n MdlModelRefP() As Long
Prop e rt y Name As String
Property ParentModelReference As
Sub PropagateAnnotationScale()
Function Range(IncludeAttachments As Boolean)
As Range3d
Sub RemoveElement(Element As Element)
Sub ReplaceElement(OldElement As Element,
NewElement As El ement)
Function Scan([ScanCriteria As
ElementScanCriteria]) As ElementEnu merator
Sub SelectElement(Eleme nt As Element,
[DisplayAsSelected As Boolean = True])

I Review I



Sub SetSheetDefinition(NewDefin i tion As

SheetDefinit i on)
Sub SetXData(Applicati onName As String,
NewXData() As XDatum)
Property StorageUnit As MeasurementUnit
Property SubUnit As MeasurementUnit
Property SubUnitsPerMasterUnit As Double
{read-on l y)
Property Type As MsdModelType
Sub UnselectAllElements()
Sub Unse l ectElement(Element As Element)
Property UORsPerMasterUnit As Double {readonly)
Pr operty UORsPerStorageUnit As Double
Pr oper t y UORsP e r Su bUnit As Dou bl e {re ad -onl y)
Funct i on Wor kin gU nit sToDoubl e( Val ue As St ri ng)
As Doub l e

We have just displayed a fraction of the Objects available to us in
MicroStation. At times it is useful to see a listing (even a partial listing)
and browse through the items in it.
The Object Browser in VBA is especially helpful when attempting to get
a grasp on the Object Model of any Library. VBA includes other tools as
well that can aid in our development efforts. These include adding
Watches and the AutoList functionality.


I Chapter 11: The MicroStation Object Model- Objects I


The MicroStation Object

Model - Enums
What is an Enum? Enum is an abbreviation for enumeration, a
collection of constants that can be used in our code.
Let's consider the following enumeration:

msdDesign FileFormatCurrent
msdDesignFileFormatDWG = 3
msdDesignFileFormatDXF = 4


The enumeration name is "MsdDesignFileFormat". It has six members

with values ranging from -1 to 4. Each member in an enumeration has a
"msdDesignFileFormatCurrent" has a value of O. As we saw in the
previous chapter, some properties and methods make use of these
enumerations. For example,



I Chapter 12: The Mi croStation Object Model - Enums I

Sub SaveAs(New FileName As St ri ng. [Overwr it e As _
Boo l ean = False]. [NewFormat As MsdDes i gnFileFormat _
= msdDesignFileFormatCurrent])
The SaveAs method is found under the DesignFile object. When we use
it, we can specify a file name, whether an existing file should be overwritten, and the file format to be used. The data type for
"NewFileName" is String. The value type for "Overwrite" is Boolean.
The value type for "NewFormat" is MsdDesignFileFormat. The
"NewFormat" parameter utilizes the "MsdDesignFileFormat"
enumeration. As we use the SaveAs method, we see the following:
ActiveDesignFile.SaveAs "test.dgn", TrUe,
SaveAs(NewFiIeNameA"Stni?Q,IOYelwl1 CD ~~!2!I.i~~atcur~~ _ _CD msdDesignFHeFormatDWO
III msdOesignFileFormalDXF
!II mSdDesignFlieFormatUnknown
!II msdDeslgnFileFormal'l7
CD msdDesignFlleFo rmatvS

sdOesi nFileFormut - msdOes! IlFileFormlltCurrent )

While working in VBA, when we come to a parameter that utilizes an

enumeration, we see the list of the members of that enumeration. We are
not shown the value of each member.
Enumerations provide several benefits, with one of the largest being that
we can more easily see the desired parameter results as we look at our
code. In other words, seeing "msdDesignFileFormatDWG" is clearer
than seeing the number 3 in the NewFormat parameter.
There are two ways to use enumerators. One is to use the Name.Member
format, such as:
ActiveDesignFile . SaveAs "test.dgn", True, _
MsdDesignF ileFo rmat .msdDesignFileFormatDWG

The other way is to use the member name without the enumerator
ActiveDesignFile.SaveAs "t est.dgn ", True, msdDesignFileFormatDWG

Names of enumeration members often begin with the enumeration

name or a shortened version of the enumeration name. The above
examples use the "MsdDesignFileFormat" enumeration with the
"msdDesignFileFormatDvVG" member. Notice how the enumeration
name is used to begin the member name. Occasionally, an abbreviation
is used such as with the "MsdCoordinateAccuracy" enumeration. The
members of this enumeration begin with "msdAccuracy" instead of the
full enumeration name "msdCoordinateAccuracy". It should be noted

I The Enumeration List I


that all MicroStation enumerations begin with the three-letter

designation "Msd" and all member names begin with "msd",
Now that we have discussed what enumerations are and how they can be
use d, let's examine the enumerations available in MicroStation VBA.


msdACSTypeCyl i nd r ical
msdACS TypeNone = 0
msdACSTypeRectangu l ar
msdACSTypeSpherica l = 3
msdAddAttachmentElementsVisible = 4
msdAddAttachmentFlagNone = 0
msdAngleAccurac y3
msdAng l eAccuracy5



msdFormatDD_DDDD = 0
msdFormatDD MM SS = 1
msdFormatGradians = 2
msdFormatRadians = 3


I Chapter 12: The MicroStation Object Model - Enums I

msdAngleModeAzimuth = 1
msdAngleModeBearing = 2
msdAttachNone = 1

msdBsp l ineCurveOffsetCuspArc = 4
msdBsplineCurveOffsetCuspChamfer = 1
msdB s pl i neC urveO f fsetCus pJ ump = 0
msdBsp li neCurveOffsetCuspParabo la = 3
msdBsplineCurveOffsetCuspPoint = 2
msdBsplineCurveCircle = 3
msdBsplineCurveC i rcu l arArc
msdBsplineCurveEllipse = 5
msdBsplineCurveGeneral = 0
msdBsplineCurveHyperbolicArc = 7
msdB s pline CurveLin e = 1
msdBsplineCurveParabolicArc = 6
msdBsp l ineParame t r i zat i onCentripeta l
msdBsplineParametrizatio nChordLength
msdBsplineParametrizationInher i ted
msdBsplineParametrizatio nUniform
msdBsp l ineSurfaceU
msdBsplineSurfaceV = 1
Msd BsplineSurfaceType
msdBsplineSurfaceCone = 3
msdBsplineSurfaceGeneral = 0

I The Enumeration List I


msdBsp lin eS urfaceP l ane = 1

msdB sp lineSur faceRevo l ution = 6
msdBsplineSurfaceRightCylinder = 2
msdBsplineSurfaceRuledSurface = 8
msdBsplineSurfaceSphere = 4
msdBsplineSurfaceTabCylinder = 7
msdBsp l ineSurfaceTorus = 5
msdCadInputTypeAny = 5
msdCadInputTypeKeyin = 4
msdCadInputTypeReset = 2

msdCe l lTypeMenu = 1
msdCel l TypePoint = 7
msdChangePropagationA l ways = 2
msdChangePropagationGroupL ock
msdCha ngeTrackActionAppData = 8
msdChangeTrackAct i onDrop = 6
msdChangeTrackActionMark = 7
msdChangeTrackActionModelAdd = 9
msdChangeTrackActionModify = 3
msdChangeTrackActionModifyFence = 5
msdChangeTrackActionNewF il ePositionAndMod ify


I Chapter 12: The MicroStation Object Model - Enums I

msdCommandResult3dL i brary2dF il e
msdCommandResult3dOnly = 39
msdCo mmandResultAcceptOuery
msdCo mm andResultBadCellName
msd Comm an dRe s ult Ce ll Del et ed
msdCom ma ndRe s ult CellE xi sts = 55
msdCo mmandResultCe ll Li braryNot Found
msdCommandResultCellNestingError = 43
msdCommandResu l tCe llN ot Found = 44
msdCommandResultElementNotFound = 21
msdCommandResultEmptyFence = 27
msdCommandResultFileReadOnly = 287
msdCommandResultIllegalDefinition = 23
msdCommandResultNeedCharacters = 27
msdCommandResultNoActiveCell = 19
msdCommandResultNoFenceActive = 15
msdCommandResultNoOrigin = 56
msdCommandResultOffDesignPlane = 22
msdCommandResultReferenceNotFound = 7
ms dCommandResultSuccess = 0
msdCommandResultUnknownCommand = 16
msdConversionModeAlways = 1
msdConversionModeNever = 0
msdConversionModePrompt = 2
msdAccuracyO = 1
msdAccuracyl = 2
msdAccuracy16th = 56
msdAccuracy3 = 4


I The Enumeration List I


msdAccuracy32nd = 120
msdAccuracy4 = 5
ms dAccuracy5 = 6
msd Accu r acy6 = 7
msd Acc ura cy6 4t h = 248
msdAcc ura cy 8th = 24
msdAcc ur acy Hal f = 0
msdMasterUnits = 1
msdSubUnits = 0
msdWorkingUnits = 2
msdCopyContextLevelAlreadyRemapped = 4
msdCopyContextLevelByUserPreference = 0
msdCopyContextLevelCopyAlways = 3
msdCopyContextLevelCopylfD i fferent = 2
msdCopyContextLevelCopylfNotFound = 1

msdCopyViewPortApplyAspectRatio = 2
msdCopyV iewPortApp lySize = 3
msdCopyViewPortKeepCurrent = 0

msdDatabaseLinkagelnformix = 1
msdDatabase Linkagelngres = 32
msdDatabaseLinkageOdbc = 128
msdDatabaseLinkageOleDb = 256
msdDatabaseL i nkageOracle = 8
msdDatabaseLinkageXBase = 4
msdDataEntryRegionJustificat i onCenter = 0
msdDataEntryRegionJustificationLeft = -1
msdDataEntryRegionJusti f icationRight = 1


I Chapter 12: The MicroStation Object Model - Enums I

Msd Design FileForm at
ms dD es i gn Fi1 e Form atCurrent
msdDesignFi1eFormatDWG = 3
msdDesignFi1eFormatDXF = 4
msdDes i gnF i 1e Format Unknown
msdDe s i gnFi 1e For matV 7
msdD es i gn Fi1 e Forma t V8


msdDeve1opab1eCones = 4
msdDeve1opab1eConesP1anar = 5
msdDeve1opab1eRu 1eLines = 0
msdDeve1opab1eRu 1eLine s P1anar
msdDeve1opab1eShapes = 2
msdDeve1opab1eShap esPlanar
Msd Dialog BoxResult
msdD i alogBoxResu1tApply = 1
msdD i a1ogBoxResu1tCance1 = 4
msdD i a1ogBoxResu1tDefau 1t = 5
msdO i alogBoxResu 1tHe l p
msdD i a1ogBoxResu l tNo = 7
msdOia1ogBoxResultOK = 3
msdDialogBoxResu1tReset = 2
msdDia1ogBoxResultRetry = 8
msdDia1ogBoxResu1tStop = 9
msdDia1ogBoxResu1tYes = 6
msdDi a 1ogBoxResu1 tYesToA 11
msdDimAccuracyO = 0
msdDimAccuracy1 = 129
msdDimAccuracy16th = 8
msdDimAccuracy2 = 130
msdDimAccuracy3 = 132
msdDimAccuracy32nd = 16
msdDimAccuracy4 = 136
msdDimAccuracy4th = 2

IThe Enumeration List I


msdDimAccuracy5 = 144
msdDimAccuracy6 = 160
msdD i mAccuracy64th = 32
ms dDim Accur acy7 = 192
msdDimAccuracy8 = 128
msdDimAccuracy8th = 4
ms dDim Acc uracySc i 6
msdDim Accura cySci7
msdDim Acc ur acySc i 8
msdDim Alig nme nt Ar bitr a ry
msdD i mAl i gnm ent Dr awin g = 1
msdDi mA lignm ent Tru e
msdDimAlignmentView = 0
MsdD i mAlter nat eT hres hol dCo mpar i sonGreater = 1
MsdDimAlternateThr esho ldC omparisonGreaterOrEqual = 3
MsdDimAlternateThresholdComparisonLess = 0
MsdDimAlternateThresholdComparisonLessOrEqual = 2
Msd Oi mAng leMeasure
MsdDimAngleMeasureAngle = 1


= 2


I Chapter 12: The MicroStation Object Model - Enums I

msdD i mBal l AndC hai nC hain TypeA r c = 2

msdDimCusto mSym bolDefault
MsdDimDMSPrecisionModeFixed = 0
Msd Di mDM SPrecisionModeFloa t ing

MsdDim LabelLine FormatAngleAbove = 3
MsdDi mLabe lL ine FormatA ngl eBe l ow = 5
MsdDimLabe lL ineForma t Angl eOver Lengt h = 1
MsdD i mLabe lL ine Forma tL engt hAbove = 2
MsdD i mLabe lL ineFor matLengthAng l eAbove
Ms dDi mLabe lLin eFor mat Lengt hAngl eBe l ow
MsdD im Labe lLin eFor mat LengthBelow = 4
MsdD i mLabe lLi neFor matS t andard = 0
msdDimM LNoteFrameTypeBox = 2
msdDimM LNoteFrameTypeNone = 0
msdDimMLNoteJustificationCenter = 3
msdDimMLNoteJustificationDynamic = 2
msdDimMLNoteJustificationLeft = 0
msdDimMLNoteJustificationRight = 1


IThe Enumeration List I

msdDimNoteHor iz ontalAttachmentRight


MsdDimNoteLeaderTypeCurve = 1
MsdDimNoteLeaderTypeLine = 0
msdDimNoteTextRotationlnline = 2
msdDimNoteVerticalAttachmentBottom = 4
msdD i mNoteVert i calAttachmentBottom Lin e = 3
msdDimNoteVerticalAttachmentDynamic Line = 5
msdDimNoteVerticalAttachmentMidd le = 2
msdDimNoteVerticalAttachmentTop = 0
msd DimNoteVerticalAttachmentTopLine = 1
msdD im NoteVert i calJust i f i cat ionDynam i c
msdDimNoteVerticalJustification Top = 0

= 3

msdDimPlacementTextPosit i onAuto = 2
msdDimPlacementTextPositionManual = 0
msdD imPl acementTextPositionSemiAuto
msdDimRadialModeCenterMark = 0
msdDimRadialModeDiame te r = 3
msdD i mRadialModeRadius = 1


I Chapter 12: The MicroStation Object Model - Enums I

Ms dDi mSt ac kedFractionAlignmentBottom
MsdO i mStacked FractionAl i gnment Top = 0


MsdOimStackedFractionTypeOiagon al
Msd Oim St ac kedFra ct i on Type Fr omF ont
msdO im Sty l ePropBallAndChai nAl ignment = 101
msdOimStylePropBallAndChainChainType = 103
msdDimSty l ePropBallAndChainIsActive = 104
msdOimStylePropBallAndChainNoOockOnDimLine = 106
msdOimSty l ePropBal l AndChainShowText Leader = 105
msdOimStylePropE xtensionLineAngleChordAlign = 213
msdO im StylePropExtens i onLineColor = 201
msdOimStylePropExtensionLineExtend = 202
msdDimStylePropExtensionLineJoin = 203
msdOimStylePropExtensionLineLeft = 204
msdOimStylePropExtensionLineLineStyle = 205
msdOimSty l ePropExtensionLineOffset = 206
msdOimSty l ePropExtens i onLineOverrideColor = 207
msdOimStylePropExtens i onLineOverrideLineStyle = 208
msdO i mSty l ePropExtens i onLineOve rrid eWeig ht = 209
msdOimStylePropExtensionLineRight = 210
msdOimStylePropExtensionLineShowAny = 211
msdOimStylePropExtensionLineWeight = 212
msdOimStylePropGeneralAlignment = 301
msdDimStylePropGeneralColor = 303
msdOimStylePropGeneralOimensionScale = 304
msdOimSty l ePropGeneralOimStyleOescription
msdOimStylePropGeneralOimStyleName = 306
msdO i mStylePropGeneralFont = 30 7

IThe Enumeration List I


ms dDimSt yle Pr op Gene r alI gnoreLeve lS ym bolog y

msdDimStyle Pro pGe nera lL ineSty l e = 309
msdDimStylePropGeneralOverrideColor = 310
msdDimStylePropGeneralOverrideLineStyle = 311
msdDimStylePropGeneralOverrideWeight = 312
msdDimStylePropGenera lRa dialMode = 313
msdDimStylePropGeneralRelativeDimLine = 314
msdDimStylePropGeneralShowCenterMark = 315
msdDi mStylePro pGener alStacked = 316
msdD i mSty l e Pro pGeneralS t ackOf f set = 317
msdD im StylePropGeneral Wei ght = 318
msdDimStyleProplnvalid = 0
msdDimStylePropMLNoteE l bowLength = 108
msdDimStylePropMLNoteFrame Type = 401
msdDimStylePropMLNoteHorAttachment = 407
msdDimStylePropMLNoteJustif i cation = 402
msdDimS t ylePropMLNoteLeaderType = 405
msdDimSty l ePropMLNoteLeftMargin = 410
msdDimStylePropMLNoteLowerMargin = 411
ms dDimStylePropMLNoteShowLeader = 40 j
msdDimStylePropM LN oteTextRotation = 406
msdDimStylePropMLNoteVerLeftAttachment = 408
ms dDimSty l ePropMLN ot eVerRightAttac hment = 40 9
msdDimStylePropMLNoteVerticalJustification = 404
msdDimSty l ePropPlacementAnnotat i onScale = 507
msdDimStylePropPlacementCompatibleV3 = 501
msdDi mStyle PropP l acement Level = 502
msdDimStylePropPlacementNotUseModelAnnotationScale 506
msdD im StylePropPlacementOverr i de Leve l = 503
msdDimStylePropPlacementTextPosition = 504
msdDimSty l ePro pSymbolDiameterChar
msdDimSty l ePropSymbo l DiameterFont
msdDimStylePropSymbo l Di ameterType


I Chapter 12: The MicroStation Object Model - Enums I

ms dDimSt yl eP r opSy mb olM ai nP r efi xC har = 606
msdD i mSty l e PropSymbo l MainSuffixC har = 607
msdDimStylePropSymbolPlusMinusType = 609
msdDimStylePropSymbolPrefix = 610
ms dDimStylePr opSy mbolP ref ix Ce l lNa me = 611
msdDimStylePropSymbolPrefixFont = 613
msdDimStylePropSymbolPrefixType = 614
msdDimSty l e PropSy mbo l Suffix = 615
msdD i mStylePropSy mbo l SuffixCe l l Name = 616
msdDi mStylePropSymbo l SuffixChar
msdDimStylePropSymbolSuffixFont = 618
msdDimStylePropSymbolSuffixType = 619
msdDimStylePropSymbolTolPrefixChar = 620
msdDimStylePropSymbolTolSuffixChar = 621
msdDimStylePropSymbolUpperSuffixChar = 623
msdDimStylePropTerminatorArrowCellName = 701
msd Dim Sty l e Pr opT e rm i nator Arr owType
msdDimStylePropTerminatorColor = 705
msd Di mSty l e PropTe r mi natorDo t Char
msdD i mStyleProp TerminatorFirst = 710
msdDimStylePropTerminator Heig ht = 711
msdDi mSty l e PropTerm i natorJoint = 712
msdDimSty l ePropTerminatorLeft = 713
msdDimStyleProp TerminatorLineStyle = 714
msdDimStylePropTerm i natorMinLeader = 715
msdDimSty l ePropTerminatorMode = 716

IThe Enumeration List I

msd DimStyle Pro pT e rmin ator No Lin eThruD ot = 718
msdDimStyle Prop Term i na t orNoLi neThru Or i gi n
msdDimStylePropTerminatorNoLineThruStroke = 720
msdDimStylePropTerminatorNote = 736
msdDimStylePropTerminatorNoteCellName = 738
msdDimStyleProp TerminatorNoteChar
msdDimStylePropTerminatorNoteFont = 740
msdDimStylePropTerminatorNoteType = 737
msdDimSty l eP r opTerm i natorOrig i nC har
msdD i mSty l ePropTerm i natorOr i ginFont
msdDimStylePropTerm i natorOriginType
msdDimStylePropTerminatorOverrideColor = 725
msdDimStylePropTerminatorOverrideLineStyle = 726
72 7
msdDimStylePropTerminatorRight = 728
msdDimStyleProp TerminatorStrokeChar
msdDimStylePropTermina t orStrokeFont
msdDimSty l eProp TerminatorStrokeType
msdDimStylePropTerminatorWeight = 734
msdDimSty l ePropTerminatorWidth = 735
msdDimS ty l e Pr opT ex t Ar cL eng t hSy mbol = 801
msdDimStylePropTextAutoLift = 802
msdDimStylePropTextCapsule = 804
msdD i mStylePropTextColor = 805
msdD i mSty l eProp TextDec i malCo mma = 806
msdDimStylePropTextFont = 808
msdDimStylePropTextFrame Type = 837
msdDimStylePropTextHeight = 809
msdDimStyleProp TextHorizontal = 810
msdOimStylePropTextHorizonta l Marg i n = 811
msdOimStylePropTextlnlineTextLift = 838
msdOimStylePropTextJustif i cation = 812
msdOimStylePropTextLeadingZero = 813
msdOimStylePropTextLocation = 835



I Chapter 12: The MicroStation Object Model - Enums I

msdDimSt yle PropTextOverrideColor = 816
msdDimStylePropTextOverrideHeight = 817
msdDimStylePropTextOverrideUnderline = 834
msdDimStylePropTextOverrideWeight = 818
msdDimStylePropTextOverrideWidth = 819
msdD imSt yl ePropTextSec Le ad ingZ ero = 820
msdDimStylePropTextShowSecondary = 821
msdDimStylePropTextStackedFractions = 830
msdDimStylePropTextStackedFractionScale = 832
msdDimStylePropTextStackedFractionType = 831
msdDimStyle Prop TextSuperscriptMode = 839
msdDimStylePropTextTextStyle = 827
msdDimStylePropTextTextStyleID = 828
msdDimStylePropTextUnderline = 822
msdDimStylePropTextVerticalMargin = 824
msdDimStylePropTextVerticalOpts = 836
msdD im Sty l ePropTextWeight = 825
msdDimStylePropTextWidth = 826
msdD imSt ylePropToleran ce Low e rValue = 901
msdDimStylePropToleranceMode = 902
msdDimStylePropToleranceSecAccuracy = 911
ms dDimSt yleProp Toleran ceShow = 903
msdDimStylePropToleranceStackEqual = 904
msdDimStylePropToleranceTextHorizontalMargin = 905
msdD i mStyleProp ToleranceTextScale = 906
msdDimSty l ePropToleranceTextVerticalMargin = 907
msdDimStylePropToleranceTextVerticalSeparation = 908
msdDimStylePropToleranceUpperValue = 909
msdDimStylePropValueAccuracy = 1001
msdDimStylePropVa lueA ltAc curacy = 1002
msdDimStylePropValueAltFormat = 1067
msdDimStylePropValueAltIsActive = 1003

IThe Enumeration List I


msdD i mSty l ePropValueAltSecAccuracy = 1004

msd Di mStylePropVa l ueAltSecFormat = 1069
msdDimStylePropValueAltSecIsActive = 1005
msdDimStylePropValueAltSecShowZeroMasterUnit = 1012
msdDimStylePropValueAltSecShowZeroSubUnit = 1081
msdDimStylePropValueA ltSec Thresh old = 1013
msdDimStylePropValueAltSecThresholdComparison = 1071
msdD i mStylePropValueAltShowZeroMasterUnit = 1020
msdDimStylePropValueAltShowZeroSubUnit = 1079
msdDimSty l ePropValueA l tThreshold = 1021
msdDimStylePropValueAltThresholdComparison = 1070
msdDimStylePropValueAngleForma t = 1023
msdD im Style Pr opVa lu eA ngl e Lea ding Zero = 1024
msdDimStylePropValueAngleMeasure = 1025
msdDimStylePropValueAnglePrecision = 1026
msdDimStylePropValueAngleTra i lingZero s = 1027
msdD i mStylePropValueDMSPrecis i onMode = 1082
msdD i mS t ylePropValueFormat = 1066
msdD im St yleP r opValueLabe l LineFormat = 1077
msdDimStylePropValueNoReduceAltFraction = 1043
msdDimStylePropValueNoReduceAltSecFraction = 1062
msdDimStylePropValueNoReduceFraction = 1042
msdDi mStylePropVa l ueNoReduceSecFraction = 1061
msdDimSty le PropVa lue NoReduceTolFraction = 1044
msdDimStylePropValueNoReduce TolSecFraction = 1063
msdDimStylePropVa l ueOrdDatumValue = 1057
msdDimStylePropValueOrdDecrementReverse = 1055
msdDimStylePropValueOrdFreeLocation = 1065
msdDimSty l ePropVa l ueOrdUseDatumValue = 1056
msdDimStylePropVa lueRou ndLSD = 1028
msdD imSty l ePropValueSecAccuracy = 1029
msdDimStylePropVa lueSecFormat = 1068
msdD imSty lePropVa lueSecShowTrailingZeros = 1033
msdDimStylePropVa l ueSecShowZeroMasterUnit = 1035
msdDimStylePropValueSecShowZeroSubUnit = 1080
msdDimStylePropValueSecUnitMaster = 1075


I Chapter 12: The MicroStation Object Model- En ums I

msdD i mS t ylePropVa l ueSecUn i tS ub = 1076
msdDimStylePropValueShowTrai l ingZeros = 1039
msdDimStylePropValueShowZeroMasterUnit = 1041
msdDimStylePropValueShowZeroSubUnit = 1078
msdDimStylePropValueSuperscriptLSD = 1045
msdDimStylePropValueThousandsOpts = 1072
msdDimStylePropValueUnit = 1048
msdDimStylePropValueUnitLabelMaster = 1049
msdDimStylePropValueUnitLabelSecMaster = 1050
msdDimStylePropValueUnit Labe l SecSub = 1051
msdDimStylePropValueUnitLabelSub = 1052
msdDimStylePropValueUnitMaster = 1073
msdDimStylePropVa l ueUnitSec = 105 3
msdDimSty l eProp Val ueUnitSub = 1074
msdD i mSty l e Pro pVa l ueUseWorkingUnits
MsdD i mSupe r Scr i ptModeFrom Font = 0
MsdDimSup e rScr i pt ModeGenerated = 1
msdD i mSymbo lTypeCel l = 2
msdD im Symbo l Type Def aul t = 0
msdDimTerminatorModeAuto = 0
msdDimTerm i natorModelnside = 2
msdDimTerminatorModeOutside = 3
msdDimTerminatorModeReversed = 1

IThe Enumeration List I


msdOimTerminatorTypeCirc l e
msdOimTerminator TypeOot = 4
msdOimTerminatorTypeNone = 0
msdOimTerminatorTypeNote = 5
msd OimT erminatorTypeOrig in
msdOimTe rm inatorTypeStroke
msdOimTextFieldMain = 0
msdOimTextFieldMinus = 2
msdOimTextFieldPlus = 1

MsdOi mTex tForm atMU = 0
MsdOimT ex tFor mat MU dash SU = 4
MsdO i mT ex tF orm atMU_L abel = 1
Ms dOimTex tFor matMU Lab el das h SU Label
Ms dOi mText FormatMU_L ab el _S U_ Label = 5
MsdOimTe xtFormat SU = 2
Ms dOi mTex tF ormatSU_ Label = 3
MsdOimTextFrameTypeBox = 1
MsdOimTextFrameTypeNone = 0

msdOimTextJustificationCenter = 2
msdOimTextJustificationLeft = 1
msdOimTextJustificationRight = 3
Msd Di mTextLocation
MsdOimTextLocationAbove = 1
MsdOimTextLocationInline = 0
MsdOimTextLocationTopLeft = 3


I Chapter 12: The MicroStation Object Model - Enums I

Msd Oi mTextOrientation
MsdDimTextOrientationA l igned = 0
MsdD i mTextOrientationHorizontal
MsdDimThousandsOptsComma = 2
MsdDimThousandsOptsNone = 0
MsdDimThousandsOptsSpace = 1
MsdDimToleranceTypeLimit = 1
msd DimTypeA ng leAx i s = 10
msdD imTypeAngleAx i sX
msdDimTypeAngleAxisY = 51
msdDimTypeAngleLines = 9
msdDimTypeAngleSize = 5
msdDimTypeArcS ize = 6
msdDimTypeCenter = 19
msdDimTypeC ustomLinear = 15
msdDimTypeDiameter = 12
msdDimTypeDiameterExtended = 18
msdDimTypeDiameterPara = 13
msdDimTypeDiameterPerp = 14
msdDimTypeLabelLine = 52
msdDimTypeLocateSingle = 3
msdDimTypeLocateStacked = 4
msdDimTypeNone = 0
msdDimTypeNote = 53
msdOi rnTypeOr'd i ria Le = 16
msdDimTypeRadius = 11
msdDimTypeSizeArrow = 1

IThe Enumeration List I


msdDimTypeSizeStroke = 2
msdDimTypeUseActive = -1
msdDimValueAngleFormatCentesimal = 2
msdD im ValueA ngleF orma tDe grees
msdDi mVal ueA ngl e For matRadi ans
msdDimVa l ueAnglePrecision2Place
msdDimVa l ueAnglePrecision5Place
msdDimValueAnglePrecisionWhole = 0
MsdDimVerticalTextOptionsAlways = 1
MsdDimVerticalTextOptionsNoFit = 2
msdDrawingModeErase = 1
msdDrawing ModeHi lit e = 2
msdD r awingModeNormal = 0
msdDrawingModeTemporary = 3
msdD r awingModeXor = 6

msdElementCachePurposeGraph i cal
msdElementCachePurposeNonModel = 1


I Chapter 12: The MicroStation Object Model - Enums I

ms dEl ement Cl assCon tru cti onRul e
msd El ementC l assOi mens i on = 3
msdElementClassLinearPatterned = 5
msdElementClassPatternComponent = 1
msd ElementClassPr im ary = 0
msd El eme ntC la ss Pri ma ryRu l e = 4
msdElementSubtypeApplicationElement = 20
ms dEl ement SubtypeA uxil iaryCoo r dinateSyste m
msdElementS ubtypeNone = - 1
msd ElementSubtype UpdateSeque nceE l ement
Msd ElementType
msdElementType44 = 44
msdE l ementTypeArc = 16
msdElementTypeBsplineCurve = 27
msdElementTypeBsplineKnot = 26
msdElementTypeBsp lin ePole = 21
msdElementTypeBsplineSurface = 24
msdElementTypeBsplineWeight = 28
msdElementTypeCellHeader = 2
msd El em entTyp eC ellLibraryHeader = 1
msdE l ementTypeComplexShape = 14
msdE l eme ntTypeCo mplexString = 12
ms dEl ementTypeC one = 23
msdE l ement TypeCon i c = 13
msdEleme ntTypeCurve = 11
msdE l ementType OesignFi l eHeader = 9
msd Eleme ntTypeOgnSto r eComponent = 38
msd Element Type OgnSto r eHeader = 39
msdElement Type Di gSetData = 8
msdElement TypeDimension = 33
msdElementTypeE l lipse = 15
msdElementTypeLevelMask = 99

IThe Enumeration List I

msdE l ementTy pe LevelSymbo l ogy
msdElem entTy peLine = 3
msdElement TypeLineString = 4
msdElementTypeMatrixDoubleData = 103
msdElementTypeMatrixHeader = 101
msdElementTypeMatr ixlnte gerData = 102
msdEle me nt Type Mes hHea der = 105
msdElementTypeMicroStation = 66
msdElementTypeMultiLine = 36
msdElement Ty peNamedGroupComponent = 111
msdElementTypeNamedGroupHeader = 110
msdElementTypePo int String = 22
msdEle me ntTypeRasterComponent = 88
msdElementTypeRasterFrame = 94
msdElementTypeRasterHeader = 87
msdElementTypeRasterReferen ce = 90
msdElementTypeReferenceAt tac hment = 100
msdElementTypeShape = 6
msdElementTypeS ha redCe l l = 35
msdElementTypeSolid = 19
msdE l ementTypeSurface = 18
msdElementTypeTable = 96
msd ElementTypeTableEntry
msdElementTypeTag = 37
msdE l ementTypeText = 17
msdElementTypeTextNode = 7
msdElementTypeView = 98
msdElementTypeViewGroup = 97
msdAccuDrawNotEnabled = -2147218287
msdError3dReference2dMaster = -2147220767
msdErrorAccessVio la t i on = -2147218313




I Chapter 12: The MicroStation Object Model - Enums I

msdE r ro rAcs Not Fou nd = -2 14722 0744
msd Er r or AcsRep l aced = - 2147220745
msd Erro r AddressNotK nown = -2147220784
ms dErr or Addr es sN otValid = - 2147220779
msdE r rorAlreadyExi s ts = - 2147218310
msdErrorAlreadylnUse = -2147220804
msdErrorAlreadyOpen = -2147218312
msdErrorBadBSplineElement = -2147217996
msdErrorBadCharacterConstant = -2147220794
msdErrorBadConti nui ty = -2147217986
msdErrorBadElement = -2 147218399
msdErrorBadFi l e = -2147218304
msdErrorBadFloat = -2147220796
msdErrorBadFormat = -2147218309
msdErrorBadHexNumber = -2147220799
msdErrorBadlndex = -2147218370
msdErrorBadKnots = -2147217991
msdErrorBadLineWeights = -2147217990
msdErrorBadModelld = -2147218334
msdErrorBadModelReference = -2147218397
msdErrorBadName = -2147218316
msdErrorBadNumber = -2147220800
msdErro r BadOctal = - 2147 220797
msdErrorBadOrder = -2147217994
msdErrorBadParameter = -2147217995
msd Er ro r BadPer i od i city = -2 147217993
msdErrorBadPoles = -2147217992
msdErrorBadRasterFormat = -21472 18350
msdErrorBadReso urceType = -2147220772
msdErrorBadScanList = -2147218389
msdErrorBadSp i ra l Oef i nition = -2 147217989
msdErrorBadStr in g = -2147220795
msdErrorBadType = -2147220803
msdErrorBadVersion = -21472 18308
msdErrorBadWordsToFollow = -21472 18311
msdErrorCachelnUse = -2147218318

IThe Enumeration List I

msdErrorCach e LoadError = -2 147218291
msdErrorCacheNotEnabled = -2147218320
msdErrorCacheNot Filled = -2147218288
msdErrorCacheNotFound = -2147218317
msdErrorCannotCreateFile = -2 147218329
msdErrorCannotOereference = -2147220787
msdErrorCannotlmportSeed = -2147218292
msdErrorCan notOpenSeed = -2147218303
msdE r ro r Ca nnotSaveFi l e = -2147218328
msdErrorCellExists = -2147218372
msdErrorCellLibraryIs2d = -2 14 7218365
msdErrorCellNotFound = -2147218373
msdErrorCellTooLarge = -2147218369
msdErrorCircularOependency = -2147219604
msdErrorCommandReceived = -2147220704
msdErrorComplexHeaderRequired = -21472 18387
msdErrorCompressionError = -2147218296
msdErrorCopyError = -2147218289
msdErrorOiskFull = -2147218395
msdErrorOivideByZero = -2147220780
msdErrorOuplicateLogical = -2147220766
msdErrorOuplicateTaskld = -2147218353
msdErrorElementFilled = -2147220756
msdErrorElementFrozen = -2147218359
msdErrorE l ementNotFilled = -2147220755
msdErrorElementNot Fou nd = -2 147218323
msdErrorE l ementNotP l anar = -2147220753
msdErrorElementTooLarge = -2147220754
msdErrorEndOfFile = -2147218390
msdErrorException = -2147219504
msdErrorFileExists = -2147218326
msdErrorF i l eNotFound = -2147218338
msdErrorHasChanges = -21472 18298
msdErrorldExists = -2147218321
msdErrorldNotF ound = -2147218322



I Chapter 12: The MicroStation Object Model - Enums I

msdE rro rIl leg alC ha r ac t e r = -2 14722 0793
msdErr or l ns uff ic i ent lnf or mat i on = -2 14721840 1
msdErrorlnsufficientMemory = -2147218388
msdErrorlntegralNeeded = -2147220777
msdErrorlnvalidACSType = -2147220770
msdErrorlnvalidButton = -2 147220769
msdErrorlnvalidCell = -2147218371
msdErrorlnvalidClip = -2147220761
msdErrorlnvalidForFloat = -2147220781
msdErro r l nvalid For Func ti on = -2147220771
msdError l nvalidForStructure = -2 147220782
msdErrorlnva li dForType = -2147220778
msdErrorlnvalidLibrary = -2147218368
msdError l nva l idMaterOrigin = -2147220763
msdErrorlnvalidOperationForNested = -2147218302
msdErrorlnvalidOperationForNonNested = -2147218301
msdErrorlnvalidPatternSpace = -2147220760
msdErrorlnvalidReference = -2147220762
msdErrorlnvalidReferenceOrigin = -2147220764
msdErrorlnvalidSymbol = -2147220789
msdErrorLinkageNotFound = -2147218344
msdErrorLoadinglnterface = -2147218297
msdErrorModelerNotLoaded = -2147219 703
msdErrorModel IdExi sts = -2147218332
msdErrorModelNameExists = -2147218333
msd ErrorModelNotEmpty
-2 147218342
msd Error Mod i fyComplex
msd ErrorNameNotU nique
msdErrorNa meToo Long = -2147218335
msdErrorNeedExponent = -2147220798
msdError Needlnteger = -2147220802
msdErrorNoAcsOefined = -2 147220746
msdErrorNoBounds = -2147218001
msdErrorNoBSplineHeader = -2147217999
msdErrorNoCel l Li brary = -2147218375
msdErrorNoClipVolume = -2147218336

IThe Enumeration List I

msdE rr or NoFence = -21472 18337
msdEr rorNoGraphicGroup = -2147220742
msd Er ror NoKno t s = -2 14 7218003
msdErrorNoLevelMask = -2147220740
msdErrorNoLineWeights = -2147218002
msd Er rorNoMatch = -214721838 1
msdEr ror No Mode l = -2 14 721 8299
msd Err orNoModelln f ormation = - 214721 8331
msdErro r NonClosedElement = -2147220757
msdErrorNonC l osedPatternElement = -214 7220759
msdErrorNonCoplanarShapes = -2147220750
msdErrorNonSolidPatternElement = -2147220758
msdEr ror NoN umberB ounds = -2147218000
msdEr r orNoOffsetlntersect i on = -2147217987
msdErrorNoParentModel = -2147218330
msdErrorNoPoles = -2147218004
msdErrorNoReferenceS l ots = -2 14722074 7
msdErrorNoselection Se t = -2147220748
msdError NoSuchModel = -2147218294
msdErrorNoSymbol = -214722079 1
msdErrorNotDesignFIle = -21 47 220768
msdErrorNotDirectAttac hment = -2147220739
msdErrorNotFunction = -2147220775
-2 147218293
- 2147220785
msdEr r orN otOpen = -2 14 7218315
msdErrorNotSingleView = -2147220765
msdErrorNotStructure = -2147220786
msdErrorNotSupported = -2147218348
msdErrorNotVa lidE xpression = -2147220776
msdErrorNullSolution = -2147220752
msdErrorOldMaterialTable = -2147220749
msdErrorOperationCanceled = -2147218306
msdErrorParasolidError = -2147219703
msdErrorReadOnly = -2147218396



I Chapter 12: The Micro Station Object Mode l - Enu ms I

msdErrorRecurse Limit = -2 147217985
msdErrorRenameError = -2 147218290
msdErrorRequires3dFi l e = -21472 18400
msd Er r orR eso urceNot Foun d = -21472 18376
msdErrorS haringViola t io n = -214721831 4
msdErrorStructureNeeded = -2147220801
msdErrorSymbolNotResolved = -2147219704
msdErrorSyntaxError = -2147220790
msdErrorSystemError = -2147218363
msdErrorTagBadAssoc i at i on = -2 147220096
msdErrorTagBadReportFile = -2 147220098
msdErrorTagBadReportKeyword = -2147220097
msdErrorTa gN am eTo oLong = - 214 722009 2
msdE r rorTagNo Ta r get
msdErrorTagNo t Found = -2 147220093
msdError TagN otlnSet = - 2147220 101
msdError TagPrevious l yDefined = -2147220094
msdErrorTagSe t Na meLong = -2147220103
msdError TagSetN otFound = -2147220102
msdErrorTagSetPrev i ously Def i ned = -2147220100
msd ErrorTagSetTooB i g = -2147220099
msdError TagU ndefinedType = -2 147220104
msdEr r orTimeout = -214 7218 362
msdErro rTooComplex = -2147220783
msdErrorTooFewArguments = -2147220773
msdErrorTooFewPoles = -2147217998
msdErrorTooManyArguments = -2147220774
msdErrorTooManyKnots = -2 147217988
msdErrorTooManyOpenFiles = -2147218307
msdErrorT oo Man yPo les = -2147217997
msdErrorTooManySurfaceE lements = -2147218346
msdError Typeslncompatible = -2147220788
msdErrorUnboundedSolution = -2147220751
msdErrorUnknownError = -2147218305
msdErrorUnknownFormat = -2147218295
msdErrorUnsupported = -2147220792

IThe Enumeration Li st I


ms dErr orU se r Ca nc el edAc t i on = - 214 7218382

msd Err orV7Ce ll Libr ary = - 21472 18327
msdErrorViewGroupNotFound = -2147220741
msdErrorViewNotDisplayed = -2147218374
msdErrorViewNotFound = -2147220743
msdErrorWriteFailed = -2147218393
msdErrorWrongElementID = -2 147218319
msdFileAccessMo deRead = 1
msdFileAccessModeReadWr i te

msdFil lMod eFi lled = 1
msdF illModeNot Fi lled = 0
msdFi l lModeOutl in ed = 2
msdFillModeU s eActive = -1
msd Fo nt TypeS HX = 1
msdFontTypeUnknown = 3
msdFont TypeWindow sTru eType

msdGeoReferenceS i sterFileTypeHgr = 1
msdGeoReferenceSisterFileTypeNone = 0
msdGeoReferenceS i ster FileTypeTwf = 2
MsdGloba lLineStyleScale
msdGloba lL ineStyleScaleBoth
msdGloba l LineStyleScaleMaster
msdGloba l LineStyleScaleNone = 1
msdG l obalL i neSty l eScaleReference



I Chapter 12: The MicroStation Object Model - En ums I

msd LevelChangeA f terCreate = 2
msdLeve lChange Aft er Delet e = 3
msdLevelChangeBeforeDelete = 18
ms dLevelChangeChangeCode = 5
msdLevelChangeChangeDisplay = 7
msdLevelChangeChangeName = 4
msdLevelChangeChangeParent = 6
msdLevelChangeTableUndo = 14
msdLevelElementAccessAll = 0
msdLevelEl ementAccess Loc ked = 1
msdLimitsMaxVi ews = 8


msdMeasurementBaseDegree = 2
msdMeasurementBaseMeter = 1
msdMeasurementBaseNone = 0
msdMeasurementSystemEnglish = 1
msdMeasurementSystemMetric = 2
MsdMem berTraverse Type
msdMemberTraverseCopy = 2
msdMemberTraverseEnumerate = 3
msdMemberTraverseManipulate = 1
msdMember TraverseSimple = 0


I The Enumeration List I


msdMessageCen t erPr i or i tyDebug = 13
msdMessageCenterPriorityError = 10
msdMessageCenterPrioritylnfo = 12
msdMessageCenterPr i orityNone = 14
ms dMessageCe nte r Pr i orit yWa rn i ng
md lM ode l ChangeActive = 5
mdlModelChangeBeforeAct i ve
md l Mode l ChangeBeforeDelete
md l Mode l ChangeBeforeName = 12
mdlModelChangeBeforeProperties = 14
mdlModelChangeCreate = 1
mdlModelChangeDelete = 2
mdlModelChangeName = 10
md lM odelC hange Un Crea t e
mdlModelChangeU nDelete
msdMode lTypeDefa ul t = -1
msd Mode lTypeNormal = 0
msdModelTypeSheet = 1
MsdN estOverrides
msdNestOverridesA l ways = 1
msdNestOverridesNever = 2



I Chapter 12: The MicroStation Object Model - Enums I

Msd NewLevel Display
msdNewLevelDisplayAlways = 1
msdNewLevelD i sp la yNever = 2

Msd RasterBlockType
msdRasterBlockTyp e lmage = 4
msdRast e rBl ockTypeLin e = 1
msdRasterBlockTypeStr i p = 3
msdRasterBlockTypeTile = 2
msdRasterDisplayOrderCommandBackward = 3
msdRasterDisplayOrderCommandForward = 2
msdRasterDisplayOrderCommandToBack = 1
msdRasterDisplayOrderCommandToFront = 0
msdRasterD isp layPriorityPlaneBack = 1
msdRasterDisp l ayPriorityPlaneFront = 3
msdRasterDisplayPriorityPlaneVector = 2
msdRasterModificationType_ClipBoundary = 5
msdRasterModificationType_ClipMask = 4
msdRasterModificationType_Extendedlnformation = 0
msdRasterModificationType_GeoReferenceInformation = 1
msdRasterModificationType_Raster ln formation = 3
msdRasterModificationType_Re l oad = 6
Msd RasterWorld File
msdRasterWorldFileHgr = 1
msdRasterWorldFileNone = 0
msdRasterWor l dFi l eWorldFile

IThe En umeration List I

msdReferenceSys t emRaster


msdRenderingModeConstantShade = 5
msdRenderingModeCrossSection = 1
msdRe nder i ng Mode Hi dde nLine = 3
msdR enderingModePar t icle Trace = 11
msdRenderingModePhong = 7
ms dRender i ng ModeRadiosity = 10
msdRenderingModeRayTrace = 8
msdRenderingModeRe nderWireFrame
msdRender i ngModeSo li dFil l = 4
msdRenderingModeWireFrame = 0
msdRenderingModeWireMesh = 2
msdStandard s CheckerReplaceCh oiceAbo rt = 4
msd Stan dard s Ch eckerReplaceChoiceFi x = 1
msdStandardsCheckerRepla ceChoiceMarkIgn ored = 2
msdStandardsCheckerRep l ac eChoiceSkip = 0
msdStandardsCheckerReplaceOptionCanFix = 2
msdStatusBarAreaMiddle = 15
msdTagTypeBinary = 5
msdTagTypeCharacter = 1
msdTagTypeDouble = 4
msdTagTypeShortlnteger = 2


I Chapter 12: The MicroStation Object Model - Enums I

msdTangen t Arcs = 1
msdTangentCircles = 0
msdTange ntTr i angles
msdTangentFromCircleFit = 1
msdTangent Fr om Cu bi c Fit = 2
msd TangentFromCurve = 0
msdTextDirect ionHori zontal = 0
msdTextDirectionRightToLeft = 8
msdTextDirectionVertical = 4

msdTextJustificationCenterTop = 6
msdTextJustificationLeftBottom = 2
msdTextJustificationLeftCenter = 1
msdTextJustificationLeftTop = 0
msdTextJustificationRightBottom = 14
msdTextJustificationRightCenter = 13
msdTextJustificationRightTop = 12
msdTextNodeLineSpacingTypeExact = 0
msdTextNodeLineSpacingTy peExactFr omLineTop
MsdV7 Action
msdV7ActionAskUser = 0
msdV7ActionWorkmode = 3

I Review I


msd Vi ew1
ms dView All = 25 5
msdV i ew None = 0
msd XDatumTypeBinaryData = 1004
msdXDatumTypeControlString = 100 2
msdX Datu mTypeData ba se Ha nd l e = 1005
msdX DatumTypeD i stance = 1041
10 71
msdXDatumTypeReal = 1040
msdX DatumTypeString = 1000
msdXDatum TypeUns upported = 0
msdX Datum TypeWor ldD irect i on = 1013
ms dXD atumT ypeW orldS pace Di s plac ement = 1012
msdXD atumTypeWorld SpacePos i t i on = 1011

As we continue through this book, we will see examples of using
enumerations in the code samples.
As we pointed out in the objects chapter, the Object Browser is useful in
finding and determining how to use enumerations.


I Chapter 12: The MicroStation Object Model - Enums I


The MicroStation Object

Model - Types
Thus far we have introduced and discussed concepts such as variables,
objects, properties, and methods. We are now going to discuss types.
A type is used like a variable but is similar to an object because it holds
multiple elements. The best way to demonstrate this is by looking at a
type we will use extensively in our MicroStation VBA programming.
Type Po in t3d
X As Double
Y As Double
Z As Double
End Type

The Point3d type has three members: X (which is a Double), Y (which is

a Double) and Z (which is a Double).
Sub TestPoint3d ()
Di m StartPo i nt As Po i nt 3d
Dim EndPoint As Po i nt3d
Dim My Line As LineElement
StartPoint . X 1. 5
StartPoint . Y = 2. 5



I Chapter 13: The MicroStation Object Model- Types I

Star t Poi nt.Z = 3 . 5
End Po int. X 4
EndPoint.Y = 0
EndPoint.Z = 0
Set MyLine = CreateLineElement2(Nothing. StartPo i nt. EndPoint)
ActiveModelReference.AddElement MyLine
End Sub

We declare two variables with a type of "Point3d". We assign coordinate

values to the X, Y, and Z elements of these variables. They are then used
with the CreateLineElement2 method. Here is the declaration for
"CreateLineElement2" :
Sub CreateLineElement2(Template As Element . StartPoint As Point3d. _
En dPo i nt As Po i nt3d) as Lin eEleme nt

Notice how this method is asking for two Point3d Types Start Point and the other for the End Point.

one for the

Here is a list of the types we have available to us in MicroStation VBA:

Type MsdACSType
Just if icat ion As MsdDataEntryRegionJustificat i on
Length As Long
StartPosition As Long
End Type
Type Msd ACS Type
High As Long
Low As Long
End Type
Type MsdAddAttac hmentFlags
Center As Po i nt3d
St art As Double
Sweep As Double
VectorO As Point3d
Vector90 As Po i nt3d
End Ty pe
Type MsdAng l eAccuracy
Du As Point3d
Dv As Point3d
End Type

I The MicroStation Object Model - Types I

Type MsdAngleAccuracy
RowX As Point3d
RowY As Po~ nt3d
RowZ As Poi nt3e
End Type
Type MsdAngleAccuracy
Base As MsdMeasurementBase
Label As Stri ng
System As MsdMeasurementSystem
UnitsPerBaseDenominator As Double
UnitsPerBaseNumerator As Doub l e
End Type
Type MsdAngleFormat
Details As String
Msg As Str i ng
Priority As MsdMessageCenterPriority
End Type
Type MsdAngleMode
Norma 1 As Poi nt3d
Origin As Point3d
End Type
Type MsdAttachMode
X As Double
Y As Double
End Type
Type MsdBsplineCurveOffsetCuspType
X As Double
Y As Double
Z As Double
End Type
Type MsdBsplineCurveOffsetCuspType
High As Point3d
Low As Poi nt3d
End Type



I Chapter 13: The Mi croStatio n Object Mode l - Types I

Type MsdBspl i neCurve Type
Direction As Point3d
Origin As Point3d
End Type
Type MsdBsplineCurveType
Duu As Point3d
Duv As Point3d
Dvu As Po i nt3d
Dvv As Poi nt3d
End Type
Typ e Msd Bsp li neC ur veTyp e
EndPo i nt As Point3d
St artPo i nt As Po i nt3d
End Type
Type Ms dBs pl ineParametrizationType
RowX As Point3d
RowY As Poi nt3d
RowZ As Po i nt3d
TranslationX As Double
Trans l at i onY As Doub l e
Tr ans l ati onZ As Double
End Type
Type MsdBsplineSurfaceType
X As Doub l e
Y As Dou bl e
Z As Doub l e
End Type
Type MsdBsplineSurfaceType
Type As MsdXDatumType
Va l ue As Varian t
End Type

Each of these types is available to us when we are using MicroStation

VBA. The "Type", "End Type" declaration as shown is a standard VBA
convention. As a matter of fact, we can create our own "Types" inside
VBA. Custom Types are declared in the General Declarations area of a

I Review I


Code Module. For example, if we want a new type named "Point4d", we

would use the code:
Type Po'nt4d
X As Double
Y As Double
Z As Double
A As Double
End Type

If this declaration is made, we can declare variables as follows:

Dim MyPoi nt As Point4d

As with enumerations, types will be used extensively as we continue

working with the MicroStation VBA.

Types are similar to objects. An object has properties. A type has
members which are similar to properties. One of the most common
types we use in MicroStation is the Point3d type. It has members of X, Y,
and Z. Each of these members are declared as Doubles.


I Chapter 13: The MicroStation Object Model- Types I


The MicroStation Object

Model - Events
Objects, as we have discussed, have properties, methods, and events. We
introduced events when we discussed creating a Visual Interface. When
a user clicks on a CommandButton, the click event of the
CommandButton is triggered. MicroStation events are triggered as the
user interacts with various aspects of MicroStation.
When a company (such as Bentley) embeds VBA into their application
(in this case, MicroStation), the question of how to deal with events is
raised. Here is how Microsoft Excel deals with events:

End S ub

Private Sub hlo rksheet_SelectionChange (ByVa l Target As Range)

E n d Sttb

Each worksheet in an Excel workbook has events automatically exposed.

Two of them are the Change and the SelectionChange events. These



I Chapter 14: The MicroStation Object Model - Events I

events are triggered as a worksheet's cell value changes and when the
user moves from one cell to another.
There are two ways we can capture and make use of MicroStation
events. One is to declare a variable in a class module or a form as an
application and using the "WithEvents" keyword. This exposes two
events: OnDesignFileOpened and OnDesignFileClosed. The majority of
MicroStation events are accessed through the use of interfaces.
MicroStation has exposed much more than simple events through the
use of interfaces, which are discussed in detail in Chapters 22
through 26.


Here is a small example of how the OnDes i gnFileOpened and
On Des i gn Fi 1eC los ed events work.
We will use a UserForm that is shown modeless. This means the user
can still interact with MicroStation even though the form is displayed.
When the form is initialized, we set the MicroStation application object
to a variable that has been declared "WithEvents" in the General
Declarations area of the UserForm. When we declare a variable
"WithEvents", the events belonging to the object we specify are available
to our code.
Here's the program as it is running after a couple of files have been
opened (the previous file closes when the new file is opened).

File Opened

....C:.)M!q.9.~~<!~!Q[\.Y.~A?)f!!~.!.'.9.gD. .........................
C:\Microstation VBA2\Mathcad Model.dgn

File Closed
C:\Documents and Settings\AII Users\Application Data\Bentley\WorkSpace\Projects\Examples\E
C:\Microstation VBA2\file l .dgn

I OnDesignFileOpened and OnDesignFileClosed I


Each time a file is opened or closed, the associated event is triggered. We

will begin by looking at the code in the code area of the UserForm.
Dim WithEvents MyApp As MicroStationDGN.Application

Private Sub UserForm_Initialize()

Set MyApp


End Sub

Private Sub MyApp_OnDesignFileOpened(ByVal _

DesignFileName As String)
lstOpened . Addltem DesignFileName
End Sub

Private Sub MyApp_OnDesignFileClosed(ByVa l _

Des i gnFileName As String)
lstClosed . Addltem DesignFileName
En d Sub

Each time the OnDe sig nF ile Ope ned event is triggered, we add the
DesignFileName parameter to the IstOpened ListBox. When a file is
closed, it is added to the IstClosed ListBox.
We want to display this fo rm as modeless, so we will display it by
running the next macro:
Sub ShowEvents ( )
frmEvents . Show vbModeless
End Sub

The Procedure ShowEvents is placed inside a code module.

We can use the OnDesignF i leOpened and OnDesignF i leClosed events to
log which files have been opened. We are given the file name as a
parameter in the event. This basic functionality could be expanded to
include capturing the current Date/Time (with the Now Function) as well
as the current User (with the App 1i cat ion . UserName property).


I Chapter 14: The MicroStation Object Model - Events I

Events are triggered as users interact with software. MicroStation events
are primarily exposed through the use of interfaces (covered later). The
OnDesignFileOpened and OnDesignFileClosed events can be exposed
by declaring the MicroStation.Application object "WithEvents" in a
Class Module or UserForm. More information on the use of
"WithEvents" can be found in the standard VBA help file.


Adding To Documents
We have created lines, circles, arcs, and text as we introduced
programming topics. Let's examine the specifics of adding elements and
other objects to our design files. We begin with graphical elements and
then work on non-graphical elements such as levels.
In this chapter:

Graphical Elements

Creating New Documents

Security Issues with Creating Data

There are two steps to adding elements to our design files. First we
create the element in memory. Then we add the element to our design
file. As you will see, there is often more than one way to create the
element. We will demonstrate multiple examples of each creation

"The shortest distance between two points is a straight line:' If this is
true, we should be able to create a line by providing two points, right?



I Chapter 15: Adding To Documents I

Well, that is one way to create a line. We can also provide an array of
vertices if we want to draw more than one line.

Function CreateLineE l ementl(Template As Element,

Vertices() As Point3d) As LineElement
Function CreateLineElement2(Template As El ement,
StartPoint As Point3d, EndPoint As Point3d) As

Sub TestCreateLineA ()
Dim StPt As Point3d
Dim EnPt As Po i nt3d
Dim myLine As LineElement
EnPt.X = 4: EnPt.Y = 6: EnPt.Z = 8
Set myLine = CreateLineE l ement2(Nothing, StPt , EnPt)
ActiveModelReference . AddE l ement myLine
End Sub

TestCreateLineA uses the Create LineElement2 method to create a new

line element. It does so using a start point and an end point.
Sub TestCreateLineB()
Dim StPt As Point3d
Dim EnPt As Point3d
Dim myLine As LineElement
'Line 1
Stpt.X = 0: StPt.Y = 0: Stpt.Z = 0
En Pt.X = 4: EnPt.Y = 0: EnPt.Z = 0
Set myLine = CreateLineElement2(Nothing, StPt, EnPt)
ActiveModelReference.AddElement myLine
'Line 2
StPt.X = 4 : StPt.Y = 0 : Stpt.Z = 0
EnPt.X = 4: EnPt.Y = 4 : EnPt.Z = 0
Set myLine = CreateLineElement2(Nothing, StPt , EnPt)
ActiveModelReference . AddElement myLine
' Line 3
Stpt.X = 4: StPt . Y = 4 : StPt.Z = 0
EnPt.X = 0: EnPt . Y = 4 : EnPt.Z = 0
Set myLine = CreateLine[lement2(Nothing, StPt, [nPt)
ActiveModelReference.AddElement myLine
' Line 4
Stpt .X
0: Stpt.Y
4: Stpt.Z
EnPt.X = 0: EnPt.Y
0: EnPt . Z 0

I Graphical Elements I


Set myLine = CreateLineE l ement2(Nothing , StPt, EnPt)

Ac tiveM odelReference . Add Ele ment myLine
End Sub

TestCreateL i neB creates and adds four lines by using the

CreateL i neEl ement2 function. As we look at the coordinates used to
create the lines we will recognize that we are drawing a square. Let's
create the same square by using the CreateL i neEl ementl Method.
Sub TestCreateLineC ()
Dim LinePoints(O To 4 ) As Poi nt3d
Dim myLine As LineElement
0: LinePoints(O).Y
LinePoints(l) . X 4 : LinePoin ts(l).Y
4: LinePo i nts(2) . Y 4
LinePoints(3) . X 0: LinePo i nts(3) . Y 4
Li ne Poi nts(4).X
0: LinePo i nts(4).Y
Set my Line = CreateLineElement1(Nothing , LinePoints)
Act i veModelReference.AddElement myLine
End Sub

As we can see in Test CreateL i neC, we can supply an array of Point3d

types and use a single Cr eateL i neEl ementl Method to create four lines.

NOTE: When we declare a numeric variable, a value of zero (0) is

automatically assigned to the variable. Knowing this, we can leave
the .Z element of each point alone and it will be assigned a value of
zero by default. We could have left out th e .X and . Y elements that
were to be assigned values of zero as well, but keeping them in makes
the code much easier to read. Also, note that we are putting two lines
of code on the same line. We can do this by using the colon (:) symbol.
This keeps our vertices on the same line of code and can make it easier
to read the code.

In our next example, we are going to create a procedure that allows us to

specify x, y, z elements for the creation of 3d Lines. We want to be able to
provide any number of x, y, z sets of elements so we will use the
ParamArray keyword in our parameter declaration.
Sub Create3dLines (ParamArray PointElems() As Variant)
If (UBound(PointElems) + 1 ) Mod 3 <> 0 Then
MsgBox " Invalid number of point elements. " , vbCritical
Exit Sub


I Chapter 15: Adding To Documents I

End I f
If UBound(PointElems) + 1 < 5 Th en
MsgBox "A mirimum of 2 X,


Z poirts must be prov i ded. ", vbCr 'ti ca '

Exit Sub
End If
Dim LinePoirts() As Point3d
ReD i m LinePo i nts(O To (UBound(Po i nt El ems) + 1) \ 3) As Po i nt3d
Dim I As Long
Dim Po i ntCount e r As Long
Dim myLine As LineE l ement
For I = LBound( Poi ntEl ems) To UBound(Po i ntE l ems) Ste p 3
Li nePoints( PointCoun t e r ) . X Point Elems(I)
Li nePoi nts (Poi ntCounter). Y = Po i ntEl ems (I + 1)
Line Poi nts ( PointCo unter ) . Z = PointElems ( I + 2)
PointCounter = PointCounter + 1
Next I
Se t myLi ne = CreateLineElement1(Nothing, LinePoints)
Act i veModelReference . AddE l emen t my Li ne
En d Sub

This procedure is straightforward but a little more complicated than

those we have worked with in the past. Let's start at the top of the
procedure and work our way down to the end.

The procedure is named Create3dLines and a single ParamArray

parameter is declared.
NOTE: Only one ParamArray parameter can be declared in a function
or procedure and it must be the last parameter.

3D Points are comprised of x, y, and z elements. Because of this, we

need to make sure that we have been given the PointElems array in
groups of 3. If the upper-bound value of the parameter is 4, this
means 5 elements have been provided. This is a problem because 5
elements do not produce two complete 3-D points.

3 If we pass the "group of 3 elements" test, we need to see if we have

been provided at least two points. After all, we cannot create a line
from one point. This can be done a number of different ways. One
way is to look at the upper-bound (UBound Function) of the
PointElems array. If it is less than 5, we know we don't have enough
elements in the array. If it is equal to 5 we know we have two
complete 3d Point elements.

I Graphical Elements I


Since the number of points may be different each time this

procedure is used, we need to create a dynamic array of points. We
then set the number of points in the array based on the number of
PointElems provided.

Now we need to populate the X, Y, and Z components of the points

based on the elements provided in the ParamArray.

6 We use CreateL i neEl ementl, using the points created from the

We add the line to the ActiveModelReference.

Since this procedure utilizes parameters, it cannot be run by itself. Here

is a test procedure to run our "Create3dLines" procedure.
Sub TestCreate3dLines ( )

Create3d Lines
Create3d Li nes
End Sub




4 , 0 , 0 , 4 , 4, 0 , 0 , 4, 0 , 0, 0,
4, 4 ,
4, 0,

Our test procedure, TestCreate3d Li nes , calls our newly created

procedure Crea te3 dLin es five times. In the first instance, a square is
created from (0,0,0) to (4,0,0) to (4,4,0) to (0,4,0) and back to (0,0,0).
The next one draws a line from (0,0,0) to (4,4,0). Next we draw a line
from (0,4,0) to (4,0,0). The next two lines are put in to test our
ParamArray validation code. We are unable to draw a line from (0,4,0)
to (4,0) because the second point is only given two elements (x and y)
and we are requiring three elements per point. The last one attempts to
draw a line from (0,4,0) to ... to nothing. We cannot draw a line with
only one point. Here are the two message boxes in the order in which
they appear.

Invalid number of point elements.

A minimum of 2 X, Y, Z points must be provided.

Once a line is created, we can make changes to its properties such as its
color, level, or linestyle properties.


I Chapter 15: Adding To Documents I


In MicroStation's Color
Table dialog box, if we
scroll over the colors in
the table we see the
color number and the
RGB values for each
color. In the graphic
shown we can see that
color number 3 has an
RGB value of (255, 0,

Let's draw a couple of lines and change their color to red (255, 0, 0).
Sub TestCreateLineD ()
Dim LinePo i nts(O To 1) As Point3d
Di m myL i ne As LineElement
Li nePoints(O) . X = 0: Li nePo ints(O ).Y = 0
LinePoints(l).X = 4: LinePoints(l).Y = 4
Set myLine = Create Li neElement1(Nothing, LinePoints)
myLine.Color = 3
ActiveModelReference . AddElement myL i ne
LinePoints(O).X = 0: LinePoints(O).Y = 4
LinePoints(l).X = 4 : LinePoints(l).Y = 0
Set myL ine = CreateLineElement1 ( Nothing, LinePoints )
myL i ne . Color = 3
ActiveModelReference.AddElement myLine
End Sub

Two lines are created with their color properties changed to color
number 3 (red).
Here is another way we could accomplish the same task:
Sub TestCreateLineE ()
Dim LinePoints(O To 1) As Point3d
Dim myLine As LineElemcnt
Dim myLine2 As LineElement
LinePoints(O) . X = 0 : LinePoints(O).Y = 0
LinePoints(l ) . X = 4 : LinePoints(l).Y = 4
Set myLine = CreateLineElement1(Nothing, LinePoints)

I Graphical Elements I


myLine . Color = 3
Act iveM odelReference.Add Element myLine
LinePoints (O).X = 0 : LinePoints(O).Y = 4
LirePoi~ts(l).X = 4: LinePoints(l).Y = 0
Set myLine2 = CreateLineElement1(myLine. LinePoints)
ActiveModelReference . AddElement myL i ne2
End Sub

In this example, we added one line of code, removed one line of code,
and made a slight change to another line. Here is the line we changed:
Set myLine2

CreateLineElement1(myLine. LinePoints)

In the previous work we did with Create Li neEl ementl , we supplied a

value of "Nothing" in the template parameter. In this example, we
provided the variable of the first line we created. This results in the
creation of a new line with the same non-geometric properties as the
'Template' element.

Creating Shapes
A shape is a series of lines that are joined together into one element.
Here is the declaration for CreateShapeElementl:

Functi on Cre at eShape El ementl( Template As El ement .

Vert i ces( ) As Poi nt 3d . [ Fil l Mode As MsdFi ll Mode
msd Fil lM odeUseAct iv e J ) As Shap e El ement

Here is a procedure that creates a triangle.

Sub TestCreateShapeA ()
Di m myShape As ShapeElement
Dim Shape Po ints(O To 2) As Point3d
ShapePoints(O) . X 0 : ShapePoints(O) . Y 0
ShapePoints(l).X = 2: ShapePoints(l) . Y 0
ShapePoints(2).X = 1: ShapePoints(2).Y
Set myShape = CreateShapeElement1 ( Nothing. ShapePoints )
ActiveModelReference . AddElement myShape
End Sub

When this code is run, a triangle is created and added to the

ActiveModelReference. Notice that we do not need to close the triangle
by providing a fourth point at (0, 0, 0). Shapes are always closed.


I Chapter 15: Adding To Documents I

A comparison of the declaration and the use of CreateShapeEl ementl
reveals that we did not use the optional FillMode parameter. By default,
the FillMode parameter uses the active setting in MicroStation. Let's
copy and paste TestCreateShapeA, rename the new procedure to
TestCreateShapeB and supply a FillMode parameter:
Sub TestCreateShapeB( )
Dim myShape As ShapeElement
Dim ShapePoints(O To 2) As Point3d
ShapePoints(O) . X 0: ShapePoints(O).Y
ShapePoints(l).X = 2: ShapePoints(l) . Y 0
ShapePoints(2).X = 1: ShapePoints(2).Y
Set myShape = CreateShapeElement1( Nothing. ShapePoints . _
msdF i 11 ModeFi 11 ed)
ActiveModelReference.AddElement myShape
End Sub

TestCreateShapeB creates a filled triangle. If the resulting triangle does

not look like it is filled, the fill setting in view attributes may not be
selected (Settings> View Attributes).

Let's build on our knowledge of creating shapes. Now we are going to

create a function that creates a regular polygon based on a center point,
a number of sides, and a radius. The polygon we create will be inscribed
within the radius we provide.
Function CreatePolygon(CenterP oint As Point3d. _
NumOfSides As Long. Radius As Double) As ShapeElement
Dim myShape As ShapeElement
Di m ShapePoints() As Po i nt3d
ReD i m ShapePoints(O To NumOfSides - 1) As Point3d
Dim Point ln dex As Long
Dim IncAngle As Double
In cAngle = 360 / NumOfSides
For Pointlndex = LB ound(ShapePo int s) To UBound(ShapePoints)
ShapePoints(Pointlndex) = _
Point3dAddAng leD istance(CenterPoint. _
Radians(IncAngle * Pointlndex) . Radius. 0)
Set CreatePo l ygon
CreateShapeElement1(Nothing. ShapePoints)
End Function

This is our function. It returns a ShapeElement. Since it utilizes

parameters, we need to create a test procedure to run it.

I Graphical Elements I


Sub TestCreatePolygon ()
Di m CPoint As Poi nt3d
Dim myShape As ShapeE'ement
Set myShape = CreatePolygon(CPoint. 6 . 1)
Act'veModelReference.AddElement myShape
End Sub

Our TestCre at ePolyg on procedure declares a variable as a Point3d. No

modification is made to the X, Y, or Z elements of the point so the
polygon is created centered around (0, 0, 0).

Creating Circles
A circle is defined by a center point and a radius or diameter. We create
circles in MicroStation VBA by using the CreateEll i pseEl ementl and
CreateEll i pseEl ement2 methods.

Function CreateE l l ipseElementl(Template As Element.

PerimeterPointl As Point3d. PerimeterPo i nt2 As
Point3d. PerimeterPoint3 As Point3d . [Fi l lMode As
MsdFillMode = msdFi l lModeUseActiveJ) As
Ell i pseEl ement
Function CreateEllipseElement2(Temp l ate As Element.
Origin As Point3d. PrimaryRadius As Do uble.
SecondaryRadius As Double. Rotation As Matrix3d.
[Fil l Mode As MsdFi l lMode = msdFillModeUseActiveJ) As
Ell i pseEl ement


I Chapter 15: Adding To Documents I

We will begin with CreateEll i pseEl ement 2.
Sub TestCreateCircleA()
Dim CPoint As Point3d
Di m myEll ipse As Ell i pseEl ement
Dim rotMatrix As Matrix3d
CPoint . X = 2. 5: CPoint.Y = 2 . 5
Set myEllipse = (reateEllipseElement2(Nothing, (Point, 0.5 , 0.5, _
ActiveMode1Reference.AddE l ement myEl1ipse
End Sub

The center point is set at (2.5, 2.5, 0) and we are using a radius of 0.5. We
supply the same value for the PrimaryRadius parameter as we do for the
SecondaryRadius parameter. This results in a circle. If the primary and
secondary radii values are different, an ellipse is created.
Sub TestCreateCircleB ()
Dim CPoint As Poi nt3d
Dim my E11 ips e As El l ip seE1ement
Dim rotMatrix As Matr i x3d
Dim CirRad As Double
CPoint.X = 2. 5: CPoint . Y = 2.5
For Ci rRad = 0 . 5 To 2 Step 0 .12 5
Set myE1lip se = CreateEl1ipseElement2(Nothing, CPoint, _
CirRad , CirRad , rotMatrix)
ActiveModelReference.AddElement myE1l ipse
Next CirRad
End Sub

Test Crea t eCir e1eBcreates a series of corradial circles with radii ranging
from 0.5 to 2 in .125 unit increments.
The next procedure allows the user to select the center point of the circle
to be drawn. The radius used is 0.5.
Sub TestCreateCircleC ()
Di m CPoint As Point3d
Dim myE 1lipse As EllipseE1ement
Dim rotMatr i x As Matrix3d
Dim inputOueue As CadlnputQueue
Dim inputMessage As CadlnputMessage
Set inputOueue = CadlnputOueue

I Graphica l Elements I


Set i nputMessage = _
inputOueue . GetInput(msdCadInput TypeDa taPoint , _
Select Case inputMessage.InputType
Case msdCadInPJtTypeDataPo'nt
CPoint = inputMessage . point
Set myEllipse = CreateEllipseElement2(Nothing,_
CPoint, 0.5, 0.5, rotMatrix)
ActiveModelReference.AddElement myEllipse
Exit Do
Case msdCadInputTypeReset
Exit Do
End Select
End Sub

The last circle-creating procedure we will write allows the user to select
two points. A circle is then drawn through the selected points.
Sub TestCreateCirc l eD ()
Dim CPoint As Point3d
Di m StPoint As Po i nt3d
Dim EnPoint As Point3d
Dim myEll i pse As Ell i pseElem ent
Di m rotMatr i x As Mat r ix3d
Di m i nputOueue As Cad I nput Oueu e
Dim i nputMessa ge As CadInputMessage
Dim CirRad As Double
Set inputOueue = CadInputOueue
Set inputMessage = _
inputOueue.GetInput(msdCadInputTypeDataPoint, _
Select Case inputMessage.InputType
Case msdCadInputTypeDataPoint
StPoint = inputMessage . point
Exit Do
Case msdCadlnputTypeReset
Exit Sub
End Select


I Chapter 15: Adding To Documents I

Set i nputMessage

i nputO ueue . Getlnput _

(msdCad l npu t Ty peDat aPo i nt , _
msdCad l nput TypeAny)

Select Case inputMessage.lnputType
Case msdCadlnput TypeDataPoint
EnP oi nt = i nputMessage .poi nt
Exi t Do
Case msdCadlnputType Reset
Ex it Sub
End Se l ect
CPoi nt . X Stpo i nt . X + (EnPo i nt . X St Po i nt . X)
CPo i nt.Y
StPoint.Y + (EnPo i nt.Y
StPoint . Y)
CPoint . Z StPoint . Z + ( EnPoint.Z
StPo i nt . Z)
CirRad = Po i nt3dDistance(StPoint, EnPoint) / 2
Set myEll i pse = CreateE llip se Elemen t 2(Not hi ng,
Ci r Rad, Ci rRad ,
Act i veModelReference.AddE l ement myEllipse
End Sub

/ 2
/ 2

CPoint, _

We calculate the center point of the circle by using the selected points.
We also use the MicroStation VBA Po i nt3dO i stance function to give us
the distance between the selected points.

Creating Ellipses
We have already used code that could create ellipses but the code created
circles because the primary and secondary radii were the same. Let's
look at three examples of creating ellipses.
Sub TestCreateE ll ipseA ()
Dim CPo i nt As Po i nt3d
Dim myEllipse As EllipseElement
Dim r ot Mat ri x As Mat r ix3d
CPoint . X = 2.5 : CPoint.Y = 2 . 5
Set myE ll ipse = Crea t eE l l i pse Elemen t 2(Noth i ng , CPo i nt , 1, 0. 5 , _
ActiveModel Ref erence . Add Element myEllipse
End Sub
Sub TestCreateEll i pseB ( )
Dim MajorA1 As Point3d
Dim Maj orA2 As Point3d

I Graphical Elements I


Dim MinorAl As Point3d

Dim myEllipse As EllipseElement
1: MajorAl.Y
MajorA2 . X = 5: MajorA2.Y = 5
MinorAl.X = 3: MinorAl . V = 2
Set myEllipse = CreateEllipseE"ementl(Nothing. MajorAl. MajorA2. _
ActiveModelReference . AddElement myEllipse
End Sub
Sub TestCreateEllipseC ()
Dim CPoint As Point3d
Dim myEllipse As EllipseElement
Dim rotMatrix As Matrix3d
CPoint.X = 2 . 5: CPoint . Y = 2 . 5
rottvlatr i x.RowX . X = 2
rottvlat ri x . RowY . X = 4 : rotMatr i x . RowY . Y = 5
Set myEllipse = CreateEllipseElement2(Nothing . CPoint. 1 . 0 . 5. _
ActiveMode l Reference.Add Element myE l lipse
End Sub

After running the above procedures, what do we find? Two of the three
procedures shown above create ellipses. However, the procedure
TestCreateE l l i pse8 created a circle. The method Create Ell i pseEl ementl
always creates a circle through the three points provided.

Creating Arcs
We have five different ways we can create arcs in MicroStation VBA.

Function CreateArcElementl (Template As Element, StartPoint As

Point3d, CenterPoint As Point3d, EndPoint As Point3d) As

Function CreateArcElement2(Template As Element, CenterPoint As

Point3d, PrimaryRadius As Double, SecondaryRadius As Double,
Rotation As Matrix3d, StartAngle As Double, SweepAngle As
Double) As ArcElement

3 Function CreateArcElement3(Template As Element, StartPoint As

Point3d, PointOnCurve As Point3d, EndPoint As Point3d) As


I Chapter 15: Add ing To Documents I


Function CreateArcElement4(Template As Element, StartTangent

As Ray3d, EndPoint As Point3d) As ArcElement

Function CreateArcElementS(Template As Element, Chord As

Segment3d, ArcLength As Double, Plane Point As Point3d) As

Let's look at a few ways to use these methods.

Sub TestCreateArcA ()
Dim CPoint As Point3d
Dim StPo i nt As Point3d
Dim EnPoint As Point3d
Dim myArc As ArcElement
CPoint . X = 1: CPoint . Y = 1
StPoint . X = 4: StPoint . Y 1
EnPo i nt . X = 1: EnPo i nt . Y 4
Set myArc = (reateArcElementl(Nothing . StPoint . (Point . EnPoint)
ActiveModelReference.AddElement myArc
End Sub
Sub TestCreateArcB ()
Di m CPoint As Poi nt3d
Dim rotMatrix As Matrix3d
Dim myArc As ArcE l ement
CPoint . X = 1: CPo i nt.Y = 1
Set my Arc = CreateArcElement2(Nothing. CPoint, 0.5, 0.5, _
rotMatri x, 0, Pi)
ActiveModelReference . AddElement myArc
End Sub
Sub TestCreateArcC ()
Dim PointA As Point3d
Dim Po int B As Point3d
Dim PointC As Po i nt3d
Dim myArc As ArcElement
PointA . X 1: PointA.Y
PointB . X = 2: PointB.Y = 2
PointC . X = 1: Po i ntC.Y = 3
Set myArc = CreateArcElement3(Nothing , PointA , PointS, PointC)
ActiveModelReference.AddElement myArc
End Sub

I Graphical Elements I


Sub Tes t Cr eateArc D()

Di~ myArc As Arc~lement
Dim myRay As Ray3a
Dim EndPoint As Point3d
myRay.Origin.X = 1
myRay .Origi n . Y = 1
myRay . Direction.X =
myRay.Direction.Y = 4
EndPoin~.X = 0 : EndPoint.Y = 2
Set myArc = CreateArcElement4(Nothing , myRay , EndPoint )
ActiveModelReference.AddElement myArc
End Sub
Sub Te s tCreateArcE ()
Di m myArc As Arc Element
Di m mySeg As Segment3d
Di m myPoi nt As Point3d
mySeg . startPo i nt.X = 1 : mySeg . startPoint.Y = 1
mySeg . End Point . X = 4 : mySeg . EndPoint.Y = 4
myPoint.X = 3.5 : myPoint . Y = 3 : myPoint . Z = 0
Set myArc = CreateArc Element5 ( Nothing , mySeg , 8 . 5 , myPoint)
Ac t iveModelRe f erence . AddEle ment myArc
End Sub

Creating Text
Text is easy to create by using the CreateTextElementl method.

Function CreateTextElementl(Te mp late As Element,

Tex t As String, Origin As Point3d, Rotation As
Matrix3d) As Text Element

Here is an example of creating nine text elements spaced 0.5 units away
from each other.
Sub TestCreateTextA ()
Dim myText As TextElement
Dim TextPt As Point3d
Dim rotMatrix As Matrix3d
Dim I As Double
For I = 1 To 9
TextPt.Y = TextPt.Y - 0.5


I Chapter 15: Adding To Documents I

= CreateTextEleme ntl(N ot hing, "N ote " & I &
":", TextPt, rotM atrix)
Active~cdelReference.AddEle~ent myText

Se t myText

End Sub



iN. ote
. I
iNote 3:
4 :
N. ote ,:
-----------'-6'-- -- - ------+-. ----ote

" .


. . .. ..... t

"N-'---! .


'Note 7:::



'l'Iote .

Creating Cells
Thus far, all elements we have created have been added to the design file
as individual elements. When we begin working with cells, we work with
multiple elements as a single cell. We create the elements in the same
manner as when we are adding them to our model but instead of adding

I Graphical Elements I


the created element to the model we add it to the cell. We have three
options for creating cells.

1 Function CreateCellElementl (Name As String, ElementsO As

_Element, Origin As Point3d, [IsPointCell As Boolean]) As

Function CreateCellElement2(CellName As String, Origin As

Point3d, Scale As Point3d, TrueScale As Boolean, Rotation As
Matrix3d) As CellElement

3 Function CreateCellElement3(CellName As String, Origin As

Point3d, TrueScale As Boolean) As CellElement
Our first example creates a cell named "Box". Four lines are added to an
array of elements. This array is used when we create the cell.
Sub TestCreateCellA ()
Dim myCell As CellElement
Dim BoxL i nes(O To 3) As Element
Dim OriginPoint As Poi nt3d
Se t BoxL i nes(O) = Create Lin eEle me nt2(Nothing, Point3dFromXYZ(0 , 0, 0) , Point3dFromXYZ(4, 0 , 0))
Set BoxLines(l) = CreateL i neElement2(Nothing, _
Point3dFromXYZ(4, 0 , 0) , Point3dFromXYZ (4, 4, 0) )
Set BoxLines(2) = CreateLineElement2(Nothing, _
Point3dFromXYZ(4, 4, 0) , Point3dFromXYZ(0 , 4, 0) )
Set BoxLines(3 ) = CreateLineElement2(Nothing , Point3dFromXYZ(0, 4, 0) , Point3dFromXYZ(0, 0, 0) )
OriginPoint.X = 2: OriginPoint.Y = 2
Set myCell = CreateCellElementl( "Box", BoxLines, OriginPoint)
ActiveModelReference.AddEl ement myCell
myCell . Redra~1

End Sub

Tes tC rea teCe 11 A creates A 4-unit square with an origin of (2,2, 0).
Sub TestCreateCellB ()
Dim myCell As CellElement
Dim CellElements(O To 6) As Element
Dim OriginPoint As Point3d
Dim rotMatrix As Matrix3d
Set CellElements(O) = CreateLineElement2(Nothing,


I Chapter 15: Addin g To Docum ents I

Point3dFromXYZ(O , 0 , 0) , Po i nt3dFromXYZ(4 , 0 ,
Set CeIIElements(l) = CreateLineElementZ(Nothing , _
Po i nt3dFromXYZ(4 , 0 , 0) , Point3dFromXYZ(d , 4 ,
Set CeIIEl ements( Z) = Cr ea teLin eEl em entZ(Nothing , _
Po i ~t3dFromXY Z ( 4, 4, 0) , P o in t3d ~ romXYZ(O , d
Set Ce I IElements ( 3 ) = Create LineElementZ ( Nothing, _
Point3dFromXYZ(O, 4, 0) , Point3dFromXYZ ( O, 0,
Set CeIIElements(4) = CreateLineElementZ ( Nothing, _
Point3dF ro mX YZ(O , 0, 0 ) , Point3dFrom XY Z( 4, 4 ,
Set CeI I Elements(5) = CreateLineElementZ(Nothing, _
Po i nt3dFromXYZ(4, 0 , 0) , Point3d FromXYZ(O , 4,
OriginPoint.X = Z: OriginPoint.Y = Z
Set CeI I Elements(6) = CreateEllipseElement2(Nothing, _
OriginPo i nt , 1 . Z5, 1.Z5 , rotMatrix)
Set myCel1 = CreateCe I IElement1( "BoxZ ", Ce l lElem ents , _
OriginP oint)
Ac t iveModelReference . AddElement myCel1
myCell . Redraw
End Sub

0) )

0) )

0) )
0) )

As the number of elements we want in a cell increases, the upper-bound

array number increases. Six lines and a circle are used in
TestC re ate Ce ll Bto create a cell named "Box2".
Creating cells is easy to do as we have already seen. Adding the cell to a
cell library makes the creation of the cell useful in other files.
Sub TestCreat eCel l C()
Di m myCell As Ce l lEl ement
Dim CellElements(O To 6) As Element
Dim OriginPoint As Point3d
Dim rotMatrix As Matrix3d
Set CellElements(O) = CreateLineElementZ(Nothing ,
Po i nt3dFromXYZ(O, 0 , 0) , Point3dFromXYZ(4,
Set CellElements(l) = CreateLineElement2(Nothing, _
Point3dFromXYZ(4, 0 , 0) , Point3dFromXYZ(4,
Set CeIIElements(2) = Create LineElementZ(Nothing , _
Point3dFromXYZ(4 , 4 , 0) , Point3dFromXYZ(O ,
Set CellElefllenLs(3) = CreateLineElement2(Nothing , _
Point3dFromXYZ(O , 4 , 0) , Point3dFromXYZ(O ,
Set CellElements(4) = CreateLineElement2(Nothing, _
Point3dFromXYZ ( O, 0, 0) , Point3dFromXYZ(4,
Set CellElements(5 ) = CreateLineEleme nt2(Nothing, _

0 , 0))
4 , 0))
4 , 0))
0, 0))
4, 0 ))

I Creating New Documents I


Point3d Fr omXY Z(4 , 0 , 0) , Po i nt 3dFromXYZ(0 , 4

Orig i nPoi nt . X

2 : Or i g i nPo i nt . Y


Set CellElements(6) = CreateEllipseE emert2(Nothi n g, _

OriginPoint, 1 . 25, 1.25, rotMatrix)
Set myCell = CreateCellElementl( "Box3 " ,
ActiveModelReference . AddE l e~ent



myCell . Redraw
App li c a t i on . AttachC e l l Li br ar y "Mi c ro St a ti on VBA . c e l "
Application.AttachedCellLibrary.AddCell myCell, _
"Box3 ", " Box3" , False
End Sub

Note that we specify the file name of the cell library we want to attach
the cell to. We do not specify the full path, only the file name.

o !Jse Shared Cells





o Q.isplay All Cells In Path

, Type


, Where

Active Cells

I 80x3


I Element


We have drawn lines, circles, ellipses, arcs, text, and cells to the current
design file. This assumes we have a file to work with. How do we create
new design files?

Function CreateDesignFile(SeedFileName As String,

NewDesignFileName As String, Open As Boolean) As DesignFile

Sub CopyDesignFile(ExistingDesignFileName As String,

NewDesignFileName As String, [Overwrite As Boolean])


I Chapter 15: Addin g To Documents I

Here are two methods that create new design files. Crea t eOesignF il e
allows us to specify whether the new file is to be a 2D or 3D file by
specifying the seed document. Let's look at a couple of examples.
Sub TestCreateDesignFileA ()
Dim myFile As DesignFile
Application . ActiveDesignFile.Close
Set myFile = CreateDesignFile( "seed2d " ,
"C:\MicroStation VBA\filea.dgn", True)
End Sub

TestC reateOes i gn Fil eA creates a new 2D design file. The file path and
name are specified. After the Crea teOes i gnF i 1e line of code is executed,
the new file is created and opened. It becomes the active document. If
the file already exists, a new file is created and overwrites the existing
file. Since we don't receive any warning of this, we should check if the
file already exists.
Sub TestCreateDesignFileB ()
Di m my Fi le As Des i gnF i le
Di m myF i l eNa me As Str i ng
my Fi leN ame = "C: \M i cr oStation VBA\filea . dgn "
If Dir (my Fil e Name) = "" Then
Set myFi l e = Cr eate Design Fi l e( "seed3d ", my Fil eName , Tr ue)
Els e
Ms 9 Box "The f i 1e " & my Fi 1eName & " a 1 rea dye xis t s . ", _
vb Crit i ca 1
En d If
End Sub

If the file we want to create exists (we know this by using the Dir
function), we inform the user it already exists. If it does not exist, we
create a new 3D file.
Let's look at one more example:
Sub TestCreateDesignFileC ()
Dim myFile As DesignFile
Dim I As Long
For I = 1 To 10
Set myFile = CreateDesignFile( "seed2d ", _ "C:\MicroStat i on
VBA \ f i 1 e" & I & ". d 9 n" , Fa 1 s e )
End Sub

I Security Issues with Creating Data I

How many files does
TestCreateDesignFileC create?

It creates ten (10) files. Each

file is a new 2D file and th e
files are not opened in MicroS tation (the False Parameter) .


h':)fll; l :dgn

til file2, dgn

l:il file3, dgn
til f ile4 ,dgn
l:il fileS,dgn



Bentley ~licroS t ati o n Design File

34 KB

Bentley ~licroSta tion Design File


Bentley ~1icroStation Design File

34 KB

Bentley ~li croStation Design File

34 KB

Bentley ~1icroStation Design File

34 KB

Bentley ~licroStation Design File

34 KB

Bentley i'licroStation Design File

l:il file8, dgn

liJ file9,dgn

34 KB

Bent ley ~licroS tation Design File

34 KB

Bentley ~1i c r oStation Design File

b:jJ file lO,dgn

34 KB

Bentley i'licroStati on Design File



34 KB

'-',,_-'.L" '"'"'"'=c.:;C"~C'.C-'==.




Our ability to create data in MicroStation using VBA is dependent on
our security settings. VBA is not intended to bypass these security
settings. Writing and attempting to run the procedures in this chapter
on one machine may result in the intended creation of data. Other
machines with different security permissions may cause the code to fail.
CAD administrators should be able to provide the appropriate
permissions if this becomes a problem.

Simple geometry can be created with the knowledge of only a few
MicroStation VBA calls. The Object Browser and MicroStation VBA
help file can be used to find other data creation alternatives and can
provide examples of how to use them.


I Chapter 15: Adding To Documents I


Searching In Files
Our design files range in complexity from one or two elements to many
thousands. The number of elements can vary as well as the element
types (lines, circles, arcs, text) and colors. Levels, line styles and classes
can differ from element to element. Line weights and transparency can
also vary. As we begin searching in our files, we will learn how to
discover these properties we find in our files.

In this chapter:

The Basics of Searching Files


Using ScanCriteria


Multiple Combinations of Criteria


Reviewing Three Collection Methods


Scan Criteria Methods


Let's begin by examining each element found in a file.

TestScanAllA ()
Dim myElement As Element
Dim myEnum As ElementEnumerator



I Chapter 16: Sea rchi ng In Fil es I

Set myEnum = ActiveModelReference . Scan()
While myEnum.MoveNext
Set myElement = myEnum.Current
Debug.Print myElement.Type
End Sub

This procedure prints the type

property value of each element in the
active model to the Immediate



Running the procedure TestScanA 11 A

results in a list of numbers telling us
the type of element found. This
msdElementType enumeration.



."' .!

Here is a listing of the msdElementType enumeration's members:

msdX DatumTypeW orld Space Posit io n

msdElementType44 = 44
msdElementTyp eArc = 16
msdElementTypeBspl i neBound ary
msd El eme ntT ypeBs plin eCurve = 27
msdElementTypeBsplineKnot = 26
msdElementTypeBsplinePole = 21
msdElementTypeBsplineSurface = 24
msdElementTypeBsplineWeight = 28
msdElementTypeCellHeader = 2
msdElementTypeCellLibraryHeader = 1
msdElementTypeComplexShape = 14
msdE1ementTypeComplexString = 12
msdElementTypeCone = 23
msdElementTypeConic = 13
msdE1ementTypeCurve = 11
msdElementTypeDesignFileHeader = 9
msdElementTypeDgnStoreComponent = 38
msdElementTypeDgnStoreHeader = 39
msdElementTypeDigSetDa t a = 8

I The Basics of Searching Files I

msdElementTypeDimension = 33
msdE l ementTypeEllipse = 15
msdE l ementTypeGroupData = 5
msdElementTypeLevelMask = 99
msdElementTypeLine = 3
msd ElementTypeL i neStr i ng = 4
msdElementTypeMatr i xDoubleData = 103
msdElementTypeMatrixHeader = 101
msdElementTypeMatrixlntegerDa t a = 102
msdElementTypeMeshHeader = 105
ms dElementTypeMicroS t atio n = 66
msdElemen t TypeMultiLin e = 36
msdElementTypeNamedGroupComponent = 111
msdElementTypeNamedGroupHeader = 110
msdElement TypePointString = 22
msdElementTypeRa sterCompo nent = 88
msdE l ementTypeRa st erFrame = 94
msdElementTypeRasterHeader = 87
msdElementTypeRasterReference = 90
msdElementTypeReferenceAttachment = 100
msdElement Ty peReferenceOverride = 108
msdElementTypeShape = 6
msdElementTypeSharedCel l = 35
msdElementTypeSharedCellDefinit io n
msdElementTypeSolid = 19
msdElementTypeSu r face = 18
msdEleme ntTypeTab l e = 96
msdElementTy peT ag = 37
msdElementTypeText = 17
msdElementTypeTe xtNode = 7
msdEleme ntTyp eView = 98
msdElementTypeViewGroup = 97



A review of the Immediate window, shown previously, shows the first

three unique element types are 9, 96, and 97. Referring to the list above
tells us the first three element types found were:


I Chapter 16: Searching In Files I

msdE1ementTy peDesignF i1 eHeader
msdE 1eme ntTypeTab1e = 96
msdE1ementTypeV i ewGroup = 97

Not exactly lines, circles, or arcs, right? MicroStation design files are
composed of far more than what we see on the screen as we are working
with MicroStation. What are the next three element types? 66, 6, and 4.

msdE1ementTypeMicroStation = 66
msdE1ementTypeShape = 6
ms dE1ementTypeLineString = 4
Now we're getting somewhere. We can see shapes and linestrings.
We are going to do a lot of copy and paste operations in this chapter.
Let's begin by copying and pasting TestScanA 11 A as TestScannA 11 B.
Sub TestScanAllB ()
Dim my Ele ment As El ement
Dim myEnum As Elemen tEnu me rat or
Set myEnum = ActiveModelReference . Sca n()
Whi l e my Enu m.M ove Next
Set myElement = myE num . Current
Select Case myElement.Type
Case msdElement TypeArc
Dim myArc As ArcElement
Set myArc = my Element
Case msdElementTypeCurve
Dim myC urve As CurveElement
Set myCurve = myElement
Case ms dEl eme nt Ty pe Lin e
Dim myL i ne As Li ne El ement
Set myL i ne = myE l ement
Case msd Elem ent Type Text
Dim myText As Text Element
Set my Text = my Element
Case Else
Debug . Pr i nt myElement . Type
End Se l ect
End Sub

We can make the use of a Se1ect ... Cas e statement to allow us to

perform actions based on the Element.Type property. As we cycle

IThe Basics of Searching Files I


through each element in our ElementEnumerator we set each element to

a generic element object. If we want to work with a LineElement we
could do so through the generic element object but declaring a variable
as a LineElement makes our programming tasks much easier. Let's see
why this is true.

@mm:mm ~~ fDl,Ii' '"


@' Subtype


,'~ T ransiorm

@' Subtype
_,~ Transiorm

@' Type
@' URLTitie
@j' Vertex


@' Type
~@' URLTitle


As we are programming, which list would help us most if we are

working with a line element? The list on the left gives us a StartPoint
property. Lines have start points. The list on the right does not have a
StartPoint in the list. If we declare a variable as a LineElement we will
see line-specific properties in addition to the standard element
Let's do a little more with the above procedure. Copy and paste it as
TestScanA 11C. After doing so, we are going to remove everything inside
the Sel ect ... Case statement except for the "Case msdElementTypeText"
Sub TestScanAl lC ( )
Dim myElement As Element
Dim myE nu m As ElementEnumerator
Set myEnum = ActiveModelReference.Scan()
Whi l e myEnum . MoveNext
Set myElement = myEnum.Current
Select Case myElement . Type
Case msdElementType Text
Di m myText As TextElement
Set myText = myElement
myText .T ext = UCase(myText . Text)
End Select
End Sub

Now, our procedure is only going to react to text elements. And what are
we doing to the text element? UCa s e capitalizes everything. The result of
this procedure should be the capitalization of all text elements, right?


I Chapter 16: Searching In Files I

After this code is executed we should find that nothing has changed.
How is this possible? The code is capitalizing the text. Let's take a look at
the next procedure and see if we can find what is missing.
Sub TestScanAllO ()
Dim myElement As Ele ment
Dim myEnum As ElementEnumerator
Set myEnum = ActiveModelReference.Scan()
While myEnum.MoveNext
Set my El ement = my Enu m. Cur ren t
Select Case myEleme nt . Type
Case msdE l eme nt Type Tex t
Di m myText As TextElement
Set myText = myE l ement
myText . Text = UCase(myText.Text)
End Select
End Sub

If we don't rewrite the element to the model, the text element may be
modified in memory but the change is not actually made to the design

Now, let's suppose we are working with a large file. It is composed of
thousands of elements but only four of them are TextElements. If we run
the code shown above, the TextElements will be capitalized to be sure.
However, it may take a while because each and every element in the
design file is reviewed. Let's make our code more efficient by working
only with text elements. We accomplish this through the use of an
ElementScanCriteria object.
Sub TestScanFilterA ()
Dim my Enum As El emen t Enume r ator
Dim my Fi lter As New ElementScanCriteria
Dim ElementCounte r As Long
myFilter . lncludeType msdE l ement TypeText
myFi l ter . lnclude Type msd ElementTypeTextNode

I Using ScanCriteria I


Set myE num = ActiveModelReference.Scan(myFilter)

While myEnum . MoveNext
ElementCounter = ElementCounter + 1
MsgBox ElementCoun:e r & " elements foune ."
End Sub

When we include Text and TextNode elements, we should only be

counting the number of Text and TextNode elements. After running this
code, however, we find that something is not working as expected.
On careful examination we find that, by default, ScanCriteria includes
everything. Before specifying which elements we want to look at, we
need to exclude everything and then include those elements with which
we want to work.
Sub TestScanFilterB ()
Dim myEnum As ElementEnumerator
Dim myFilter As New ElementScanCriteria
Dim ElementCounter As Long
myFilter . ExcludeAllTypes
my Fi lter. lncludeType msdEle mentTypeText
myFilter . lnc l udeType msdEle mentTypeTextNode
Set myEnum = ActiveModelReference.Scan(myFilter)
While myEnum . MoveNext
ElementCou nte r = ElementCounter + 1
MsgB ox ElementCoun ter & " elements found. "
End Sub

Now, myEnum only contains Text and TextNode elements.

Let's build on TestScanFi 1terB by adding a filter for a specific level.
Before we look for a specific Level, we must first exclude all levels. If we
miss this critical step, we will be retrieving all levels.
Sub TestScanFilterC ()
Dim myEnum As ElementEnumerator
Dim myFilter As New ElementScanCriteria
Dim ElementCounter As Long
myFilter . ExcludeAl l Types
myFilter . ExcludeAllLevels
myFi l ter .l ncludeType msdElementTypeText
myFilter .l ncludeType msdElementType TextNode


I Chapter 16: Searching In Files I

my Fi 1t e r . In c 1 ude Lev e 1 Act i ve Des i 9n Fi 1e . Leve l s ( " SI DEWALK" )
Se t myEnum = Act i ve Mode lR ef e r ence.Scan(myF i lter)
While myEnum.MoveNext
ElementCounter = ElementCounter + 1
MsgBox El ementCounter & " el ements found ."
End Sub

Let's look over the macro "ScanFilterc". What is being counted here?
Text elements and TextNode elements on Level "SIDEWALK'~
Sub TestScanFilterO ( )
Di m myEnum As ElementEnumerator
Dim myFil t er As New Ele mentScanCr i teria
Dim ElementCounter As Long
myFilter . ExcludeA l lTypes
myFilter.IncludeType msdElementTypeText
myFilter.IncludeType msdElementTypeTextNode
myFilter.IncludeLevel ActiveDesignFile.Levels( "SIDEWALK " )
myFilter . IncludeCo l or 4
Set myEnum = ActiveModelReference.Scan(myFilter)
While myEnum.MoveNext
ElementCounter = ElementCounter + 1
MsgB ox ElementC ounter & " elements found."
End Sub

We have added one more scan criteria. In addition to looking at the

element type and level, we are now looking at the color.
If we know a color's index in the document's color table, we can specify
it as shown above. Let's look at the next example where we specify an
RGB color value to filter for a specific color. We will also add one more
item in our scan criteria. Let's add a Linestyle criteria.
Sub TestScanFilterF ()
Dim myEnum As ElementEnumerator
Di m myFi l ter As New ElementScanCr i teria
Dim ElementCounter As Long
Dim myColorTable As ColorTable
Dim myColor As Long
Set myColorTable = ActiveDes i gnFile . Extra ct Co l orTa ble

I Using ScanCriteria I


myColor = myCo l orTable.FindClosestColor(RGB(192 . 192 . 192))

myFilter . ExcludeAl lTypes
my Fi 1t e r . Ex c 1ude All Level s
myFi 1:er. Excl udeA 11 Li neStyl es
myFilter.IncludeType msdElementTypeLineString
myFilter.lncludeLineStyle ActiveJesignFile .Li neStyles( " ( Hidden ) " )

my Fi 1t e r . Inc 1udeL eve 1 Act i ve Des i 9n Fi 1e . Level s ( " SID EWA LK" )
myFilter.lncludeColor myColor - 1
Set myErum = ActiveModelReference.Scan(myFilter)
While myEnum . MoveNext
ElementCounter = ElementCo unte r + 1
MsgBox ElementCounter & " elements found. "
End Sub

And yet another scan criteria is added in our next procedure:

Sub TestScanFilterG ()
Dim myEnum As Ele mentEnu merator
Dim myFilter As New ElementScanCriteria
Dim El ementCounter As Long
Dim my ColorTa ble As ColorTable
Dim myCol or As Long
Set myColorTable = ActiveDesignFile.ExtractColorTable
myColor = myColorTable .Fin dClosestColor(RGB(192. 192. 192))
myF il ter.ExcludeAll Types
my Fi 1t e r . Ex c 1 ude All Color s
myFilter .E xcludeAllL i neStyles
myFilter.lncludeType msdElementTypeLineString
myFilter.lncludeLineStyle ActiveDesignFile . LineStyles("( Hidden )")

my Fi 1t e r . Inc 1udeL eve 1 Act i ve Des i 9n Fi 1e . Level s ( " SID EWALK" )

myFilter . lncludeColor myColor - 1
myFilter. IncludeClass msdElementClassPrimary
Set myEnum = ActiveModelReference.Scan(myFilter)
While myEnum.MoveNext
ElementCounter = El ementCounter + 1
MsgBox ElementCounter & " elements found. "
End Sub


I Chapter 16: Searching In Files I

N ow we are adding the "Class" to our scan criteria.
Thus far we have excluded everything from our criteria and added in
only the criteria we wanted. When we 'ExcludeAllLevels', the number of
levels we exclude varies from file to file.
N ow, let's look at each of the levels in our design file. One specific level
will not be added to our scan criteria and everything else will be added.
Su b TestScanFilterH ()
Dim myEnum As ElementEnumerator
Dim my Fi lte r As New El ement ScanCrit eri a
Di m my Level As Level
Dim El emen t Coun t e r As Long
For Eac h myLevel In ActiveDes ig nF il e.Levels
Select Case UCase( myLeve l. Na me)
Case Else
myFilter.IncludeLevel myLevel
End Select
Set myEnum = ActiveModelReference . Scan(myFilter)
While myEnum.MoveNext
ElementCounter = ElementC ounter + 1
MsgBox ElementCounter & " eleme nt s found. "
End Sub

When dealing with our ElementScanCriteria object, everything is

within the bounds of the criteria. Since we don't have the option to
remove a specific element type or level, etc., in the above example, we
remove all levels and then add back those levels that meet our criteria. In
the above example, we are adding all levels except for the "SIDEWALK"

I Multiple Combinations of Criteria I



Thus far we have dealt with elements matching specific criteria in each
procedure. What do we do if we want all cells on level "Columns" and all
text elements on level "Marks"? Here are three ways to accomplish the
same task.
Sub TestSca nFi lterJ()
Dim myElem As Element
Dim myEnum As ElementEnumerator
Dim myEnumZ As ElementEnumerator
Dim myFilter As New ElementScanCriter i a
Dim myFilterZ As New ElementScanCriteria
Di m El ement Co un t er As Long
myFilter . ExcludeAllTypes
my Fi 1t e r . Ex c 1ude All Leve l s
myFi 1 ter. In cl udeType msdEl ementTyp eSha redCell
myFilter . Include Level Act i veDes i gnFile .L evels ( "CO LUMNS " )
Set myEnu m = ActiveMode l Referen ce . Scan (my Filter )
Wh il e myEnum.MoveNext
El ementCounter = El ementCounter + 1
Wen d
my Fi l te r Z. Exc l udeAl l Types
my Fi 1terZ . Exc l udeA 11 Level s
my FilterZ . In c ludeTyp e msdEl ementTypeTe xt
myFi l terZ.IncludeLevel ActiveDesignFile.Levels( "MARKS")
Set myEnumZ = ActiveModelReference . Scan(myFilterZ)
While myEnumZ.MoveNext
ElementCounter = ElementCounter + 1
MsgBox ElementCounter & " elements found ."
End Sub

We can us e two different enumerator objects with two different scan

criteria objects. This is one way to deal with our current scenario. Are
there other ways we can accomplish the same goal?
Sub TestSca nFilterK ()
Dim myElem As Element
Dim myEnum As ElementEnumerator


I Chapter 16: Searching In Files I

Dim myFi1ter As New E1ementScanCriteria
Dim myCo11ection As New Collecti on
myFi 1ter. Exc1 udeA 11 Level s
myFi1ter . Inc1udeType msdE1ementTypeSharedCe11
my Fi 1t e r . Inc 1 udeL eve 1 Act i ve Des i 9 nFi 1e . Level s ( "C0 LUMNS" )
Set myEnu~ = ActiveMode1Reference . Scan(myFi1ter)
While my Enum . MoveNex t
Set myE1em = myEnum . Current
myCo11ection . Add myE1em
myFi 1ter.Exc 1udeA11Ty pes
myFi 1ter . Exc1udeA 11 Leve 1s
myFi 1ter .I nc1udeType msd E1ementTypeText
myFi 1ter . I nc1 udeLeve1 Ac t i veDesi gnFi 1e . Level s ( "MARKS " )
Set myEnum = Act i veMode1Reference . Scan(myFi1ter)
Wh il e myEnum . MoveNext
Set myE1em = myEnum . Current
myCo11ect i on.Add myE1em
MsgBox myCo11ection . Co unt & " el ements found ."
End Sub

This is another way to accomplish the same goal. We apply two separate
criteria. As we move through each enumerator, we add the element in
the enumerator to a custom collection. This allows us to work with a
single collection of objects after each combination of criteria is applied.

I Multiple Combinations of Criteria I


Elementcache!ElemenlCac I

Adding a watch to
t he variable
myCo llection
shows somet hing
like th is:

Cache Index









#9120120055:26:39 PM#













IsComponentElement False














Is Locked

Fals e




8 00le an



8 00lean











<Attempting to perform a non-! MsdElementSubtype


msdElementTypeSharedCe l1






Var iantlOiJjecl!Element

Var iantlObject!Element

Here is one more way to accomplish the same task. We are going to
create a named group and then add the objects we find to the named
Sub Tes t ScanFi l terM ()
Dim myEnum As ElementEnumerator
Dim myFilter As New ElementScanCriteria
Dim myGroup As NamedGroupElement
Set myGroup = ActiveModelReference.AddNewNamedGroup( "GroupA " )
myFilter . ExcludeAllTypes
myFi 1ter. Excl udeA 11 Level s
myFilter . lncludeType msdE l ementTypeSharedCe l l
my Fi 1t e r . Inc 1udeL eve 1 Act i ve Des i 9 n Fi 1e . Level s ( "C0 LUMNS " )
Set myEnum = ActiveModelReference . Scan(myFilter)
While myEnum . MoveNext
myGroup . AddMember myEnum.Current


I Chapter 16: Searching In Files I

myFi l ter.ExcludeAllTypes
my Fi 1t e r . Ex c 1ude All Level s
myFilter.lncludeType msdElementTypeText
myFilter.lncludeLevel ActiveOesignFile.Levels("MARKS")
Set myEnum = ActiveModelReference . Scan(myFilter)
While myEnum.MoveNext
myGroup.AddMember myEnum.Current
MsgBox myGroup.MembersCount & " elements found."
End Sub


Each of the three methods described above have their advantages and
disadvantages. For the sake of discussion) we will refer to the methods as
multi-criteria) collection) and group.
The multi-criteria method provides a straightforward and simple way to
get groups of criteria in their own individual enumerators. One benefit
to doing things this way is that we have our individual groups of criteria
in their own distinct groups. This allows us to work with each group
separately if desired. The primary disadvantage is that these individual
groups make it more difficult to work with the elements in each group as
a whole.
The collection method uses only one Scan Criteria object and places all
objects found into a single custom VBA collection. Doing so allows us to
use Fo r Each ... Next statements on the entire collection) remove items
from the collection, etc.
The group method may provide the best possible results. Each item is
placed into a single container. This gives us the same benefit as using a
collection. The real benefit to using groups is that when we use
"myGroup.Rewrite") the group is added to the design file and can be
used by the user with other standard MicroStation commands and

I Scan Criteria Methods I


functionality. If we do not "rewrite" the group, the elements added to the

group do not get added to the group in the design file even though the
group itself is in the design file. So, if we want to use a group without
rewriting it to the design file, we should remove the group after we have
completed our programming tasks.


The code we have written in this chapter has used several methods of
the ElementScanCriteria Obj ect. Here is a comprehensive listing of the
Su b
Su b

Exc l udeA 11 Cl asses ()

Exc l udeA 11Co l ors ( )
Excl udeAll Level s()
Excl udeA 11 Li neStyl es ()
Excl udeA 11 Li neWei ghts ()
ExcludeAllSubtypes( )
Excl udeA 11 Types ( )
Excl udeGraphi ca 1 ()
Excl udeNonGraph i cal ()
IncludeClass(ElemClass As MsdElementClass)
IncludeColor(Colorlndex As Long)
I ncl udeLevel ( Level As Level)
Incl udeL ineSty le(LineS tyle As LineStyle)
IncludeLineWeight(LineWeight As Long)
IncludeOnlyCell(CellName As String)
Inc l udeOnlyFilePositionRange(Min As Long. Max As Long)
IncludeOnlyGraphicGroup(GraphicGroupNumber As Long)
Incl udeOnly Hol e()
Inc l udeOnlylnvi si bl e()
Inc l udeOnlyModif i ed()
Inc l udeOnlyModifiedRange(Min As Date . [Max As Date])
Incl udeOnlyNew()
I ncl udeOn 1yNonSnappab 1e ( )
Incl udeOnlyPl anar()


I Chapter 16: Searching In Files I


IncludeOnlyUn l ocked()
IncludeOnlyUserAttribute(UserAttributeID As Long)
IncludeOnlyWithinRange(Range As Range3d)
IncludeSubtype(Long As Long)
Inc l udeType(Type As Msd El ementType)

A review of the MicroStation VBA help file explains any of the methods
that are not self-explanatory. One method is worth noting: the
"IncludeOnlyWithinRange" method.
Sub TestScanFilterN()
Dim myEnu m As ElementE numerator
Dim myFilter As New Ele mentScanC riteria
Dim myGroup As NamedGroupElement
Dim myRange As Range3d
Set myGroup = ActiveModelReference.AddNewNamedGroup("GroupC")
myRange. Low.X = 1: myRange.Low.Y = 1: myRange.Low.Z = 0
myRange . High.X = 3: myRange.High.Y = 3: myRange.H i gh.Z = 0
myFilter . lncludeOnlyWithinRange myRange
Set myEnum = ActiveModelReference.Scan(myFilter)
While myEnum.MoveNext
myGroup.AddMember myEnum.Current
We nd
myGroup. Rewrite
MsgBox myGroup.MembersCount & " el ements found."
End Sub

The ability to scan a file from within only a specific area is very
powerfuL We may look for elements surrounding a point selected by the
user, for example. Or we may scan for elements surrounding cells with a
specific name. The range we specify is 3D so we can provide a Low.Z
and a High.Z value if we are working on 3D files.

I Review I


Each file in MicroStation is composed of many objects. Some of these
are visible, others are not. Levels, for example, are not graphical
elements but are still very important.
We should be careful when we scan our files. If we scan with the intent
to create new geometry, it is possible to create a problem for ourselves.
For example, if we are scanning a file for lines and are drawing new lines
over old ones, the new lines may be added to our ScanCriteria and we
could end up in an endless loop.
This chapter covered scanning MicroStation files with pre-defined
criteria. In the next chapter, a user makes selections in MicroStation and
then has our code manipulate the selection.


I Chapter 16: Searching In Files I


Interactive Modification
User interaction can be helpful when modifying files and elements in
VBA. When our programs are designed well, they are powerful and

In this chapter:

Giving users feedback and information


Working with selection sets


Getting user input


Using the send command


Employing modeless dialog boxes


Applying some real-world applications


Interacting with MDL applications


When we are working with MicroStation in any capacity, three distinct
areas at the bottom of the MicroStation window give us information and



I Chapter 17: Interactive Modification I

These areas are called the command, prompt, and status areas.


Place SmartLine > Enter first verte~


11"0-File [C:\Microstation VBA\docs\chapter17a.dgnj saved

When we begin the "Place SmartLine" command, we see the command

and the first prompt associated with this command. We are prompted to
"Enter first vertex". After we click the first vertex, we are prompted to
"Enter next vertex or reset to complete". The Status area gives us general
feedback on the results of selections and other commands.
Let's see how we can work with these areas to give our users similar
feedback and information as they use our programs.
Sub TestShowCommand ()
ShowC om man d "Draw a Li ne "
ShowPrompt "Select First Po i nt :"
ShowSta tu s "Draw Line by selec t ing tw o pO i nt s . "
En d Sub


Draw Line by selecting two points.

Three methods are used to show the text we want to display in the
command, prompt, and status areas of MicroStation. Even though the
user can change the size of the command/prompt area, make sure that
commands and prompts are visible without requiring users to stretch
the area wider. Commands and prompts are not meant to provide
comprehensive instructions, but rather, general guidelines.
Sub TestShowTempMe ss age ()
ShowTempMessage msdStatusBarAreaLeft, "Message Left."
ShowTempMessage msdStatusBarAreaMiddle, "Message Middle."
End Sub

I Givi ng Users Feedback and Information I

: Eile


Element 2.ettings 10015 !,Ltilities wor!5,space

: ~ lroe;aU-lt ------.;JI 0

~ Q i~ I ~ ~

ft II<)


IMndow tielp

. 1_




ItQ i ?


11J~ 1 ~:"Ij

;... : CJ

2} .. :.



A!- .

.I. ~

.GJ~ E-



Another way we can provide feedback to the user is by sending a

"Temporary Message". We have the option of placing the message in the
"Left Area" or the "Middle Area". Messages placed in the middle area
also appear in the Message Center.
Sub ShowTempMessage(Area As MsdStatusBarArea,
Message As String, [Details As String])
Here is the declaration for Sh owTempMessage . It has one optional
parameter, "Details". When we provide a value for this parameter and we
have specified "msdStatusBarAreaMiddle" as the location for the
message, the detail we provide displays in the Message Center. This is an
excellent way to provide a more lengthy message to the user if needed.

Sub TestShowTempMessageCenter ()
ShowTempMessage msdStatusBarAre aMi dd l e. "Changes made to file :" , _

"Changes were made to t he fi l e C: \testa.dgn. " &

"These changes were made by the macro " &
"" "TestS howTempMessageCenter ""."
End Sub


I Chapter 17: Interactive Modification I

Changes made to file.

Circle, Level: Default


Shaoe. Level: Def aull

Message Details
Changes were made to the me C:\testa,dgn These changes
were made by the macro "T estShow TempMessageCenter",

The next feedback method we will look at is the ShowErr or method. The
text we supply with this method displays in the command/prompt area.
Sub TestShowError()
ShowError "Selection of Cell Fai led ,"
End Sub
Selection of Cell Failed.


No Eiements Found


Users can select elements in their files through a variety of methods.
Once selected, we can make modifications to the selected elements by
using the Ge t Se l ectedEleme nt s method.
Sub TestSelectionSetA ()
Di m myE l ement As El ement
Dim myElemEnum As ElementEnumerator
Set myElemEnum = Act i veModelReference , GetSelectedElements
While myElemEnum.MoveNext
Set myElement = myElem Enum , Current
myE l ement .L evel = ActiveModelReference.Levels( _
End Sub

We used the ElementEnumerator in a previous chapter. In this example,

we get the selected elements and change the level of each element oneby-one. Let's look carefully at the code. Are we missing anything?
Sub TestSelect i onSetB ()
Dim myElement As Element
Dim myElemEnum As ElementEnumerator
Set ~yElemEnum = ActiveModelReference.GetSelectedElements

I Working With Selection Sets I


While my El emEnum . MoveN ext

Set my El ement = my El emEnu m. Cur r ent
myEl ement . Level -

ActiveMode l Re ~ erence.Levels( " A-FURN-FREE " )

End Sub

If we do not rewrite the element to the design file , element

modifications are not persistent. This is critical. You could spend a great
deal of time debugging code only to find that changes made to elements
are not reflected in your files. Any changes made to elements in files
must be rewritten back to the file or they are not permanent. This is by
Sub TestSelectionSetC ( )
Dim mySettings As Settings
Se t mySettings = Application . ActiveSettings
If MsgB ox( "Change Select i on t o Colo r" & myS ett i ngs.C ol or & " ?", _
vbYesNo) = vbYes Then
Dim myElement As El ement
Dim myElemEnum As ElementEnumerator
Set myElemEnum = ActiveModelRefere nce . GetSelectedElements
While myElemEnum.MoveNext
Set myElement = myElemEnum.Current
myElement . Color = mySettings . Color
myElement.Rewr i te
End If
End Sub

TestS el ecti onS etC

Change Selection to Color O?

elements to the active color in MicroStation if
the user clicks on the Yes button in the
MessageBox. We are using the same
methodology going through each of the
elements in the ElementEnumerator.


I Chapter 17: Interactive Modification I



Thus far we have discussed prompting the user with information and
working with previously-selected elements. Allowing the user to give us
input as our procedures execute makes our interactive modifications
more powerful.
The Cad Input Queue allows us to capture some of the user's interaction
with MicroStation. Let's look at a few examples of using the CAD Input
Queue. We begin with a very simple example that demonstrates the use
of the CAD Input Queue and then move to some real-world examples.
Sub TestCadlnputA ()
Di m myCIO As CadlnputOueue
Dim myCI M As CadlnputMessage
Dim I As Long
Set myCIO = CadlnputOueue
For I = 1 To 10
Set myC IM = myCIO.Getlnput
Debug.Print myCIM . lnputType
Ne xt I
End Sub

In the above example, we capture ten user interactions and print the
InputType to the Immediate Window. The main thing we want to see
with this example is the mechanics of how to use the CadInputQueue
and the CadInputMessage.
Let's make a couple of modifications to the above example to capture
only point selections.
Sub TestCadlnputB ()
Dim myC IO As CadlnputOueue
Di m myC IM As Cad l nputMessage
Dim I As Long
Dim pt3Select i on As Point3d
Set myCIO = CadlnputOueue
For I = 1 To 10
Set myC I ivi = [IIYC 10. Ge LI nput (msdCad I nputTypeData Poi nt)
pt3Selection = myCIM.Point
Debug.Print pt3Selection.X & ", " & pt3Selection.Y
Next I
End Sub

I Getting User Input I


The CadInputQueue captures a number of different types of inputs.

When we use the GetInput method we can specify which type of inputs
we want to capture. In the above example we are restricting the capture
to data point entries. Since we know we are getting a point, we can use
the point property of the CadInputMessage object and print the X, Y,
and Z elements of the point to the Debug window (Immediate Window).
Let's continue to build on our "TestCadInput" macros. In the next
example we will capture points and resets.
Sub TestCadlnputC()
Dim myCIO As CadlnputOueue
Dim myCIM As CadlnputMessage
Dim I As Long
Dim pt3Selection As Point3d
Set myCIO = CadlnputOueue
Fo r I = 1 To 10
Set myCIM = myCIO . Getlnput(msdCadlnputTypeDataPoint, _
Select Case myCIM .l nputType
Case msdCadlnputTypeDataPoint
pt3Selection = myCIM.Point
Debug . Pr i nt pt3Selection.X & " " & pt3Selection.Y
Case msdCadlnputTypeReset
Exit For
End Select
Next I
End Sub

Now, our macro captures up to ten input points or until a reset is

initiated by the user. We use Ex it For to exit out of the loop when a reset
is detected.
We have introduced DataPoint and Reset input types, so what other
types are available to us?

msdCadlnputTypeReset = 2
msdCadlnputTypeOataPoint 3
msdCadlnputTypeKeyin = 4
msdCadlnputTypeAny = 5
msdCadlnputTypeUnassignedCB 6


I Chapter 17: Interactive Modification I

When we begin capturing input using the CadlnputQueue, our program
listens to each of the inputs, then the results of the inputs is entirely in
the hands of our program. For example, if we begin capturing inputs,
selecting a command from a toolbar sends the command information to
our queue but MicroStation does not begin acting on the command
Sub TestCadlnp utD()
Dim myCIO As CadInpu t Oueue
Dim myCIM As CadInputMessage
Dim I As Long
Di m pt3Selection As Point3d
Set myCIO = CadInputOueue
For I = 1 To 10
Set myCIM = myCIO.Getlnput
Select Case myCIM .l nputType
Case msdCadlnputTypeCommand
Debug.Pr i nt "Command " & vbTab & myCIM . Command Keyin
Case msdCadlnputTypeReset
Exit For
Case msdCadlnputTypeDataPoint
pt3Selection = myCIM.Point
Debug.Print "Point" & vbTab & pt3Selecti on . X & vbTa b &
pt3Selection.Y & vbTab &
pt3Selection.Z & vbTab & _
myC IM.Vie w.l ndex & vbT ab & _
myCIM . ScreenPoint.X & vbTab &
myCIM.ScreenPoint.Y & vbTab &
Case msdCadlnput TypeKeyin
Debug . Print "Keyin " & vb Tab & myCIM.Keyin
Case msdCad l nputTypeAny
Debug . Pr i nt "Any "
Case msdCadlnputType UnassignedCB
Debug.Print "UnassignedCB " & vbTab &
myCIM . CursorButton
End Select
Next I
End Sub

I Getting User Input I


This pro cedure captures ten inputs or captures until a reset is detected.


bogus keyin
30833 . 5756568922
MOL KEYIN lv l mang~ levelmanage~ dialog open
310 1 9 . 7486873093


377 579 0
196 475 0

168 238 0
144 363 0
241 329 0


The resu lts of runn ing this procedure with a variety of inputs.

The points selected gives us much more than the X, Y, and Z locations in
M icroStation. We also see in which view the point was selected and the
screen coordinates in X, Y, and Z when the point was selected. The
screen X, Y, and Z could be useful for more advanced work such as
displaying graphical information in MicroStation using the Windows

Whenever a legitimate MicroStation command is initiated and we are
listening using the Cad Input Queue, the input comes across as a
command. This is the case no matter whether the command was
initiated using menus, toolbars, or the Keyin window.

If the Keyin window is used to enter a legitimate command, the input is
registered as a command and not a keyin. When something is entered in
the Keyin window that does not result in a legitimate command, it is
registered as a keyin. The example above demonstrates this when "bogus
keyin" was entered into the Keyin window.

Unassigned Cursor Buttons

An unassigned cursor button generates an UnassignedCB input. We use
the CursorButton property of the message to retrieve which cursor
button was used.


I Chapter 17: Interactive Modification I

The Reset Input is triggered when the user initiates a reset. For example,
clicking the right mouse button initiates a reset when the user is asked to
select a point.


Now that we have an understanding of how these inputs work, let's put
th em to work in some real-world examples.
Sub Te stC ad lnputE ()
Dim myCIO As CadlnputOueue
Dim myC IM As Cad ln put Mess age
Dim pt3Start As Po i nt3d
Dim pt3End As Point3d
Dim myLine As LineE l ement
Set myCIO
Set myCIM = myCIO.Getlnput (msdCadlnputTyp e DataPoi nt .
Select Case myCIM. l nputType
Case msdCadl nput TypeReset
Exit Sub
Case msdCadlnputTypeDataPoint
pt3Start = myC IM. Point
End Se l ect
Set myCIM = myCIO.Getlnput(msdCadlnputTypeDataPoint.
Select Case myCIM.lnputType
Case msdCadlnputTypeReset
Ex it Sub
Case msdCadlnputTypeDataPoint
pt3End = myCIM . Point
End Select
Set myLine = CreateLineElement2(Nothing . pt3Start. pt3End)
ActiveModelReference . AddElement myLine
End Sub

Te s t Cad I n putE allows the user to select two points. A line is then
drawn between these two points. A careful examination of the code, and

I Some Real -World Applications I


better yet, running the code, reveals that although the user can select
two points and a line is drawn between the points, the user has no way
of knowing what to do or what the results of the actions will be. Let's use
our knowledge of ShowCommand and ShowPrompt to make the macro more
user friendly.
Sub TestCadlnputF( )
Dim myCIO As CadlnputOueue
Dim myCIM As CadlnputMessage
Dim pt3Start As Point3d
Dim pt3End As Point3d
Dim myLine As LineElement
Set myCIO = CadlnputOueue
ShowCommand "Two-Point Line"
Show Prompt "Select First Point:"
Set myCIM = myCIO . Getlnput(msdCadlnputTypeDataPoint . _
Select Case myCIM.lnputType
Case msdCadlnputTypeReset
ShowPrompt ""
ShowCommand ""
ShowSta t us "Two-Point Line Reset."
Exit Sub
Case msdCadlnputTypeDataPoint
pt3Start = myCIM.Point
En d Select
ShowPrompt "Select Second Point:"
Set myC IM = myC IO.Getlnput(msdCa dlnputTypeDataPoint . _
Select Case myCIM . lnputType
Case msdCadlnputTypeReset
ShowPrompt ""
ShowCommand ""
ShowStatus "Two-Point Line Reset ."
Ex it Sub
Case msdC adlnputTypeDataPoint
pt3End = myC IM . Point
End Select
Set myL ine = CreateLineElementZ(Nothing . pt3Start . pt3End)
ActiveModelReference . AddElement myLine


I Chapter 17: Interactive Modification I

ShowPrompt ""
ShowCommand ""
ShowStatus "Two-Point Line Drawn."
End Sub

Now, when this macro is run, the user is prompted at each step.
The CadlnputQueue can be used for more than just capturing user
input. We can use it to execute commands as well. Here is one example:
Sub TestCadlnputH ()
Dim myCIO As CadlnputOueue
Dim myCIM As CadInputMessage
Dim pt3Start As Point3d
Dim pt3End As Point3d
Di m myL i ne As LineElement
Dim SelElems() As Element
Set myCIO
Set myCIM = myCIO.GetInput(msdCadlnputTypeDataPoint,
Select Case myCIM.InputType
Case msdCadInputTypeReset
Exit Sub
Case msdCadI nputTypeDataPoint
pt3Start = myCIM.point
End Select
Set myCIM = myCIO.GetInput(msdCadInputTypeDataPoint,
Select Case myCIM.lnputType
Case msdCadInputTypeReset
Ex it Sub
Case msdCadInputTypeDataPoint
pt3End = myCIM.point
End Select
CadInputOueue.SendDragPoints pt3Start, pt3End
SelElems =
ActiveModelReference . GetSelectedElements.BuildArrayFr omC ontent s

If MsgBox("Are you sure you want to delete" &

UBound(SelElems) + 1 & " Elements?", vbYesNo) _
= vbYes Then
CadInput Ou eue.SendCo mmand "DELETE"
End I f
End Sub

I Some Real-World Applications I


In this example we used the selected points with the Se ndD ragP oi nts
method of the CadInputQueue object to effectively select the elements
within the window generated by the two points. We get a count of the
number of elements selected and ask the user to verify that the elements
are to be deleted through a MessageBox with Yes and No buttons. If the
user says "Yes", we delete the selected elements by sending a Command
of "DELETE".
This allows the user to select two points and delete the window between
the two points. But we must ask ourselves, does it work well? After the
first point is selected, we cannot see where the point had been selected.
It would be better if we could see the first selection point like when we
draw a line.
The next function allows the user to select two points. After the first
point is selected, we see the same graphical interface from MicroStation,
as we when drawing a line using standard MicroStation commands,
until the second point is selected. This function then returns the two
Function PointsByLine () As Point3d()
Dim myCIO As CadInputOueue
Dim myCIM As CadlnputMessage
Dim pt3Start As Point3d
Dim pt3End As Point3d
Dim selPts(O To 1) As Point3d
Set myCIO
Cad l nputOueue
Set myCIM = myCIO . GetInput(msdCadlnputTypeDataPoint. _
Select Case myCIM . InputType
Case msdCadInput TypeReset
Err . Raise - 12345
Exit Func t ion
Case ms dCad Inp ut TypeDat aPoin t
pt3Start = myCIM . point
End Select
CadlnputOueue . SendCommand "PLACE LINE "
Cad l nputOueue . SendDataPoi nt pt 3Start
Set myCIM = myCIO . GetInp ut(msdCadInputTypeDataPoint . _
Select Case myCIM . InputType
Case msdCadInputTypeReset


I Chapter 17: Interactive Mod ification I

Err.Raise -12346
Exi t Functi on
Case msdCadlnputTypeDataPoint
pt3 End = myCIM . point
End Se lect
selPts(O ) = pt3Start
selPts(l ) = pt3End
PointsByLine = selPts
End Functio n

After the user selects the first point, we begin the "PLACE LINE"
command and supply the command the point the user selected. This
creates a rubber-band effect that allows us to see the first point selected
and also shows the cursor's coordinates as it waits for the second point
to be selected. After the second point is selected, we place the selected
points into an array that is used for the return value of the function . If
the user issues a reset while the first or second points are entered, we
raise an error so the function or procedure that called Poi nt sByLi ne"
function will know what happened. We need to remember that the
"PLACE LINE" command is still in process as we exit the function. We
will handle it in the calling procedure or function as follows :
Sub TestCadlnputJ ()
On Err or GoTo err hnd
Di m sel Pt s( ) As Point3d
selPts = Poi nt sBy Li ne
CadlnputOueue . SendReset
Debug.Pr i nt selPts(O).x & " , " & selPts ( O) .Y & ", " & selPts(O).Z
Debug . Print se l Pts(l) . X & ", " & selPts(l) . Y & ", " & selPts(l).Z

Ex i t Sub
er r hnd :
CadlnputOueue . SendReset
Selec t Case Er r.N umbe r
Case -12345
'Start Point not selected
MsgBox "Start Point not selected. ", vbCritica l
Case -12346
' End Point not selected
MsgBox "End Point not selected . " , vbCritical

I Some Real-World Applications I


End Selec t
End Sub

We use Poi ntsByL i ne to get two points. Notice the SendReset and
StartOefaul tCommand calls. This resets the Place Line command which
started when we called "PointsByLine". If the user selects the two points
as requested, we display the coordinates of the points in the Immediate
Window. If the user does not select one of the points, we know which
point selection was aborted based on the error raised in the

PointsByLine Function.
Here is a more practical application of our new Poi ntsByL i ne function:
Sub TestCadlnputK ()
On Error GoTo errhnd
Dim selPts() As Point3d
Dim pt3TextPt As Point3d
Dim my Te xt As TextElement
Dim rotMatrix As Ma t rix3d
selPts = PointsByLine
Set myText = CreateTe xtEleme ntl ( Nothing. "St a rt " . se lPt s (O) . r ot Matrix )

ActiveModelReference . AddElement myText

Set myText

CreateTextElement1(Nothing. "En d". se l Pts(ll . rotMatrix)

ActiveModelReference .A ddElement myText

pt3TextPt . X selPts(O) .X + (selPts(l).X - selPts(O) . X) /2
pt3 Textpt.Y = se l Pts(O) . Y + (se l Pts(l).Y - sel Pts(O)'Y) /2
pt3TextPt.Z = selPts(O).Z + (selPts(l) . Z - selPts(O) . Z) / 2
Set myText = CreateTextElementl(Nothing. "Mid". pt3TextPt. rotMatrix)

ActiveModelReference.AddElement myText
Ex i t Sub
Cad ln putQue ue . SendReset
Comma ndState . StartDefaultCommand
Select Case Err.Number
Case -12345
' Start Point not se lected
MsgBox "Start Point not selected ." , vbCrit i cal
Case -12346
' End Point not selected
MsgBox "End Point not selected. " . vbCritical


I Chapter 17: Interactive Modification I

End Select
End Sub

The framework is the same as the previous example. We use our new
Poi ntsByL i ne function to get two points while simulating the Place Line
command. Once we get the points, we use them to place three new text
elements in our file. "Start", "End" and "Mid" are placed at the start
point, the end point, and the calculated mid point.
Here is what it
looks like in
Mi croStation :


Here is one more function that simulates the "PLACE BLOCK"

command, allowing the user to stretch out a rectangle instead of a line.
Functi on PointsByRectangle () As Po i nt3d ( )
Dim myCIO As CadInputOueue
Dim myCIM As CadInputMessage
Dim pt3Start As Point3d
Dim pt3End As Point3d
Dim selPts(O To 1) As Point3d
Set myCIO
Set myCIM = myCIO.GetInput(msdCadInputTypeDataPoint, _
msdCad I nputTypeReset)
Select Case myCIM.InputType
Case msdCadInputTypeReset
Err.Raise -12345
Exit Function
Case msdCadInputTypeDataPoint
pt3Start = myCIM . point
End Select
CadInputOueue.SendCommand "PLACE BLOCK "

I Some Real-World Applications I


CadlnputOueue . Sen dDataPo int pt3Start

Set myCIM = myCIO.Getlnput(msdCadlnputTypeDataPoint. _
msdCad l rput TypeReset)
Select Case myCIM.lnputType
Case msdCadlnputTypeReset
Err.Ra i se -12346
Exit Funct i on
Ca se msdCadlnputTypeDataP oin t
pt3E nd = myCIM.p oi nt
End Select
selPts(O) = pt3Start
selPts(l) = pt3End
PointsByRectangle = selPts
End Function

And now a procedure that uses Po i ntsBy Recta ngl e:

Sub TestCadlnp utL ()
On Error GoTo err hnd
Dim selP t s ( ) As Poi nt3d
se l Pts = Poi ntsByRectang l e
CadlnputOueue . SendReset
CommandState . StartDefaultCommand
Deb ug.Print selPts(O) . x &

& selPts(O) . Y & .... & selPts(O).Z

Debug.Print selPts(l) . X & " ... & selPts(l).Y & ..

& se l Pts(l) . Z

Ex it Sub
CommandState . StartDefaultCommand
Select Case Err .Nu mber
Case -12345
'Star t Poi nt not selected
MsgBox "Start Point not selected .". vbCritical
Case - 12346
'E nd Point not selected
MsgBox "End Point not selected .... vbCritical
End Select
End Sub


I Chapter 17: Inte ractive Modification I


,__ __ __ ___ _ __ _ __ _ __ _ __ __ _ _ ____ __ _ _ . ....



- - -'-:t: - - - - - - - - - - - _.- - - - - - _.- - - - -- - - - - - - - - - - - .

! ~'-~r~~r~'~] (j'I '~'-"""" ']~r~] Cl !",.-".-... -.. -~~

:/o tl~~~_~]G100 0 : .~~~~=~' cR0i'J ICE ~ ._~ _._,_...J G1e

! ':



C .


: . \r---,-

i~},~,.. .~~\. >..~~...~/..~..,\.~:::~,~:::.




11 0

:; , '


~< ,

\ SE Yf
- - ' TJ


: li t~
TestC adl nputL does not do anything fancy. It just displays the points
selected in the Immediate Window. Let's make better use of
Po i nts ByRec tan gl e by using the selected points as part of a scan criteria
in selecting cells in a file.
Sub TestCadlnputM ()
On Err or GoTo er rhn d
Dim sel Pts() As Po i nt3d
Di m Li nePt s(O To 1) As Point 3d
Dim LineElem As LineElement
Dim myESC As New ElementScanCriteria
Dim myRange As Range3d
Di m myElemEnum As El ement Enumerator
Dim myElem As Element
Dim FFile As Long
Dim myCellHeader As Cell Element
selPts = Po i ntsByRectangle
Cad I nputOueue.SendReset
CommandState . StartDefaultCommand
myRange = Range3dFromPoint3dPoint3d(selPts(O) , selPts(l))
myESC . ExcludeAllTypes
myESC.IncludeType msdElementTypeCellHeader
myESC . Incl udeOnlyW i thi nRange myRange
Set myElem Enum = ActiveModelReference.Scan(myESC)

I Some Real -World Applications I


FFile = FreeF i le
Open "C: \MicroStation VBA\CellExport.txt" For Output As #FFile
Pr int #FFile, ActiveDesignFile.Name
While myElemEnum.MoveNext
Set myElem = myElemEnum.Currert
Set myCellHeader = myElem
Print #FFile, myCellHeader.Name & vbTab &
myCellHeader . Origin.X & vbTab &
myCellHeader .Origin.Y & vbTab &
Close #FFile
Exit Sub
Select Case Err.Number
Case -12345
'Start Point not selected
MsgBox "Start Point not selected. " , vbCritical
Case -12346
' End Point not selected
MsgBox " End Point not selected .", vbCritical
End Select
End Sub

This macro writes the names and locations of cells in the active model
reference that fit within the selected rectangle.

Here is the
output for the
office.dgn file
installed with



pffi ce.





17411. 6804376836
15851. 6804376836
16991. 6774376836
15651. 6804376836



The results of the macro differ from file to file and from selection to
selection. If fewer cells are selected inside the rectangle, fewer cells will
be output to the text file.


I Chapter 17: Interactive Mod ification I

Thus far we have used SendCommand with "DELETE", "PLACE LINE",
and "PLACE BLOCK': Even though these commands may look familiar
to some readers, they may be fore ign to others. Each time a menu item is
selected or toolbar button clicked, a command is issued to MicroStation.
How do we know what these commands are? Good question.
The MicroStation VBA macro recorder can help us to discover
command names and how they are used. Let's try recording a few
macros to demonstrate this.

From the VBA Project Manager, select the VBA Project in which we
are currently working and then click the record button.

Now, select the Line

Command from the toolbar as

3 "Place Line" begins by asking

for points between which to draw lines. Select two points in
MicroStation and then click the right mouse button to issue a reset.

Next, stop recording the macro by clicking the "Stop Record"


After macro recording has stopped, return to the VBA environment

to see the new macro. The macros are named automatically so the
names may vary from computer to computer.

The results of this recorded macro should look similar to this:

I Using SendCommand I


Sub Macrol c)
Dim s tartPo in t As Point 3d
Dim pOi nt As Poi nt3d. point2 As Po i nt3d
DiTi lngTemp As Long
Start a command
Coordinates are in master units
startpoint . X 16735 . 231975
startpo i nt.Y
33020 . 733029
sta r tp oint.Z
Se nd a data po i nt to the cu r rent command
point. X sta r tPo int. X
point . Y = startPoint . Y
point . Z = startPoint . Z
Ca d InputOueue.SendDataPoint point. 1
startPoint.X + 1985 . 401024
point . Y startpoint . Y 610 . 892623
startPoint . Z
CadInputOue ue . SendDataPoint point. 1
Send a reset to the current co mmand
Comman dState . StartDefaultComman d
End Sub

This recorded macro reveals a command of "CGPLACE LINE

CONSTRAINED". The coordinates shown are those selected in
MicroStation as the macro was being recorded. Let's copy and paste the
recorded macro and modify it as follows:
Sub Macrol _modifiedA C)
Dim po i nt As Point3d
CadI nputO ueue . SendCommand CGPLACE LINE CONSTRAINED
point . X = 0 : point . Y = 0: point.Z = 0
CadInputOueue . SendDataPoint point . 1
poi nt . X = 4: point . Y = 5 : poi nt . Z = 6
Cad In pu t Ou eu e .S endDat aP oi nt poin t . 1


I Chapter 17: Interactive Modification I

CadlnputOueue . SendReset
End Sub

We have now stripped down this macro to the bare essentials. The
coordinates for the line have been replaced with (0, 0, 0) and (4, 5,6).
Let's record another macro. This time we will record drawing a Block
Sub Macro2 ()
Dim startPoint As Point3d
Dim point As Point3d, point2 As Point3d
Dim lngTemp As Long
Start a command
CadlnputOueue.SendCommand "PLACE BLOCK ICON"
Coordinates are in master units
startPoint . X 3 .1 96418
startPo int.Y
6 . 071205
Send a data point to the current command
startPoin t. X
Cad l nputOueue . SendDataPoint point, 1
point.X = startPoint.X + 2.537984
point.Y = startPoint.Y - 0.882104
point.Z = startPoint.Z
Cad lnput Oueue.SendDataPoint point, 1
CommandState . StartDefau lt Command
End Sub

Here is a stripped-down and modified version of Macro2.

Sub Macro2_modifiedA ()
Dim point As PoiflL3d
CadlnputOueue.SendCommand "PLACE BLOCK ICON"
point.Y = 0

I Using SendCommand I


point.Z = 0
Ca dln putOueue .S endData Poi nt po i nt . 1
po int.X = point . X + 2. 5
point . Y = pOint.Y - 0 . 75
CadlnputOueue . SendDataPoint pOint.
CommandState . StartDefaultCommand
End Sub

In this example, we are basing the second point on the first point.
Instead of entering hard-coded coordinates, the second point is relative
to the first point. However, even though the placement of the second
point is relative to the first point, the first point is hard-coded. Let's
make a few more modifications.
Sub Macro2 modi f iedB ()
Dim point As Point3d
Di m myCIO As CadlnputOueue
Dim myCIM As CadlnputMessage
Set myCIO = CadlnputOueue
Set myC IM = myCIO . Getlnput(msdCadlnputTypeDataPoint )
point = myCIM.point
CadlnputOueue . SendCommand "PLACE BLOCK ICON "
CadlnputOueue.SendDataPoint point. 1
point.X = point.X + 2.5
point.Y = point . Y - 0.75
CadlnputOueue.SendDataPoint point.
End Sub

Now the first point used for the block is entirely based on user input.
The second point is still relative to the first point.
Recording macros is one way to discover the command names of
MicroStation commands. The following macro is another way.
Sub TestCadlnputN ()
Di m myCIO As Cad l nputOueue
Dim myC IM As CadlnputMessage
Dim I As Long
Set myCIO = CadlnputOueue
For I = 1 To 10
Set myCIM = myCIO.Getlnput(msdCadlnputTypeCommand)


I Chapter 17: Interactive Modification I

Debug . Print myCIM . CommandKeyin
Next I
End Sub

TestCadlnputN captures ten

different from recording
macros in that we do not
get all of the other input,
such as point selections,
etc. The only thing we
capture is the command


Attach Tags

One additional method of determining command names should be

L~~_~!:~~~ _________________.___________________________-'

dims lyle




dialog openfile
dialog drawing scale open
dialog dr awingscale
bogus keyin

The Key-in dialog opens by selecting Key-in from the MicroStation

Utilities menu. Items can be selected from the list boxes to construct the
appropriate key-in. The image shown tells us we can use "DIALOG
OPENFILE" as a command. Let's try it.
Sub Tes tMessageA ()
CadInputOueue.SendCommand "DIALOG OPENFILE "
End Sub

I Modeless Dialog Boxes I


Running the Tes tMessageA macro shows us that "DIALOG OPENFILE"

is indeed a legitimate command. The Open File dialog box displays and
the user can select a file to open.


InputBoxes and MessageBoxes allow the user to interact with our code.
Their functionality is somewhat limited, however. When our goal can be
accomplished with a MessageBox, it should be used. But when we need a
richer interface or more dynamic interaction with the user, we need to
use Forms.
The next four examples are on the CD accompanying this book. Import
them one at a time by using the VBA menu File> Import File and
selecting the appropriate file from the CD. This imports a new form into
the active VBA project.

frm", looks like this:
r :source Element:~..,..-,--,-,--,-~ ~-:-:t !:-: De,stination Element(~): ~:-:-:- t

i: I: Change Current Selection :
simple enough. We
r I
I: : 0 Element(s) modified: ,
have a few command
buttons, a couple of
frames, a handful of
ILineweight r I
,': h-,:- .' .' , : ,: ,: ,: ,:::,J.
check boxes, a label,
and four text boxes.
Before we look at the
code behind
controls, let's discuss the program's desired functionality.

:: ,. ~ :









I Chapter 17: Interactive Modification I

Desired Functionality

The user can select a Source element in MicroStation. After the

element is selected, the Select button is clicked and four properties
are extracted from the selected element: level, color, linestyle, and

The user can select which of the properties from the source element
are to be changed in the Destination Elements.

3 The user can select any number of elements in MicroStation to be

modified based on the selected properties of the source element.
This sounds simple enough. Let's get started. Even though the form can
be imported from the CD, we will discuss the entire process of creating
the form.
The first thing we do is place the controls. As we work with an interface,
we will find ourselves resizing and moving the controls to make our
interface flow nicely for the user. Captions (when available) can be
modified immediately after we add each control.
Naming the controls is the next step. Here are the names of the controls
with which we will be interacting:





























I Modeless Dialog Boxes I


As we develop this program, we should be thinking about the future of

this program. For example, the code in this form does not do anything
to the frames. We could leave their names as "Framel" and "Frame2",
but we may decide to make the Destination frame invisible until the
Source Element is selected. Then, after the Source Element is selected,
we make the Destination Frame visible. We can do this using "Frame2"
as the frame's name, but naming it "fraDestination" in our code. This
tells us exactly what we are making visible or invisible without having to
browse through the frames on the form to find out which frame we are

Control Properties

The Locked property of each TextBox should be "True". We do not

want the user arbitrarily typing in values that do not work. The text
boxes will be populated by the source element's properties.

The Alignment property of each CheckBox should be

"fmAlignmentLeft': This places the caption of the CheckBox on the
left of the CheckBox.

The ControlTipText of the Select CommandButton is "Click Here to

make the current selection the source elemenf'.

The ControlTipText of the "Change Current Selection"

CommandButton should be "Click Here to modify the current
selection to match the selected properties from the Source element:'.

The ControlTipText of the "Close" CommandButton should be

"Click Here to Close the VBA Match Properties Program:'

Later we will add code to display this form as modeless. This means the
user will be able to interact with MicroStation even though the form is
displayed. This is important to keep in mind as we look at the code
behind the controls.

General Declarations Area

We have two lines of code in the general declarations area of our code.
Option Explicit
Dim elemSource As Element


I Chapter 17: Interactive Modification I

Select Button
We can only use one element as the source element. When the user
clicks the Select button, the first thing we need to do is to discover how
many elements have been selected. If only one element has been
selected, we can continue. Otherwise, we will display one of two
MessageBoxes: one MessageBox if nothing was selected or a second if
more than one element was selected.
If only one element is selected, do the following:

Get the level (if a level is assigned to the element). The level name is
placed in the appropriate text box.

Get the color and display the number in the appropriate TextBox
and change the TextBox's BackColor property to match the color of
the source element.

Get and place the linestyle property.

Get and place the lineweight property.

Now, let's look at the code behind the btnSelectSource_Click event:

Private Sub btnS ele ct Sourc e_Cli ck()
Di m myElement s( ) As El ement
Dim my El emEnu m As El ementEnu me r at or
Dim myC olorTa bl e As Col or Tabl e
Set my El emEnu m = Ac ti ve Mo del Refe r ence .G etSe l ectedE l ement s
my El ements = _
ActiveModelReference . GetSelectedElements . BuildArrayFromContents
If UBound(myElements) = 0 Then
Set elemSource = myE l ements(O)
If Not myE l ements(O).Leve l Is Nothing Then
txtLeve l . Text = my El ements (0). Level. Name
End If
Set myCo l orTable = Act i ve DesignFi l e .E xtractCo l or Table
Select Case myE l ements(O).Co l or
Case -1
txtColor.Text =
txtColor.BackColor = RGB(255. 255. 255)
txtLinestyle.Text = _
myElements(O) . LineStyle.Name
txtLineweight . Text = myElements(O).LineWeight
Case Else

I Modeless Dialog Boxes I


txtCo lor.Text = myElements(O).Color

txtColor . BackCo lo r =
myColorTable .Ge tCo l orAtlndex(myElements(O).Color)
txtL'nestyle.Text =

tx t Lineweight . Text
End Sele ct

my El emen t s(O) . LineWeight

E1 s e

Select Case UBound(myElements)

Case -1
MsgBox No Source element selected ., _
vbCritical , Me.Caption
Ex it Sub
Case Else
MsgBox Only one element can be the Source .. &
"element . , vbCritical . Me.Caption
Ex it Sub
End Select
End If
End Sub
Placing a
Break Point
in the code
allows us to
step through
the code line
by line. This
can help us
discover how
the program
is working or
to verify that
it is working
as designed.

We are working with the ElementEnumerator a little differently in this

instance. Instead of using "MoveNext" and getting the "Current"
element, we get an array of elements using Bu i 1dArray FromCo ntents . If
the upper-bound of the array is 0, the array is composed of one element,
This is what we want. If the upper-bound of the array is -1, this means
the array is empty and nothing was selected prior to clicking the button.

Change Current Selection Button

When the user clicks the "Change Current Selection Button", we want to
change the selected properties of the selected elements to the source
element's properties. We also change the caption of the label to reflect
how many elements were modified. We create an array of elements from
the enumerator as we did in the previous example.
Pri vate Sub btnChange_Cl i ck()
Di m myEl ements () As El ement
Dim myElemEnum As ElementEnumerator
Dim I As Long
Di m boolElemModified As Boolean
Dim lngModCount As Long
l blCount.Caption = 0 Element ( s) modified ."


I Chapter 17: Interactive Modification I

ShowStatus "0 Element(s) modified."
Set myElemEnum = ActiveModelReference.GetSe l ected Elements
myElements = myElemEnum.BuildArrayFromContents
lngModCount = 0
For I = LBound(myElements) To UBound(myElements)
boolElemModified = False
If chkLevel. Val ue = True Then
myEl ements ( I ) . Leve 1 = e 1emSource. Level
boolElemModified = True
End If
I f chk Color.Valu e = Tr ue Then
myE l em ent s (I).C ol or = el em Source .C ol or
boolEle mModi fie d = Tru e
End If
If chkLinestyle.Value = True Then
myEle me nts(I).LineSty le = elemSource.LineStyle
boolE lemModif ie d = Tr ue
End If
If chkLineweight.Value = True Then
myElements(I).LineWeight = elemSource.LineWeight
boolElemModif i ed = True
End If
If boolElemModified = True Then
myEl ements( I) . Rewri te
lngModCount = ln gModCount + 1
End If
Next I
l bl Count . Ca pt i on = l ng ModCou nt & " El eme nt (s) mod i fie d . "
ShowStatu s l ngMo dC ou nt & " Ele me nt(s) mod i f ied."
End Sub

As we look at each element in the array, we only want to change the

properties based on the CheckBox values. We only increase the element
modified counter if a change was actually made. It is possible to select a
source element and multiple destination elements and have no changes
made if each of the CheckBoxes are set to false .

Close Button
The Close button unloads the Form.

I Providing User Feedback and Information I


Pri vate Su b btn Cl ose_Cl i c k()

Unl oad Me
End Sub


Earlier in this chapter we learned how to provide the user feedback and
information through the use of the status bar area in MicroStation. If we
look at the status bar area, we see that it changes as we move our cursor
over various tool bar buttons. Let's mimic this same functionality in
VBA by using the MouseMove events of several controls.
Private Sub UserForm_MouseMove(ByVal Button As Integer, _
ByVal Shift As I nteger, ByVal X As Single, _
ByVal Y As Single)
End Sub
Private Sub fraDestination_MouseMove(ByVa l Button As Integer , _
ByVa l Sh i ft As Integer, ByVa l X As Sing l e, _
ByVa l Y As Single)
ShowPro mpt
End Sub
Private Sub fraSource_MouseMove(ByVal Button As Integer, _
ByVal Sh i ft As Integer, ByVal X As Single , _
ByVa l Y As Si ngle)
End Sub

As the user moves the cursor around the fo rm and the fram es, we do not
want to display anything in the prompt because clicking on the fo rm or
frame does not do anything. So, we use ShowPrompt with an em pty
string so nothing displays.
Private Sub btnSelectSource_MouseMove(ByVal Button As Integer ,
ByVal Shift As Integer, ByVal X As Single, _
ByVal Y As Single)
ShowPrompt "Select a single "" Source "" Element :"
End Sub
Private Sub btnChange_MouseMove(ByVal Button As Integer, _
ByVal Shift As Integer , ByVal X As Single, _
ByVal Y As Single)


I Chapter 17: Interactive Modification I

ShowPr ompt "Se l ect ""Destinat io n"" Elem ents :"
End Sub
Private Sub btnClose_MouseMove(ByVal Button As Integer, _
ByVa l Shift As Integer , ByVal X As Single, _
ByVal Y As Single)
ShowPrompt "Close "" VBA Match Propert i es """
End Sub

As the user moves the cursor over the command buttons, we want to let
the user know what happens if the button is clicked. We already do this
with the ControlTipText property of each button but using the prompt
more closely reflects MicroStation standard functionality.

UserForm Initialize
We need to discuss two additional events. The first of these is the
UserForm Initialize event. This event is triggered as the form is about to
be displayed.
Private Sub UserForm_Initia l ize()
ShowCommand "VB A Match Properties:"
End Sub

We use ShowCommand to set the Command area to "VEA Match

Properties" when the form is first initialized. This command continues
to display even though the prompt changes as the cursor is moved over
the other controls in our Form.

UserForm QueryClose
The QueryClose event is triggered just before the fo rm is terminated.
This event allows us to perform clean up operations. It also tells us how
the form was asked to close. The CloseMode parameter gives us one of
four values (which have corresponding constants).
IB vbFormControlMenu
IB vbFormCode



IB vbApp Windows


IB vbAppTaskManager


For more information on what each of these values mean, look up

"QueryClose Constants" in the Microsoft VBA help.

I Providing User Feedback and Information I


In this program we are not concerned with how the form is closed, only
that it is closing.
Private Sub UserForm_OueryClose(Cancel As Integer. _
CloseMode As Integer)
ShowPrompt ....
End Su b

Displaying the Form as Modeless

The code in the form is based entirely on the user's selection of elements
in MicroStation. Although it is possible to select elements prior to
displaying the form, we actually need the user to make two distinct
selections: the source and the destination. The source selection can only
be one element. To allow the user to select elements in MicroStation
while the form is displayed, we need to display the form as modeless.
Remember that modeless is the opposite of modal where, when the form
has focus, nothing else can be done in MicroStation until the form is
closed. Modeless means that even while a form is displayed interaction
can continue inside MicroStation. To display a form as modeless, we
must show it as such in a procedure in a code module.
Sub Tes tM atc hProperties ()
f r mMa tc hPropert i es . Show vb Model ess
End Sub

The procedure Tes tMatc hPr operti es, if placed in a code module, is
available to the user through the VBA Project Manager or from the
MicroStation menu Utilities> Macro> Macros or by pressing the <F8>
key while holding down the <Alt> key (<Alt + F8.


I Chapter 17: Interactive Modification I

Here is the Match
Properties Form in use.
Notice the Command
and Prompt areas at the

iJf: N.:ift"?'>''):~,,';'.t:'' ~;""V.... ~ I 'tf;:="'B!Z~ ..-t~ 77:. "f"'-~>'<:;

v,..,..~"""'. ".~.~ry;~'\?~

~v~~ ,~,!!ch prop~rti}~~{: ?J:~<~'\~~~'"'' ,,~,~~>'t: ~~"~>;

~ '-'''-"'



- Destination Element(s)

"' Select

r-" '-'-',




Change Current Selection

oElement(s) modified,


IClick Here to make the current selection the "Source" Element.

I:::' _~B

The Match Properties

Program is simple and
allow the user to make
Y,~, I ~ Default
,,,-II ~ I DISl]J~12I..iz
elements in the Active
Model Reference by
selecting a source element and then using its properties to change the
selected destination elements while using a modeless dialog box.


The next form we will import into
frmAlignTextfrm file. This form
allows the user to perform text
I~; ' I 0
1.--,-0-,'- - - - - operations on selected text in
MicroStation. Since we want to
allow the user to select a point to
align to, the form needs to be
Distribute Vertically
displayed as modeless. This
calculations and movmg text
elements based on those calculations.


Desired Functionality

Selected Text can be aligned Horizontally to the selected or

entered "X" value.

Text can be aligned Left, Center, or Right.

Text can be distributed evenly vertically so equal spacing exists

between each text element.


Only Text elements can be used, not Text Nodes.

I Providing User Feedback and Information I


Frames, command buttons, labels, and text boxes are used in this
project. Once again, you can import the fo rm from the CD
accompanying this book, but we will discuss building this form as
though we were starting with nothing.

Control Placement
Place the controls as shown. The Base Point frame and Horizontal
Alignment frames contain their respective controls and the "Distribute
Vertically" button is by itself. If a "Distribute Horizontally" button
existed, we would place both "Distribute" buttons in their own frame.
After placing the controls, change captions and text properties as shown

Control Names

















btnDistribute Vert

Pick Button
The code in the "Pick" button's click event allows the user to select a
point in MicroStation. The selected point's X and Y components then
display in the text boxes.
Pr i vate Sub bt nPi ckBasePo in t Click()
Di m myCIO As Cad l nputO ueue
Di m myCIM As Cad l nputMessage
Set myCIO = CadlnputOueue
Set myCIM = myC IO. Getlnput(msdCadlnputTypeDataPoint . _
msdCad l nputTypeReset)


I Chapter 17: Interactive Modification I

Select Case myCIM.InputType
Cas e ms dCa d I nputT yp eData Po i nt
pt3BasePoirt = myC IM.Point
pt3BasePoint . X
txtY.Text = pt3BasePoint.Y
Exit Do
Case msdCa d InputTypeReset
Exit Do
End Se l ect
End Sub

We use the CadInputQueue to capture the selection of a point in

MicroStation. When we initialize the CadInputMessage we specify that
we are only looking for datapoints and reset inputs. The Do ... Loo p is
designed as an eternal loop. This means that without explicitly exiting,
the loop continues forever. We use the Do ... Loop code because we can
use an Ex i t Do command that gets us out of the loop whenever we wish.

X and Y TextBoxes
The X and Y TextBoxes are populated with values from points selected
by the user through the Pick Button just discussed. In addition to
picking the point, we want to allow the user to hand-enter X and Y
values. Picking points is nice because we know that the user cannot
select an invalid point in MicroStation. Allowing data entry can cause
problems if we are not careful. What happens, for instance, if the user
enters "somewhere around 4.5" in the TextBox? This entry would be far
from the numeric value we are counting on. One way to limit the user's
entry in these text boxes is to make use of the KeyPress event.
Priva t e Sub txtX_KeyPress(ByVa l KeyAsc ii As MSFor ms . Ret urn I ntege r )
Select Case KeyAsci i
Case Asc( "O" ) To Asc( "9" )
Case Asc( " . " )
If I nStr ( l. txtX .T ext . "." ) > 0 Then
KeyAscii = 0
End If
Ca se Else
Ke yAscii
End Se l ect
En d Sub

I Providing User Feedback and Information I


Pr i vate Sub tx t Y_Key Pr ess( ByVal KeyA sci i As MSForms . ReturnInteger)

Select Case KeyAscii
Case Asc( "O" ) To Asc( "9" )
Case Asc( "." )
If InStr(l . txtY .T ext . "." ) > 0 Then
KeyAscii = 0
End If
Case Else
End Select
End Sub

The KeyPress event tells us the ASCII character code of the keyboard
character that was pressed. If we change the Key Ascii parameter to a
value of zero (0), it is as though the key was never pressed. So, we look at
the KeyAscii parameter and ask ourselves the following questions with
the following results:
[8 Is the Key Ascii between the numbers 0 to 9? If so, do nothing.

Always allow numbers 0 through 9 to be entered.

[8 Is the Decimal key pressed? If so, look to see if a decimal is

already in the TextBox. If a decimal is already in the TextBox,

set KeyAscii to zero. Otherwise, do nothing and allow the
decimal to be entered.
[8 Case Else (if any other key is pressed), set Key Ascii to zero as

though the key was not pressed in the first place.

It should be noted that this code only keeps numeric values from being
entered from the keyboard. It does not prohibit the user from pasting an
invalid entry into the TextBox from the Windows clipboard.

Align Left, Center, and Right

The best way to deal with these three alignment methods is one at a
time. We have three buttons. We could place code in each of the click
events of these buttons to perform the specific type of alignment
requested, but this would create a lot of redundant code. We will create a
function to take care of all horizontal alignments and provide for a
parameter to specify which alignment is to be performed.


I Chapter 17: Interactive Modification I

The "Alignment Mode" parameter could be defined as a string and we
could use "LEFT", "RIGHT", or "CENTER" as parameter values. This
works. There is a better way, though.
To get input from the Cad Input Queue, we can specify which types of
input we want by the using an enumeration, which is a list of constants
grouped together that usually refer to a specific method or property. We
will create our own enumeration to deal with alignments. In the General
Declarations area of this form, we declare this enumeration:
Enu m AlignMode
msvbaA l ignModeCenter
msvbaAlignModeRight = 3
End Enum

Now when we declare our procedure to align the selected text, it looks
like this:
Sub Ali gnSelected COptiona l ElemAlignMode As AlignM ode = _

We declare the parameter as optional so we can specify "Left" as the

default alignment. When we are using the AlignSelected method in our
code, we see this:

Pcivate sub btnAlignCentec_Cli c k ()

AlignSelecte d
End AlignSelected(lEleJlIl 0 i0.~y'~~~l i~ ~~~~~'~~i~i

Pci vate Su b btnA 0

msvbaAli gnModeLeft
msvbaAlignModeR ight


(ByVal But

Enumerations help us make sure that the param eter we are providing is
legitimate and make it easier to program because we are shown our
options for the parameter.
One additional declaration needs to be added to the General
Declarations area of our form:
Dim pt3BasePoint As Point3d

When the user selects a point, we use this variable to store the selection.
Here is the code that actually aligns the text left, center, or right:
Sub AlignSelectedCOptional ElemAlignMode As AlignMode = _

I Providing User Feedback and Information I


Dim myElemE nu m As Ele mentEnumerat or

Di m my Elem As Elemen t
Di m OriginPt As Point3d
Diw ~yTextElem As TextElement
Set myElemEnum = ActiveModelReference . GetSelectedE~ements
While myElemEnum.MoveNext
Set myElem = myElemEnum.Current
Select Case myEl em. Type
Case msdElementTypeText
Set myTextElew = myElem
Select Case ElemAlignMode
Case msvbaA li gnModeLeft
myTextElem . Move Point3dFromXY(pt3BasePoint.X

myText Elem.Boundary.Low . X. 0)
my Tex t Ele m.Rew r i t e
Case msvbaA li gnModeRight
myTextElem . Move Point3dFromXY(pt3BasePoint . X

myTextE l em . Boundary . High.X. 0)

myTextElem.Rewr it e
Case msvbaAl i gnModeCenter
my TextElem .Move Point3dFromX Y( pt3Base Po int.X

yTextElem . Boundary .L ow . X - _
(myTextElem . Boundary . High.X - _
myTextElem.Bou nd ary . Low.X) / 2. 0)
End Select
End Select
End Sub

When we begin executing this code, we know that in addition to

selecting text elements, the user may have selected other types of
elements. Since we only want to work with text elements, we use a
Se 1ect Ca se statement to parse out the text elements from the others.
Next, we use another Sel ec t Case statement to move the text element
based on the type of alignment specified and the X value of the base
point. We rewrite the text element so the change made is permanent in
the file.
Left, Center, and Right Buttons
When the user clicks the left, center, or right buttons, the click event of
the respective button is triggered. Notice how we use our enumeration
values when calling "AlignS elected':


I Chapter 17: Interactive Modification I

Pri vate Sub btnA 1 i gnLeft_Cl i ck()
A1ignSelected ~svbaA1ignModeLeft
End Sub
Pri vate Sub btnA 1 i gnCenter _C1 i ck()
A1ignSe1ected msvbaA1ignModeCenter
End Sub
Private Sub btnAlignRight_Click()
AlignSe1ected msvbaA1ignModeRight
End Sub

Degrees of Complexity
There are three degrees of complexity in this program. The degrees and
their tasks are as follows:

Pick a point, place X and Y components into TextBoxes.


Align selected Text Elements Left, Center, or Right.


Vertically Distribute selected Text Elements even ly.

We have already discussed the Low and Medium complexity tasks. It is

now time for the High complexity task. This task is not highly complex
because it is highly difficult. It simply requires more components for
everything to work correctly.

Vertically Distribute Selected Text Evenly

Here are four text elements in the file
1. Note1.
chapter17_AlignText.dgn. This file is on the
2. Note 2. '
CD accompanying this book. The text
alignment code we have already discussed
. 3 .Note 3. :.
- ---, ---"_ __
takes care of the horizontal alignment. Now
4. Note 4. .'.
we are faced with the task of making the text
look nice vertically. The spacing between
Note 1 and Note2 is tight, whereas the spacing between Note2 and
Note3 is loose (as it is between Note 3 and Note 4). We want the spacing
between each of these text elements to be the same. It is a simple task but
a number of considerations must be made before continuing.


I Providing User Feedback and Information I



After the user selects the text, we want even spacing between the top
and bottom elements without those elements moving.

2 On the screen it is readily apparent which element is on top and

which is on the bottom. But when we look at the selection in code,
we do not know which element is on top and which is on the

On the screen we can see the proper order. But when we look at the
selection in code, we do not know the top-down order of the text

We will create distinct functions to accomplish each of the following

[B Discover the minimum and maximum points of the selected

text elements.
[B Determine the vertical order in which the text elements appear.
[B Determine the number of selected text elements.

After we have these functions in place, we will be able to use them in

distributing the selected text elements.
Fun ction GetMinMaxY (E l emType As Long, ElementsIn As Variant)
As Point3d()
Dim I As Long
Dim pt3Sta rtPoint As Point3d
Dim pt3E ndPo i nt As Poi nt3d
Dim myTextElem As TextElement
Dim boolPointsSet As Boolean
boolPointsSet = False
For I = LBound(ElementsIn) To UBound(E l ements I n)
Set myE l em = El ements I n(I)
Select Case myEl em . Type
Case msdElementTypeText
Set myTextElem = myElem
If boo l PointsSet = False Then
pt3StartPo i nt = myTextElem.Boundary . High
pt3EndPo i nt = my TextElem . Boundary . High
boolPointsSet = True
End If
If myTextElem . Boundary.High.Y >
pt3StartPoint . Y Then


I Chapter 17: Interactive Modification I

pt3StartPoi nt .Y = myTextElem . Boundary.H i gh.Y
End If
If myTextElem.BoJndary.High.Y < pt3EndPoint. v Then
pt3EndPoint . Y = myTextElem.BoJndary.Hig~ . Y
End If
End Select
Next I
Dim pt3PointsCO To 1) As Point3d
pt3PointsCO) = pt3StartPoint
pt3Points(1) = pt3EndPoin t
GetM in MaxY
pt3Poi nts
En d Function

We have created this function to allow for future use and expansion with
other types of Elements. We ask for the element type and the elements to
be considered. From these parameters, we discover the Min and Max
values and return them as an array of Point3d types.
The next task is to sort the elements vertically. This is accomplished by
providing the type of element we want to look at and the elements to be
considered. We return the elements in their vertically sorted state as an
array of elements.
Function SortElementsVertically CElemType As long, _
Elements I n As Variant) As ElementC)
Dim I As l ong
Dim boolMadeChange As Boolean
Dim lngE l emIDC) As Dlong
Dim pt3BoundPtC) As Point3d
Dim myTextElem As TextElement
Dim myTextElem2 As TextElement
Dim tmpID As Dlong
Dim tmpPt As Point3d
ReDim lngElemIDCO) As Dlong
ReDim pt3BoundPtCO) As Point3d
For I = lBoundCElementsIn) To UBoundCElementsIn)
Selec t Case ElemType
Case msdElementTypeText
If El ements I nCI) .Type = msdElementTypeText Then
Set myTextElem = ElementsInCI)
lngElemID CUBoundClng ElemID)) = myTextElem . ID

I Providing User Feedback and Information I


pt3BoundPt( UBound(pt3BoundPt)) = _
my Tex tEl em.Bo un dary . Hi gh
ReDim Pr eserve ln gElemID ( UBound ( lngElemID ) + 1) As
ReDim Preserve


1) As

Poi nt3d
End If
End Select
Next I
ReDim Preserve lngE l emID(UBound(lngElem I D) - 1) As Dlong
boolMadeChange = True
Whi l e boo lM adeChange = Tr ue
boolMadeCha nge = False
For I = lBound(lngE l emID) To UBound(lngE l emID) - 1
If pt3BoundPt(I + l).Y > pt3BoundPt( I ) . Y Then
tmpID = lngElemID(I)
tmpPt = pt3BoundPt(I)
lngElemID(I) = lngE l emID(I + 1 )
pt3BoundPt(I) = pt3BoundPt(I + 1)
l ngElemID(I + 1) = tmpID
pt3BoundPt(I + 1) = tmpPt
boolMadeChange = True
End If
Next I
Dim Elemsln() As El ement
ReDim ElemsIn(O To UBound(lngElemID))
For I = lBound(lngElemID) To UBound(lngElemID)
Set Elemsln(I)

ActiveDesignFile.GetEleme ntByID(lngElemID ( I ))

Next I
SortE l ementsVertically
End Function

Elems I n

There is a lot of code to look at here. After we divide it into four little
chunks, it becomes easier to understand.

Variabl e Declaration

I As l ong
boolMadeChange As Boolean
lngElemID() As Dl ong
pt3BoundPt () As Poi nt3d
myTextE l em As TextEleme nt


I Chapter 17: Interactive Modification I

Dim myTex t Elem2 As TextElem ent
Dim t mpI D As DLong
Dim tmpPt As Point3d

Two variables are declared as dynamic arrays (by using the empty
parenthesis). Dynamic arrays can change in size without losing their
values. Other variables are declared as well.

Dynamic Variable Array Population in

Preparation for Bubble Sort
ReO i m lng ElemI D(O) As DLong
ReO i m pt3BoundPt(O) As Point3d
For I = LBound( El ementsIn) To UBou nd(E l ementsIn)
Select Case ElemTyp e
Case msdElementTypeText
If Ele mentsIn(I).Type = msdElementTypeText Then
Set myTextElem = Elem entsIn(I)
lngElemID(UBound(lngElemID)) = myTextElem.ID
myTextElem . Boundary . High
ReD i m Preserve
lngElemID(UBound(lngElemID) + 1) As DLong
ReDim Preserve
pt3BoundPt(UBound(pt3BoundPt)+ 1) As
Poi nt3d
End If
End Select
Next I
ReDim Preserve lngElemID(UBound( l ngElem I D) - 1) As DLong
ReDim Preserve pt3BoundPt(UBound(pt3BoundPt) - 1) As Po i nt3 d

We look at each element provided to us in the ElementsIn parameter to

see if it is of the correct type (in our example we are looking for text
elements). If it is, we get the text element's ID property and put it in one
of the dynamic array variables and get the element's Boundary.High
point and put it in the other dynamic array variable. We then re-declare
the dynamic array variables with the Preserve keyword so we don't lose
the previous values. After we have looked at each of the elements
selected, we re-declare the dynamic Array variables decreasing the size
of each by 1. Throughout the code above, we add one to the size of the
array after populating the upperbound variables so we need to take one

I Providing User Feedback and Information I


off after we have finished. Otherwise, we would h ave an array element

with nothing in it.

Bubble Sorting
boolMadeChange = True
While boolMadeChange = True
boolMadeChange = False
For I = LBound (lngE lemID ) To UBound(lng Elem ID) - 1
If pt3BoundPt(I + 1).Y > pt3Bou'ldPt(I).Y Then
tmpID = lngElem ID(I )
tmpPt = pt3BoundPt(I)
lngElemID(I) = lngElemID (I + 1)
pt3BoundPt(I) = pt3BoundPt(I + 1)
lngElemID(I + 1) = tmpID
pt3 Boun dP t(I + 1 ) = tmpPt
bool Mad eChange = Tr ue
End If
Next I

We have discussed bubble sorting previously. We are looking at the Y

values of two points. We want the highest Y values to be at the top of the
list. So, if a Y value lower down on the list is higher than the Y value just
above it in the list, we switch the two. When a switch is made we set the
variable boo lMadeChange to True. This means we will run through the
array again. We continue running through the array until a switch is not
made. When we find we have not made a switch, the sorting is complete.

Setting the Return Value

Dim Elemsln () As Element
ReDim Elems ln (O To UBound(lngElemID) )
For I = LBound(lngElemID) To UBound(lngElemID)
Set Elemsln(I) = ActiveDesignFile.GetElementByID(lngElemID ( I))

Next I


We are returning an array of elements for this function. When we did

our bubble sort, we swapped the point array values and also the ID array
values along with them to keep the IDs matched with their points. Now,
we use GetE 1ementBy I 0 to get the element back and put the element in
the return value array. We separated the IDs and points from the


I Chapter 17: Interactive Modification I

elements, so we did not need to hold onto large elements as we did our
sorting, only smaller points and ID types.
Determining the numb er of text elements is relatively easy compared
with the last function we just worked with. We get the element type we
want to count, the elements to be counted, and we return the number of
elements matching the type contained in the elements passed in.
Func ti on GetSe l ectedCount ( Elem Ty pe As Long , _
ElementsIn As Variant) As Long
Dim I As Long
For I = LBound(ElementsIn) To UBound(Elements I n)
If Ele mentsIn(I).Type = ElemType Then
GetSelectedCount = GetSelectedCount + 1
End If
Next I
End Function

The previous three functions are written so that they can be expanded in
the future. We do not need to write code right now to accommodate
these potential future needs.
Now we need to make use of these functions in a single procedure to
accomplish our "Distribution" task.
Private Sub btnDistributeVert_Click()
Dim MyPts As Variant
Dim pt 3Sta rtP oi nt As Poi nt3d
Dim pt3EndPoint As Point3d
Dim myElemEnum As ElementEnumerator
Dim myElem As Element
Dim myElems ( ) As Element
Dim I As Long
Di m myTextElem As TextElement
Dim lngSpaces As Long
Dim dblSpacePerEle ment As Double
Di m sortElems() As El ement
Set myEle mEnum = ActiveModelReference.GetSelectedElements
myEl ems - myElemEnum . BuildArrayFromContents
MyPts = GetMinMaxY(msdElementTypeText , myEl ems )
lngSpaces = Ge t Se l ectedCount (msdElementTypeText , myEl ems ) - 1
If lngSpaces > 1 Then
dblSpacePerElement = (MyPts (O) . Y - MyP ts(l ) .Y ) / l ngSpace s

I Providing User Feedback and Information I

so r tElems


Sor t Elem en t sV e r ti cal l y(msd Ele me ntTy peTe xt , myE l ems)

For I = LBound(s ort Elems ) To UBound( sor tEl ems )

Set myTextElem = sortElems(I)
myTextElem . Move Po'rt3dFromXY(O, tvlyPts(O).Y dblSpacePerElement * I - myTextElem . Boundary.High.Y)

myTextElem . Rewrite
Next I
End If
End Sub

A close look at the above code reveals the use of the three functions we
just finished discussing.
GetMi nM axY, GetSe 1ectedCount, and
So r tE 1emen ts Vert i ca11y are used. After we have sorted the elements
vertically, we move them so that they are spaced evenly.

Providing User Feedback and Information

Let's provide similar functionality to our previous program by supplying
the user feedback and information.
Private Sub UserForm_MouseMove(ByVal Button As Integer , _
ByVal Sh i ft As Integer, ByVa l X As Single , ByVal Y As Sing l e)
ShowStatus ""
ShowPrompt ""
ShowCommand Me.Caption
End Sub
Private Sub btnDistributeVert_MouseMove(ByVa l Button As Integer,
ByVa l Shift As Integer , ByVal X As Single, _
ByVa l Y As Single)
ShowStatus ""
ShowPrompt "Distr i bute Text Vert i ca ll y"
ShowCommand Me . Caption
End Sub
Private Sub fraHoriAlign_MouseMove(ByVal Button As I nteger , _
ByVal Sh if t As I nteger, ByVal X As Single , ByVal Y As Sing l e)
ShowS t atu s ""
ShowPrompt ""
ShowCommand Me.Caption
End Sub


I Chapter 17: Interactive Modification I

Pr i vate Su b f r aBa s ePoi nt_M ou seM ove (By Val Bu tto n As In te ger , _
ByVal Shift As In t ege r, ByVal X As Sing l e , By Val Y As Si ng l e)
ShowStatus ""
ShowPrompt ""
S~owCommand Me.Caption
End Sub
Private Sub btnPickBasePoint_MouseMove(ByVal Button As Integer, _
ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
ShowStatus ""
ShowPrompt "Select Base Point : "
ShowCommand Me . Caption
End Sub
Private Sub btnAlignR i ght_M ouseMove ( ByVa l Button As Integ e r, _
ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
ShowStatus ""
ShowPrompt "Align Selected Text Right as Base Point "
ShowCommand Me.Caption
End Sub
Pr i vate Sub btnAlig nLeft_MouseMove ( ByVal Button As Integer, _
ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
ShowStatus ""
ShowPrompt "Align Selected Text Left at Base Point "
ShowCommand Me.Caption
End Sub
Private Sub btnAl i gnCenter_MouseMove(ByVal Button As Integer , _
ByVal Shift As I nteger, ByVa l X As Sing l e, ByVal Y As Single)
ShowS t atus ""
ShowPrompt "Align Se l ected Text Cen t er at Base Point "
ShowCommand Me.Caption
En d Sub

At this point, this code spaces text evenly if the text elements are the
same height. The upper-left corner of each element is what we are using
to space these text elements. If one text element is larger than the others,
it could run into the text below it because we are only considering the
spacing between the top -left corners relative to each other, not the topleft corner of one text element with the bottom-left corner of the one
above it. We will leave the expansion of this macro to accommodate the
text height to the reader of this book.

I Providing User Feedback and Information I


The frmExportElements.frm User
Form accomplishes a simple task:
it exports elements on specific
levels to a new design file.

Levels ..

The task for this project is simple.

The interface reflects this. We
need to allow the user to select
any number of levels, enter a file
name for the new file to be
created, and then click on the
Export button.

Control Names
IB 1st Levels
IB txtFileName

.. . . . .

... ..

File Name : .


!:: :

IB btnExport

Control Properties
IB IstLevels property MultiSelect is set to 2 fmMultiSelectExtended
IB IstLevels property ListStyle property is set to 1 fmListStyleOption
When this program begins executing, we need to get the names of all
levels of the active design file into the list box. This is very easy to do.
Because we are not given level names in alphabetical order, we will
employ a bubble sort to put them into the list box in alphabetical order.
Private Sub UserForm_In i t i alize()
Dim my Level As Level
Dim LevelNames() As String
Dim MadeC ange As Boolean
Dim tmpName As String
Dim I As Long
ReDim LevelNames(O)
For Each myLevel In ActiveDesignFile.Levels
LevelNames(UBound(LevelNames)) = myLevel . Name


I Chapter 17: Interactive Modification I

ReDim Preserve LevelNames (UBo und(LevelNames) + 1)
ReDim Preserve LevelNames(UBound(LevelNames) - 1)
MadeChange = True
While MadeChange = True
Ma deCh ang e = False
For I = LBound(Leve l Na mes ) To UBound (L eve l Na mes )
If StrComp ( LevelNames(I ) . LevelNames ( I + 1))
t mp Name = Le ve lNam es( I )
LevelNames(I ) = LevelName s( I + 1 )
LevelNames(I + 1 ) = tmpName
MadeChange = True
End If
Next I
For I = LBound(LevelNames ) To UBound( LevelNames)
lstLevels . AddItem LevelNames(I)
End Sub

1 The n

When comparing numeric values, we can use greater than ( and less
than ) comparisons. You can also do this with text but the results are
not always what we expect. So, we employ the standard VBA StrComp
function to compare two strings.
Private Sub btnExport_C l ick()
Dim myFileName As String
Dim myNewFile As DesignFile
Dim I As Long
Dim ElemID As DLong
Dim myE l ems() As Element
Dim myElemEnum As ElementEnumerator
Dim my Level As Level
myFileName = txtFileName.Text
If ActiveModelReference.Is3D Then
CreateDesignFile "seed3d ". myFileName. False
CreateDesignFile "seed2d " . myFileName. ralse
End If
Set myNewFile = OpenDesignFileForProgram(myFileName)
Dim mySelCriteria As New ElementScanCriteria
mySelCriteria . ExcludeAllLevels

I Provi ding User Feedback and Information I


For I = 1 To lst Leve l s. Li stCount

If lstLevels.Selected ( I - 1) Then
mySelCriteria.Inc l udeLevel _
ActiveModelRefererce.Levels(lstLevels.List (: - 1))
End If
Next I
Set myElemEnum = ActiveModelReference.Scan(mySelCriteria)
myEl ems = myElemEnum . BuildArrayFromContents
For I = LBound(myElems) To UBound(myElems)
myNewFi 1 e. Model s (1). CopyEl eme nt myEl ems (I)
Next I
myNewF ile. Save
MsgBox UBound(myElems) + 1 & " elements created in file " & vbCr &
myF il eName, vbIn f or ma t ion , Me . Caption
End Sub

We create a new design file using the CreateDesi gnFile method. We

then open that file using OpenDes i gnFi 1eForPr ogra m. This function
allows us to open and work with a file without the user seeing the file in
the MicroStation interface.
Next, we create an ElementScanCriteria object, so we only look for
elements on the selected levels. We ExcludeAllLevels and then begin
adding in the ones that are selected in the IstLevels ListBox. As we
include the levels in our scan criteria, we could add them to the new
design file we just finished creating. This is not necessary as the levels
will be created when we copy elements to the new file . However, if we
select a level in our interface and it does not h ave any elements on it, the
new design file will not have that level.

Providing User Feedback and Information

The code imported with the form contains these events:
Pri vat e Sub Use r Fo rm_M ouse Move(ByVal But t on As Integer, _
By Val Shi ft As Integ er , ByVa l X As Sin gl e, _
ByVa l Y As Single)
S owPrompt
ShowCo mmand
End Sub
Pr i vate Sub lstLevels_MouseMove(ByVal Button As Integer , _
ByVal Shift As Integer , ByVal X As Si ngle , ByVal Y As Si ngle)


I Chapter 17: Interactive Modification I

ShowPrompt nn
ShowStatus nn
End Sub
Private Sub txtFileName_MouseMove(ByVal Button As Integer, _
ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
ShowPrompt nn
ShowStatus nn
End Sub
Private Sub btnExport_MouseMove(ByVal Button As Integer, _
ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
Sho wPrompt n n
ShowSta tu s nn
ShowCo mma nd
End Sub

Nothing is being sent to the prompt, status, or command areas of the

status bar in MicroStation. As we consider the fun ctionality of this
program, what should these values be? We will let each individual
answer for themselves.
Here is the "VBA Files From Levels" form working. This program does
not need to be modeless because this program does not require user
interaction inside MicroStation while the form is being displayed, but it
still needs to be shown from a procedure in a code module.
Sub TestFilesF romL eve l s()
fr mExportE lements .Show
End Sub

I Providing User Feedback and Information I

The above code is placed in a code
module so users can execute this



Here is the interface in use. Any

number of levels can be selected
and exported to the file name

File Name

I c: \~licroStation



The frmDFAV.frm program is used

to display attachments of design files.

. . .. .. . .. . . . . . . . . . ... . ... ...

..... .
.. . .. .. .. .. .. .. .. . .. .. .. .. ....
. . . .. ...
... . -..
.. . . . .. . . . ...
. . . .. ... .... . .-,..
. . . . ...
.. .. . . .. .,. ..
. .. . ....
. .... ...
' ,
. . ...
. .. . . . . . .. . . . . . .. . . . ...... . . . . . .. . . . . .
Oesign F~es in Folder 0 Files Found.


Include Sub-Folders



. .. .. . . . .. . .

The user selects a folder by clicking on the Browse button. When

selected, the selected folder is browsed for a MicroStation .dgn file. Each
design file found is added to the File listbox. When the user clicks on a
file in the File listbox, we look into the file for any attachments. All
attachments found are added to the Attachments listbox.


I Chapter 17: Interactive Modification I

Control Names








We are going to add a few

elements in this program we
have not used thus far. We
could have the user type in a
folder. Let's have them select it
instead. Here's the Folder
Selection dialog box we want:

Select Root Folder:


My Documents

$ .S iifill "I.IIIM
. $ .....


local Disk (C:)

Key largo (D:)

!I" ~ projects on 'Puny' (V: )

8:J"'~ Puny2Rem on 'Puny2' (Y:)

8:J..:!! Store on 'Dev' (Z:)

$ 10 Shared Documents

How do we get it? We use the

Windows API.


IQ Administrator's Documents

8:J .. !ClJ

Guest's Documents

8:J . 1O jerryw's Documents

, 8:J.jQ jkw's Documents


l\I,:Ih~lnrl.. PI~rp<:

The other thing we want to do

is store the settings of our
application so the next time
we use it we can recall those
settings. These settings are stored in the Windows registry.


Here is what this looks like:





d CJ

L 6



VBA File Attachment Viewer


VBA File Reference Viewer

!I CJ XM_Addln_JW

,?'!i !




i '] i~'~i~d~"S'~bs

'I fiii"lPath
,..y ,) CJ

; Type



(value not set)




C:\Microstation VBA

~ly Computer\HKEY _CURRENT _USER\Software\VB and VBA Program Settings\VBA File Attachment Viewer\Defaults

Program Components

Retrieve Settings from Registry on Form Initialize


Allow User to Select Root Folder


Search in Folder for .dgn files


Search in Folder's Sub Folders for .dgn files


When user clicks on file, display Attachments

I Provid ing User Feedback and Information I


[B When Program Closes, save settings to Registry

Retrieve Settings from Registry on Form

Pr i vate Sub UserForm_ Init i al i ze()
t xt Fol de r.Text = Ge t Setting ( "VBA Fil e Attach ment Vi ewe r", _
"Defa ults", "Pa t h")
I f txtFolder. Text <> "" The n
chkSubFolders . Value = GetSett i ng( "VBA Fi l e Attachment
Viewer", _"Defaults", "Include Subs")
PopulateFileL i st
End If
End Sub

When the form is initialized we look for the saved settings and put them
If we find a "path" saved in the registry, we set the checkbox value and
populate the file list using our PopulateFileList method.

Allow User to Select Root Folder

We use the Windows API to allow the user to select a folder. In the
general declarations area we declare the following:
Pr ivate Ty pe Browse Inf o
hWndOw ne r As Long
pidlRoot As Long
sDisplayName As String
sTitle As String
ulFlags As Long
1 pfn As Long
1 Param As Long
i Image As Long
End Type
Private Declare Function SHBrowseForFolder Lib "shel132 . dll "
(bBrowse As BrowseInfo) As Long
Private Declare Function SHGetPathFromIDList Lib "shel132 . dll "
(ByVal lItem As Long , ByVal sDir As String ) As Lo ng
Private Const Bif_ReturnOnlyFSDirs = 1
Private Const Bif_DontGoBelowDomain = 2
Pr i vate Const Bif_EditBox = 16


I Chapter 17: Interactive Modification I


Cons t

Bif _NewDialogSty l e = 64
Bif - UseNewui = 80
Bi f _BrowseForComputer = 4096
Bif_BrowseForPrinter = 8192
Bif - BrowseIncl udeFi 1es = 16384

After the constants, types, and functions are declared, we can use them
in our code. Here is the click event of the Browse button.
Private Sub btnBrowse_Cl i ck()
Dim MyBI As Browselnfo
Dim FList As Long
Dim DirName As String
Dim SelFolder As Long
DirName = Space(255)
MyB I.sTitle = "Select Root Folder: "
MyBI.sDisplayName = Space(255)
MyBI.ulFlags = Bif_Retu r nOnlyFSDirs
FList = SHBrowseForFolder(MyBI)
SelFolder = SHGetPathFromIDList(FList, DirName)
DirName = Left (D irName, InStr(l , DirName , Chr(O)) If DirName <> "" Then
txtFol der . Text
End If
PopulateF il eL i st
End Sub


PopulateFileList is a procedure that takes the folder in the TextBox and

begins looking for .dgn files.
Sub PopulateFileList ()
lstFiles . Clear
Dim myFolder As Folder
Dim myFSO As New FileSystemObject
If txt Folder.Text <> "" Then
Set myFo l der = myFSO .G etFolder(txtFolder.Text )
FilesInFolder myFolder , "dgn ", chkSubFolders , lstFiles
En d If
lblFiles . Caption = "Design Files in Folder - " &
lstFiles.ListCount & " Files Found. "
End Sub

I Providing User Feedback and Information I


Popu l ate Fil eLi st uses the FileSystemObject. This is a Windows

component we need to add to our references before we can use it.
In VBA, go to Tools> References to display the References dialog box:

available References:


~J Visual Basic For Applications

[l,iJ Bentley ~licroStation DGN 8,
; " OLE Automation


a Object Libr ary


,_~ lAS RADIUS Protocol! ,0 Type Library

[, SolidWorks OLE Automation! ,0 Type Library
[J In software IPWorks! VS FileMailer Control
C In software IPWorks I VS FTP Control
[J I n software IPWorks! VS TFTP Control
[J :.) VideoSoft VSFlexGrid 7,0 (Light)
[J AboutDlg ! ,0 Type Library
ri ArlJrlnmmnn 1. n Tvnp. 1ihrrlrv



.S 'L.........x.".~"..._......._....... _1
Microso ft Scripting Runt ime

C: \ WINDOWS\System32\scrrun ,dll



When selected, the Microsoft Scripting Runtime provides an easy way to

find files and traverse folders and sub-folders.
The POpu1ate Fi 1eLi s t method utilizes our Fi 1esIn Fold e r method.
The Fi 1e sIn Fold e r method is a recursive method, which means it calls
itself. Here it is.
Su b 'FileslnFolder (Folde rI n As Fo l der . Fi le Ext ension As String . _
IncludeSubs As Boolean. _
ListToPopulate As ListBox)
Dim myFile As File
For Each myFile In FolderIn . Files
Select Case UCase(Right(myFile.Name. 3) )
Case UCase(FileExtension )
ListToPopulate.AddItem myFile.Path
End Select
If IncludeSubs = True Then
Dim subFolder As Folder
For Each subFolder In FolderIn . SubFolders
FilesInFolder subFolder. FileExtension. _
IncludeSubs. ListToPopulate


I Chapter 17: Inte ra ctive Mod ification I

End If
En d Sub

The first thing Fi 1e sIn Fold e r does is looks for files in the "Folder In"
parameter folder. We look at the file extension to see if it matches the
FileExtension parameter. If it does, we add it to the list. After all files
have been reviewed, we check if we should also look at sub-folders. If we
are not to look at sub-folders, we complete the function and move on. If
we are to look at sub-folders, we begin a For ... Each statement to look at
each of the sub-folders in the current folder.
For each sub-folder we find using Fi 1es InFo 1der, we call Fi 1es In Fo 1de r
using the sub-folder as the FolderIn parameter. This is why it is
recursive. The procedure calls itself. When dealing with recursive
procedures or functions, we need to be sure there is a way to finish
execution. Otherwise, we could end up with hundreds or thousands of
procedures in the call stack with no ending to the execution.
Displaying Attachments

When the user selects a file in the Files listbox we get the attachments of
the selected file and display them in the Attachments ListBox.
Pri va t e Sub l s t Fi les_Cl i ck()
l s t Attach ments . Cl ea r
I f l stF il es .T ext <> "" Then
Dim my DesF il e As DesignF il e
Set myDesFile

OpenDesignFileForProgra m( l stFiles.Text. True)

Dim myAttachment As Attachment

For Each myAttachment In _
myDesFile.Defau l tModelReference . Attachment s
lstA tt achments . AddItem myA tt achment . At tachName
myDes File . Close
End If
lblAttach ments . Caption = "Attachments in Se l ected File - " &
lstAttachments . ListCount & " Attachments Found ."
End Sub

Before looking at the attachmenls of a file, we need to open the file. Vie
have two ways to open the file: open it for the user to see and work with
or open it so the user does not see the file but our program can work
with it. In this example we are using OpenOesi gnFi 1eForProgram to open

I Interacting with MOL Applications I


the file because we do not want to open the file in MicroStation's editing
window each time a file is selected.
After the user has reviewed the attachments of the desired files, the user
closes the program. When a program is being closed, we want to store
the settings so the next time the program is executed we begin with
those settings in place.
Private Sub UserForm_OueryClose(Cancel As Integer, _
CloseMode As Integer)
SaveSetting "VBA File Attachment Viewer", "Defaults", _
"Path ", txt Fold er. Text
SaveSetting "VBA File Attachment Viewer ", "Defaults " ,
" Inc l ude Subs ", _ chkSubFolders.Value
End Sub

We are saving two settings to the Windows registry. These are the
settings read by the initialize event of the form.



Let's record a macro where we import an image using the MicroStation

File > Import > Image.
Files\Bentley\MicroStation \bentleyb.jpg and place it in MicroStation.
After the image is placed, stop recording the macro.
Before we look at the macro that was created, we should discuss the two
methods of interacting with MDL Applications. The fi rst method looks
like our previous use of the "Send Command" method where, after the
command begins, we can supply points or other input as needed. The
second method, using dialog boxes, requires a class module that handles
the events of the dialog box.
The Import Image MDL application makes use of a dialog box so a new
class is created that is used with the recorded macro. Let's begin by
looking at the recorded macro. After we do, this we will look at the class
module created by the macro recorder.
Sub Macro5 ()
Dim startPo i nt As Po i nt3d
Dim point As Point3d , point2 As Point3d
Dim lngTemp As Long


I Chapter 17: Inte ractive Mod ification I

Dim modalHan dl er As New Macr o5M oda l Handl e r
AddModa l Dia l ogE ven t sHa ndler modal Handler
The following statement opens modal dialog "Select Image File "
Start a command
CadInputOueue.SendCommand "MOL LOAD PLAIMAGE"
Coordinates are in master units
-6. 270784
startPoint . Y 23.160278
startpo i nt . Z 0#
Send points to simula t e a down-drag-up action
poi nt . X startpo i nt . X
po in t . Y st artp oi nt.Y
startp oint.Z
poin t2.X
poin t . X + 2. 93 8037
poi nt 2 . Y poi nt. Y 2 . 980928
po i nt 2.Z
point. Z
CadI npu tO ueue . Se ndDr agP oi nts poi nt . po i nt 2. 1
Remove ModalDial ogE ven tsHand l er moda l Hand l er
Co mmand Sta te. St art Oefaul tCo mmand
End Su b

The class module created is named Macro5ModalHandler. After

declaring a few variables, the macro recorder declares a variable as a
"MacroSModaIHandler" and adds the events of this class. Next, the
"MDL LOAD PLAIMAGE" command is sent. This displays the dialog
box. After the dialog box is shown, the class module handles the entry of
the file path and name and closes the dialog box. When the dialog box
closes, we are back in the Mac r o5 procedure, which places the image in
the design file by 'dragging' points. The event handler is removed and
we finish out the procedure.
Let's take a look at the class module created.
Implements IModalD i alogEvents
Private Sub IModalO i al ogEvents_OnDialogClosed(ByVal _
DialogBoxName As String . ByVal DialogResult As MsdDialogBoxResult)

End Sub

I Interacting with MDL Applications I


Private Sub IModaI Dia logEv ents_OnDialogOpen ed( ByVa l _

Dia lo gBoxNam e As String . DialogResult As MsdDialogBoxResult)
If DialogBoxName

"Select Image File" Then





CadInputOueue.SendCommand _
"MO L COMMAND MGDSHOOK.fileList_setDirectoryCmd " &
"C:\Program Files\Bentley\MicroStation\"
CadInputOueue . SendCommand _
"MOL COMMAND MGDSHOOK.fileList_setFileNameCmd " &
"bentleyb . jpg "
Remove the following l ine to let the user close the di alog box .

Di alogResu l t
End If


' Select Image File

End Sub

Every time the macro Macro5 is run, the same image will be placed in the
same place. Let's make a few modifications to the code we have so we
can create a more flexible and powerful class module that can be used in
future projects.
Here is the code for the new class module. It is named
clslmagelnsertion. We have added two public variables that act as
properties to this class module.
Implements IModalDialogEvents
Pub l ic FilePath As String
Publ i c FileName As String
Pr ivat e Sub IM oda IDi alogEvents_OnD ial ogC l osed(ByVal _
DialogBoxName As String . ByVal DialogResult As _
End Sub
Private Sub IModaIDial ogEvents_OnDialogOpened(ByVal _
DialogBoxName As String . DialogResult As MsdDialogBoxResult)
If DialogBoxName = "Select Image File " Then
CadInputOueue.SendCommand _


I Chapter 17: Interactive Modification I

"MOL COMMAND MGDS HOO K,f i l eLis t_setD i re ct ory Cmd " &
Fi 1ePat h
CadlnputOueue.SendCommand _
"MOL COMMAND MGDSHOOK,fileList setFileNameCmd " &
DialogResult = msdDialogBoxResultOK
End If
End Sub

The path and filename is no longer hard-coded. This means we can use
this class module any time we want to insert an image into a file. This is
how it is used:
Sub Te s t lm ag e ln se rtion ()
Dim po i nt 1 As Point3d, point2 As Po i nt3d
Di m mo dal Ha nd ler As New cl s lma ge l nse r t i on
modalHand l er . FilePath

"C: \ Pr ogram Fi le s \Be nt l ey\ Mi croS t at i on\ "

modalHand l er . Fi l eName = "bent l eyb. j pg"

Add Mo dalDi alogEve nt s Han dle r modalHa nd ler
Ca dlnp utOu eue . Se ndCo mmand "MO L LO AD PLAIM AG E"
point 1.X = 0: poin t 1 . Y = 0: poin t 1.Z = 0
poi nt2 . X = 1: poi nt2 .Y = 1: point 2. Z = 0
CadlnputOueue.S endDragP oints poi nt1, poi nt 2, 1
Remo ve ModalDial ogEvents Han dler mo dal Han dl e r
Co mmandState.Start DefaultCommand
End Sub

Using FilePath and FileName properties for the class module allows the
class module to be used with any file path or name. Previously, the path
and name were hard-coded.

The MicroStation VBA API is powerful. This power allows us to be
creative in how we approach programming tasks. For example, initiating
the "PLACE LINE" command to provide the user with a more
graphically rich experience when selecting two points can be used even
when we are not concerned with drawing a line. Using a modeless form
allows the user complete flexibility in working with the MicroStation
interface while allowing interaction with our own GUI.


Interface Essentials
What is an interface? The term is used in programming to describ e
several different things, so lets explain and define the term.
We have already worked with user forms and controls to create a
graphical user interface, which allows users to interact with controls.
The graphics provide an interface to the code of the program.
Another type of interface allows us to interact with code in a program
but does not have a graphical component. It allows us to interact with
the user's activities in MicroStation. For example, when a user selects an
element in MicroStation, we can capture that activity through the use of
an interface named "ILocateCommandEvents". As the user picks points
in MicroStation, we can capture those points through the use of the
"IPrimitiveCommandEvents" Interface.
User interaction with some dialogs in MicroStation can be evaluated
through the use of the "IModalDialogEvents".
In this chapter:
[B Interface basics
[B Class module review
[B Class module life cycle
[B The dynamics event



I Chapter 18: Interface Essentials I

[B The LocateCriteria object

IPrimitiveCommandEvents interface

[B Optimizing the dynamics event

The ability to capture user interaction in MicroStation is powerful. To
harness this power, we create a new class module that implements the
interface. For example, to capture point selections in MicroStation, we
insert a new class module in our VBA project and place the following
line in the General Declarations area of the class module:
Implements IPr imi tiveCommandEvents

Using the "Implements" keyword in a class module means the class

module inherits the methods or events of the interface.

When we use the Implements keyword, the name of the interface

appears in the object combobox of the class module.


If we select the interface in the object combobox, as shown above, the

methods of the interface displays in the methods combobox.
As we can see, the IPrimitiveCommandEvents interface exposes six
methods or events. They are Cleanup, DataPoint, Dynamics, Keyin,
Reset, and Start.
Unlike user form controls, where we pick and choose which events we
want to display and work with, each and every method in an interface
must be declared, even if we are not going to do anything with them.
The easiest way to do this is to select each of the methods in the

I Class Module Review I


Methods combobox. Each time we do this, VBA automatically writes

the Sub .. , End Sub code for us. If we follow this procedure for the
IPrimitiveCommandEvents interface, we see the following in the class
Implements IPrimitiveCommandEvents
Private Sub IPrimitiveCommandEvents_Cleanup()
End Sub
Private Sub IPrimitiveCommandEvents_OataPoint(Point As Point3d,
ByVal View As View)
End Sub
Private Sub IPrimitiveCommandEvents_Oynamics(Point As Point3d, _
ByVal View As View , ByVal OrawMode As MsdOrawingMode)
End Sub
Private Sub IPrimitiveCommandEvents_Keyin(ByVal Keyin As String)
End Sub
Private Sub IPrimitiveC omma ndEvent s_Rese t()
End Sub
Pr i vate Sub IPrimitiveCommandEvents_Start()
End Sub

Now we are ready to enter the code into the events.


An interface must be exposed using a class module. We already
discussed class modules but a quick review is in order.


I Chapter 18: Interface Essentia ls I

We create a new class module by using the VBA menu Insert> Class
Module. By default, VBA names the new class modules "Class
"Class2", "Class3", "Class4", etc. We should rename them to something
that helps us understand what the class is. For example, we name a class
that writes to files "clsFile Writer". In a later example, we will name a
Class "LCE_Text" to indicate we are working with the
ILocateCommandEvents interface (LCE) and that we are doing
something with text.


After a class module is inserted and named, we begin writing our code.
Methods and functions are written very much like they are in code
modules. We can create events for our class modules. We can create
properties by declaring variables as "Public" in the General Declarations
area of the code module. And we can also create properties through the
use of "Get" and "Set" (or "Let") statements.
The end result of creating a class is a new object. Classes cannot operate
independently. They need other code to initiate them, set their
properties, and use their methods. I will demonstrate two ways to call up
a class module. The first is to create a new class module named
"clsNetNode". Here is the code:
Pr iva t e Type IP Add r
Se tl As Byte
Set2 As Byte
Set3 As By t e
Set4 As Byte
End Type
Public Name As St ring
Pr i vate IPAdd r ess As IPAddr
SubP i ng()
MsgBox "Pinging " & IPAddress . Setl &
IPAddress . Set2 &
IPAddress . Set3 &
IPAddress.Set4 , , Name
End Sub
Sub SetIPAddress(IPA As Byte, IPB As Byte , IPe As Byte, _
IPD As Byte)
PAddress.Setl = IPA

I Class Modu le Lifecycle I

IPA dd ress . Set2
IPAddre s s.Set3
End Sub


I Pe

This class has one property (Name) and two methods (Ping and
SetIPAddress). The SetIPAddress method sets the IP address values of
the private variable "IPAddress". The Ping method displays the entered
IP address in a MessageBox and uses the Name property for the
MessageBox caption.
The first way to call up a class module is to declare a variable as the class
type, set the variable to a "New" class type, and then set properties and
use methods.
Sub TestClsNetNodeA ()
Di m my Ne t Node As cl sNetNode
Set myN etN ode = Ne w clsNet Node
myN e tNode . Se tI PAddress 192. 168 , 1. 1
my NetNod e . Name = "Ro uter "
my Ne t Node.P i ng
End Sub

The second way to utilize a class module is to declare a variable as a

"New" class type and then begin setting variables and using methods as
shown below:
Sub Te s tClsNetNodeB ()
Di m myNetNode As New clsNetNode
myNetNode . SetIPAddress 192 , 168 , 1. 1
myNe t Node . Na me = "Router "
my NetN od e . Pi ng
End Sub

The difference between these two ways to declare and

initialize class modules is small. The net result is the
same however in this example.

Pinging 192. 163. 1.1


When we implement a simple class module, as we did with
"clsNetNode", the class is alive only as long as the variable declared as the


I Chapter 18: Interface Essentials I

class module is in scope. In the two test procedures above, we declared
the variable myNetNode inside the procedures so the clsNetNode Class
is only alive inside the procedure where the variable was declared.
Variables declared in the General Declarations area as a class module are
available to all methods in the module in which it is declared and other
modules as well if the variable was declared as "Public".
As soon as a variable declared as a specific class goes out of scope, the
object is automatically terminated. This is not the only way a declared
class can be terminated. You can terminate a class by setting the variable
representing the class to Nothing, as follows :
Set myNetNode = Noth i ng

This explanation of the life cycle of classes relates to most circumstances

where we utilize class modules. One exception to this rule is when we
use classes with the StartLocate and StartPrimitive methods of the
MicroStation CommandState object. When we use StartLocate and
StartPrimitive, MicroStation holds onto the class and notifies it of events
"CommandState.startDefaultCommand", or we use one of the
"Remove ... :' methods where applicable. We will see examples of the
"Remove ... :' methods later in this book.
It is important to understand the life cycle of the classes we will be using
to implement MicroStation interfaces because the code we use to call up
these classes will execute and the procedure will end but the class will
still be alive because MicroStation is keeping it alive.

Let's discuss two MicroStation

ILocateCommandEvents interface.






ILocateCorn rnandEvents
The ILocateCommandEvents Interface allows us to have the user select
or (Locate' an element. Here are the events exposed through the
ILocateCommandEvents interface:

Private Sub ILocateCommandEvents_Accept(ByVal Element As

Element, Point As Point3d, ByVal View As View)


Private Sub ILocateCommandEvents_CleanupO

I Class Module Lifecycle I

IB Private Sub ILocateCommandEvents_Dynamics(Point As
Point3d, ByVal View As View, ByVal DrawMode As
IB Private Sub ILocateCommandEvents_LocateFailedO
IB Private Sub ILocateCommandEvents_LocateFilter(ByVal
Element As Element, Point As Point3d, Accepted As Boolean)
IB Private Sub ILocateCommandEvents_LocateResetO
IB Private Sub ILocateCommandEvents_StartO
Each event is triggered at a specific time. Some of the events provide
information such as which element was located. Whenever we use
MicroStation's interface objects, each event or method must be declared,
whether we intend to use it or not. Let's take a look at an example.
We begin with capitalizing text elements.
The nam e of this class m odule is LeE_Text. Here is the code in the class
mo dule:
Implemen ts I LocateCommandEvents
Private SelEle ment As Element
Private Sub ILocateCommandEvents_Accept(SyVal
Element As Element. Point As Point3d. SyVal View As View )
Dim elemText As TextElement
Set elemText = Element
elemText . Redraw msdDrawingModeErase
el emText.Text = UCase(elemText .T ext)
elemText.Redraw msdDrawingModeNormal
ActiveModelReference . UnselectAllElements
End Sub
Private Sub ILocateCommandEvents_Cleanup()
End Sub
Private Sub ILocateCommandEvents_Dynamics(Po i nt As Point3d . SyVal
View As View . SyVal DrawMode As MsdDrawingMode)
End Sub



I Chapter 18: Interface Essentials I

Pr i vate Su b ILoc ate Co mmandEve nts_Lo ca te Fai led ()
If SelElement Is Nothing = False Then
Set SelElement = Nothing
End If
ShowCo mmand "CAP Text "
Sh owPr omp t "Sele ct Tex t t o be Ca pi ta liz ed "
End Sub
Privat e Sub ILo cateC omman dEv ents _ Loca teF i lt er( ByVa l _
Ele ment As El ement. Po in t As Po int 3d . Acce pted As Boolean)
Accepte d = False
If Ele ment .I sTex t Ele ment = True Then
Set SelElement = Eleme nt
Accepted = True
ActiveModelReference . Se l ect El ement El ement . Tr ue
ShowCommand "CAP Text "
ShowPrompt "Cli ck again to conf i rm. . . "
End If
End Sub
Private Sub I LocateCommandEvents_LocateReset()
CommandState . Start Defaul t Command
End Sub
Pr i vate Sub I LocateCommandEven t s_S t ar t ()
End Sub

LocateFilter Event
The first event we work with is the LocateFilter event. This event gives
us the ability to specify whether the element selected meets our criteria.
By default, the accepted property is true. If the accepted property
remains true, the user is given the opportunity to ''Accept'' the selection
by clicking again in MicroStation. When the user ''Accepts'' the
selection, the accept event is triggered and lhe code inside it is executed.
If in the LocateFilter event, the accepted parameter is set to false, the
LocateFailed event is triggered. It is common to re-start the interface
object if the LocateFilter event returns a false accepted value.

I Class Module Lifecycle I


Accept Event
Two conditions must exist before the accept event is triggered. First, the
LocateFilter event must exit with an accepted property of true. Second,
the user must "Accept" the already filtered element by left-clicking in
MicroStation. A right-click in MicroStation, after LocateFilter
successfully exits, resets the LocateFilter event but will not exit the
interface completely. When these two conditions (LocateFilter and User
Acceptance) are met, the code in the Accept event is executed.

LocateReset Event
The LocateReset event, the last triggered event in this interface, is
triggered when the user issues a reset by right-clicking in MicroStation
before the LocateFilter Event has been entered or after the LocateFilter
event has been entered but the accepted property has been set to false.
Remember that the LocateReset event is telling us that the user has
requested a reset. It is up to our code to exit the interface by issuing a

LocateFailed Event
The LocateFailed event is triggered when the user clicks to select
something but nothing is located. This event could be used to exit out of
the interface by using "CommandState.StartDefaultCommand".

Start Event
The Start event, the first event triggered when utilizing this interface,
can be used to set up variables or other objects.

Cleanup Event
The Cleanup event is triggered just prior to the LocateReset event. As
the name implies, it can be used to clean up variables, objects, or
references used by the interface.

Dynamics Event
The Dynamics event provides dynamic real-time feedback. An example
later in this chapter demonstrates how it is used.


I Chapter 18: Interface Essentials I

Class Modules do not work by themselves - they need to be created by
other code. Here is the procedure that makes use of our new LeE_Text
Sub tstLCE TextC)
CommandState.StartLocate New LCE_Text
ShowCommand "CAP Text "
ShowPrompt "Select Text to be Capitalized"
End Sub

Tong e nt
Here are the
screen shots of
the program
working. Notice
the command
and prompts
guiding the user.

CAP Text >Select Text to be Capitalized

CAP Text > Click again to conlirm ...

Element Selection> Identify efement to add to set

Here is another variation of the Capitalize Text program. The only

difference is the code handling the capitalizing of the text. It is now
placed in the LocateFilter event. This means the selected text element is
capitalized without waiting for user confirmation.
Imp l ements I LocateCommandEvents
Private SelElement As Element
Private Sub ILocateCommandEvents_AcceptCSyVal Element As Element ,
Point As Poi nt3d , SyVal Vi ew As View)
End Sub

I Class Module Lifecycle I


Pri vate Sub I LocateCommandEvents_Cleanup()

end Sub
Private Sub ILocateCommandEvents_Dynamics(Point As Point3d. _
ByVal View As View. ByVal DrawMode As MsdDrawingMode)
End Sub
Private Sub ILocateCommandEvents_LocateFailed()
If SelElement Is Nothing = False Then
ActiveModelReference . UnselectAllElements
Set SelElement = Nothing
End If
ShowCommand "CAP Text"
ShowPromp t "Select Text to be Capita l ized "
En d Sub
Private Sub ILo cate Comman dEvent s_ Loca t eFilter ( ByVal Ele ment As
Elemen t . Point As Point3d . Accepted As Boo l ean)
Acc ept ed = False
Dim elemText As TextElem ent
If Element . IsTextElement = True Then
Set elemText = Element
elemText . Redraw msdDrawingModeEra s e
el emTe xt.T ex t = UCase (el emTe xt .Te xt)
elemText.Redraw msdDraw ingModeNormal
elemText . Rewrite
End If
End Sub
Private Sub ILocateCommandEvents_LocateReset()
End Sub
Private Sub ILocateCommandEvents_Start()
End Sub
Here is the code that initializes the Interface Object.


I Chapter 18: Interface Essentials I

Sub tstLCE_Text2()
CommandState . StartLocate New LCE_Iext2
ShowCommand "CAP Text"
ShowPrompt "Select Text to be Capitalized"
End Sub

We have not used the Dynamics event mentioned previously. Let's use it
This code dynamically draws a new text element displaying the distance
between the original selection point and the cursor location. This is
done real-time. As the cursor moves, the text changes.

Here are two examples

of this Interface in
action. The first is while
the cursor is being
dragged after the initial
selection. The second is
after the mouse button
is clicked and the
"Distance Text" is placed.

L o ca~i Oflt<

3 . 08 7

L oca ti on

Here is the code for the Class Module named LeE DistanceText.
Implements ILocateCommandEvents
Private selElem As Element
Private pt3StPoint As Point3d
Private dblDistance As Double
Private Sub ILocateCommandEvents_AcceptCByVal Element As Element. _

Point As Point3d . ByVJl View As View)

Dim txtElem As TextElement
Dim rotMatrix As Matr i x3d
dblDistance = Point3dDistance(Point. pt3StPoint)

I Class Module Lifecycle I

Set txtElem


CreateTextElementl(selE l em , Round(dblDistance, _
3) , Po i nt, rotMatrix)
ActiveModelReference.AddElement txtElem
txtEl eli. Rewri te
CommandState.StartLocate Me
End Sub

Private Sub ILocateCommand Events_Cleanup( )

End Sub
Private Sub ILocateCommandEvents_Dynamics(Point As Point3d, _
ByVal View As View, ByVal DrawMode As MsdDrawingMode)
Dim tmpTxtElem As TextElement
Dim rotMatrix As Matrix3d
dblD i stance = Po i nt3dDistance(Point, pt3StPoint)
Set tmpTxtElem

CreateTextElementl(selElem , Round(dblDistance, 3) , _

Point, rotMatrix)
tmpTxtElem.Redraw DrawMode
ShowPrompt "Select Distance Point: "
End Sub
Private Sub I LocateCommandEvents_ LocateFailed ()
CommandState . Start Locate Me
End Sub
Private Sub ILo cateCo mmand Ev ents_Locate Fi lter (By Val Element As Element, Point As Point3d,
Accepted As Boolean)
Set selElem = Element
pt3StPoint = Point
CommandState . StartDynamics
End Sub
Private Sub ILocateCommandEvents_LocateReset()
CommandState . StartDefaultCommand
End Sub
Private Sub ILocateCommandEvents_Start()
ShowCommand "Te xt Distance "
ShowPrompt "Select Element for Base Point "
End Sub


I Chapter 18: Interface Essentials I

Here is the procedure that calls the interface through the class:
Sub tstLCE DistanceText()
CommandState.StartLocate New LCE_DistanceText
End Sub

This macro demonstrates using a Dynamics event. A careful review of

the LocateFilter event shows the StartDynamics method. Without this
method, the Dynamics event would not be triggered. The Dynamics
event creates a new text element at the point of the cursor displaying the
distance between the original Locate Point and the cursor location.

When an element is 'located', we enter the LocateFilter method. In
previous examples we used this method to determine the type of the
selected element. This works but if we know the kind of element we
want, we can specify this before the selection is made by using
I mpl ements IL ocateCommand Events
Pr i vate Sel El eme nt As El ement
Di m my LC As Lo cat eCri te r i a
Private Sub ILo cateCommandEvents _Accept(ByVa l Ele ment As Element . _

Point As Po i nt 3d .
By Val Vi ew As View )
End Sub
Private Sub I LocateCommandEvents_C l eanup()
End Sub
Private Sub ILocateCommand Events_Dynamics(Point As Point3d. _
ByVa l View As View. ByVal DrawMode As MsdDrawingMode)
End Sub
Pr i vate Sub I LocateCommand Events_LocateFailed()
If SelElement Is Nothing = False Then

I Class Module Lifecycle I


ActiveMod elReference . Unselect AllEl eme nts

Set Sel Element = No thin g
End If
ShowComnand "CAP Text"
ShowPrompt "Select Text to be Capitalizec "
End Sub
Private Sub I Loc ateCommandEvents_LocateFilter(ByVal Element As
Element, Point As Point3d, _
Accepted As Boolean)
Dim elemText As TextElement
Set el emText = Element
elemText.Redraw msdDrawingModeErase
elemText.Tex t = UCase(elemText .T ext)
el emText . Redraw msdDr aw in gM odeNormal
ele mText . Rewrite
Act iv eModelR efe ren ce .U ns el ectA ll El ements
Comman dState.StartDefaultCommand
End Sub
Priva t e Sub I LocateCommandEvents _L ocateReset()
CommandSta t e . Star t Defau l tCommand
End Sub
Priv ate Sub IL oca t eCommandEven ts_Start()
Set myLC = Comma nd State . Cr eat e LocateCr i te ri a(True)
myLC.Exc l udeAllTypes
myLC.lncludeType (msdElementTypeText)
CommandSta t e . SetLoca t eCriteria myLC
End Sub

We make use of the LocateCriteria object with SetLoca t eCri ter i a to

proactively filter the user's selection. This is preferable to allowing the
selection to be made, reviewing the element's properties, and accepting
or rejecting the selection inside the LocateFilter event. By using the
LocateCriteria object, we know the user has made a legitimate selection
by the time we get to the LocateCriteria event.
Here are the methods of the LocateCriteria object:
[B Sub ExcludeAllClassesO
[B Sub ExcludeAllLevelsO


I Chapter 18: Interface Essentials I


Sub ExcludeAllTypesO


Sub ExcludeClass(ElemClass As MsdElementClass)

[B Sub ExcludeLevel(Level As Level)


Sub ExcludeType(Type As MsdElementType)


Sub IncludeClass(Elem Class As MsdElementClass)

[B Sub IncludeLevel(Level As Level)


Sub IncludeOnlyHoleO


Sub IncludeOnlyLockedO

[B Sub IncludeOnlyModifiedO

Sub IncludeOnlyNewO


Sub IncludeOnlyNonPlanarO

[B Sub IncludeOnlyNonSnappableO

Sub IncludeOnlyOldO

[B Sub IncludeOnlyPlanarO

Sub IncludeOnlySnappableO


Sub IncludeOnlySolidO


Sub IncludeOnlyUnlockedO


Sub IncludeOnlyUnmodifie dO

[B Sub IncludeType(Type As MsdElementType)

The MicroStation VBA help file explains the use of each method shown
here as well as examples of how they are used.

We just finished discussing the ILocateCommandEvents interface. Its
primary use is selection (or location) of elements in a design file. Use the
IPrimitiveCommandEvents object to capture command entry and point
Here are the events we have to work with:
[B Private Sub IPrimitiveCommandEvents_ CleanupO

I Class Module Lifecycle I



Private Sub IPrimitiveCommandEvents_DataPoint(Point As _

Point3d, ByVal View As View)


Private Sub IPrimitiveCommandEvents_Dynamics(Point As _

Point3d, ByVal View As View, ByVal DrawMode As


Private Sub IPrimitiveCommandEvents_Keyin(ByVal Keyin As



Private Sub IPrimitiveCommandEvents_ResetO


Private Sub IPrimitiveCommandEvents_StartO

Some of these should look familiar: Start, Reset, Cleanup, Dynamics. We

have already used these. Two events we have not worked with are
DataPoint and Keyin. Let's take a look at several examples of how these
events work.

The PCE_LineTest class draws a rubber-band line from the first point
selected to the current cursor location. After the second point is
selected, we use StartDefaultCommand to exit out of the class:
Implements IPr i mitiveCommandEvents
Dim pt3BasePoint As Po in t3d
Dim boolSet As Boolean
Private Sub IPrimitiveCommandEvents_Cleanup()
End Sub
Private Sub IPrim i tiveCommandEvents_DataPoint(Point As Point3d , _
ByVal View As View)
If boolSet = False Then
pt3BasePoint = Point
CommandState.StartDynam i cs
boolSet = True
CommandState . StartDefaultCommand
End If
End Sub


I Chapter 18: Interface Essentials I

Private Sub IPrimitiveCommandEvents_Dynamics(Point As Point3d, _
ByVal View As View, ByVal DrawMode As MsdDrawingMode)
Dim myLineElem As LineElement
Set myLineElem


pt3BasePoint, Point)

myLineElem.Redraw DrawMode
End Sub
Private Sub IPrimitiveCommandEvents _Keyin(ByVal Keyin As String)
End Sub
Private Sub IPrimitiveC ommandEvents_Rese t ()
End Sub
Pr i vate Sub I Prim iti veCommand Eve nts_St ar t()
End Sub

Most of the code in this example is in the DataPoint event and the
Dynamics event. Remember, we only want two points to be selected. We
use the variable boolSet so we know if the first point has been selected.
If the base point has not been selected, boolSet equals false and we take
the Point parameter and place it in the pt3BasePoint variable,
StartDynamics, and change boolSet to true.
As the cursor moves in MicroStation the
Dynamics event is triggered. This
happens many times per second. We
need to make sure the code in the
Dynamics event is not too timeconsuming. In this example, we create a
new LineElement between the initial point selected and the current
cursor location given to us in the Point parameter.
Interface objects cannot run by themselves. They need code in a code
module or a form to call them up.
Sub PlaceLine()
CommandState.S t ar tPr i mitive New PCE_ Line Tes t
End Su b

I Class Module Lifecycle I


Running this code demonstrates the fact that it works. The first point is
selected and the line is drawn as the cursor moves in MicroStation. After
the second point is selected, we exit the object. Normally we would not
leave this object as it is. We would do something with the two points. We
may draw a line between the two points. Or we could write code to
divide the selected points into four equal segments and draw circles at
those division points. We will see this in a future example.

The next example utilizes the same two point selection we saw in the
previous example. However, in this example we draw a rectangle using
the two points as bounding points. The only code that differs is the code
that generates a shape using the X and Y elements of the points to create
a rectangle. The name of this class module is peE_RecTest.
Implements IPrimitiveCommandEvents
Dim pt3BasePoint As Po i nt3d
Dim boolSet As Boolean
Private Sub IPrimitiveCommandEvents_Cleanup()
End Sub
Private Sub IPrimitiveCommandEvents_DataPoint(Point As Point3d. _
ByVal View As View)
If boo lS et = False Then
pt3BasePoint = Point
CommandState . StartDynamics
boolSet = True
E1 s e
End If
End Sub
Private Sub IPrimitiveCommandEvents_Dynamics(Point As Point3d. _
ByVa l View As View. ByVal DrawMode As MsdDrawingMode)
Dim pt3RecPoints(D To 3) As Point3d
Dim myShapeElem As ShapeElement
pt3RecPoints(O) = pt3BasePoint
pt3RecPoints(1).Y = pt3BasePoint . Y


I Chapter 18: Inte rface Essentia ls I

pt3RecPoints(2) = Point
pt3RecPoints(3).X = pt3BasePoint.X
pt3RecPoints(3).Y = Point.Y
Set myShapeElem = CreateShapeElementl(Nothing. pt3RecPoir.ts)
myShapeElem.Redraw DrawMode
End Sub
Private Sub IPrimitiveCommandEvents_Keyin(ByVal Keyin As String)
End Sub
Private Sub IPrimitiveCommandEve nts Reset( )
End Sub
Pr i vate Sub IPri mi ti veCommand Eve nt s_Star t ()
End Sub

Notice how the X and Y elements of each shape vertex is derived from
the base point and the current cursor point.
Su b Pl ace Rec()
Co mmand State . Start Pri mit i ve Ne w PC E_Rec Test
End Sub

The procedure PlaceRec initiates the

PCE_RecTest class module.
After the first point is selected, a
rectangle is dragged from the first
point to the cursor. Since we are not
doing anything with the Reset event,
the only way to get out of this interface is to select the second point.

The CircleTest class draws a circle with a center at the first selected point
to the cursor.
Implements IPrimitiveCommandEvents
Dim pt3BasePo i nt As Point3d
Dim boolSet As Boolean

I Class Module Lifecycle I


Private Sub IPrimit iveCommandEvents_Cleanup ()

End Sub
Private Sub IPrimitiveCommandEvents_DataPoint(Point As Point3d. _
ByVal View As View)
If boolSet = False Then
pt3BasePoint = Point
boolSet = True
El se
CommandState . StartDefaultCommand
End If
End Sub
Private Sub IPrimitiveCommandEvents_Dynamics(Point As Point3d. _
ByVal View As View. ByVal DrawMode As MsdDrawingMode)
Dim myC ir cle As EllipseElement
Dim r otMatr ix As Matrix3d
Dim dblRadius As Double
dblRad iu s = Point3dDistance(pt3BasePoint. Point)
Set myCircle = Create El lipseEl eme nt2(N ot hin g . pt3BasePoint. _
db lR adius . db lRa di us . rotMatrix)
myCirc l e.Redraw DrawMode
End Sub
Private Sub IPrimitiveCommandEvents_Keyin(ByVal Keyin As String)
End Sub
Private Sub IPrimitiveCommandEvents_Reset()
End Sub
Private Sub IPrimitiveCommandEvents_Start()
End Sub

This example makes use of the Reset event. If the user resets the
command, we exit the interface object by calling StartDefaul tCommand .


I Chapter 18: Interface Essentials I

Sub Pl aceC i rc ()
CommandState.StartPrimit i ve New PCE_CircTest
End Sub

The PolyTest example draws a regular polygon circumscribed within an
imaginary circle centered at the first point and extending out to the
cursor location. We could draw a square, a triangle, or a hexagon. Which
should we draw? The PolyTest class can draw any regular polygon
because we specify the number of vertices. The code in the class module
is clear enough. The way we call up the class module differs from the
other examples we have looked at. Let's begin with the class module:
Im pl ement s IP rimit iv eComm an dEv ent s
Dim pt3BasePoi nt As Point3d
Dim boo l Se t As Bool ea n
Pub li c Ve rt i ces As Long
Private Sub IPrimitiveCommandEvents_Cleanup()
End Sub
Private Sub IPrimitiveCommandEvents_DataPoint(Point As Point3d. _
ByVal View As View)
If boolSet = False Then
pt3BasePoint = Point
boolSet = True
End If
End Sub

I Class Module Lifecycle I


Priv at e Sub IPr imiti veComma ndEve nt s_Dy nami cs( Po in t As Poi nt3d, _
ByVal View As View , ByVal Dr aw Mod e As Msd Dra wing Mode )
Dim pt3Po l yPo i nts() As Poin t 3d
ReDim pt3PolyPo'nts(O To Vertices - 1) As Point3d
Dim myShapeElem As ShapeElement
Dim I As Long
Di m db l BaseA ngle As Doub l e
db lB as eAngle = Atn((Po i nt . Y - pt3B asePoin t. Y) /
( Poi nt . X - pt3Bas ePoint . X))
For I = 0 To Ver t i c i es - 1
pt3PolyPoi nts(I) = Point3dAddA ngleOistance(pt3BasePoint , _
db lBa seAng l e + Radians(360 / Vertic i es * I) , _
Poi nt3dO i s t ance(pt3Base Poi nt , Point), 0)
Next I
Set myShapeE l em = CreateShapeElementl(Nothi ng , pt3PolyPoin t s )
myShapeE l em . Redraw DrawMode
End Sub
Private Sub IPrimitiveCommandEvents_Keyin(ByVa l Keyin As String )
End Sub
Private Sub IPrimitiveCommandEvents_Reset()
CommandState . StartDefaultCommand
End Sub
Private Sub IPrimitiveCommandEvents_Start()
End Sub

Take note that the vertices variable is declared as a public variable in the
General Declarations area of the class module. This allows it to act as a
property of the class module.
Sub Pl acePoly ()
Dim myPolyTest As New PCE_PolyTest
myPo l yTest . Vert i cies = 8
Comma ndState . St art Pri mi ti ve myPoly Test
End Sub

As we mentioned previously, we make use of the PCE_PolyTest class a

little differently than we did to the previous classes. In this example we
declare a variable as a New PCE_PolyTest. We need to do this so we can


I Chapter 18: Interface Essentials I

sp ecify the number of vertices we want drawn before we start the
"StartPrimitive" activities. After the class is initiated and the vertices
property is set, we use the variable myPolyTest with the "StartPrimitive"
method to begin the capture of PrimitiveCommandEvents.

The number of vertices specified is used to calculate the angle used to

project each vertex of the regular polygon drawn.

Each PrimitiveCommandEvent interface example we have used up to
this point has been based on the user's selection of two points. We drew
a line between two points. We drew a rectangle using the two points as
opposing corners. We drew a circle using two points. We drew a polygon
using the two points.
The PCE_PointStringTest class allows for selection of more than one
point. In fact, there is nothing that prohibits the user from selecting an
endless number of points.
Implements IPrimitiveCommandEvents
Dim pt3BaseP oint As Point3d
Dim pt3Points() As Point3d
Dim boolSet As Boolean
Pr ivate Sub IPrimitiveCommandEvents_Cleanup()
End Sub
Private Sub IPrimitiveCommandEvents_DataPoint(Point As Point3d. _
ByVal View As View)
If boolSet = False Then

I Class Module Lifecycle I


pt 3B aseP oint = Poi nt

pt3Points(O) = Point
ReD i m Preserve pt3Po i nts(UBound(pt3Points) + 1)
Co mmandState.StartDyramics
boolSet = True
pt3Po i nt s(UBoun d( pt3 Po i nts)) = Poi nt
ReDim Preserve pt3Points ( UBoun d( pt3P oi nts ) + 1)
En d If
End Sub
Private Sub IPrimit i veCommandEvents _Dynamics(Point As Point3d , _
ByVal View As View, ByVal DrawMode As MsdDrawingMode)
Dim myPointString As PointSt ri ngEleme nt
pt3Points(UBound(pt3Points)) = Point
Set myPointString = CreatePointStringElement1( _
Nothing, pt3Points, Fa l se)
myPointString.Redraw DrawMode
End Sub
Private Sub IPrimitiveCommandEvents_Keyin(ByVal Keyin As String)
Select Case UCase(Keyin)
Dim myPointString As PointStringElement
pt3Points(UBound(pt3Points)) = pt3BasePoint
Set myPointString

CreatePointStringElementlCNothing, _

pt3Points, False)
Act i veModelRe f erence.AddElement myPoin t String
CommandState . StartDefaultCommand
End Select
End Sub
Private Sub IPrimitiveCommandEvents_Reset()
End Sub
Private Sub IPrimitiveCommandEvents_Start()
ReDim pt3Points(O)
End Sub

The user is likely to enter more than one or two points when using this
example. We could declare a variable to hold up to 10 points or 50 points
or 100 points. We could then prompt the user to select "up to 10 points",


I Chapter 18: Interface Essentia ls I

for example. This may work or may be necessary in some circumstan ces,
but when we want any number of points to be allowed, we need a
different solution.
Declaring the variable pt3Points as a dynamic array (using empty
parenthesis when declaring it) allows us to change the size of the array as
n eeded. We ch ange the size with the "Preserve" keyword so VBA
changes the size of the array without dumping the existing array
Each time a new point is selected, we place the selected point in the
upper-bound element of the array and then we immediately increase the
array size by one. It is important to increase the array size by one each
time a new point is entered because we use the new upper-bound
element in the dynamics event.
In the previous examples we wanted the user to only select two points.
This made it easy for us to exit the class module using
"StartDefaultCommand". We have placed the reset event and it works
well. However, we want to allow the user to close the point string and
finish the command without having to reset things. How do we do this?
One way is to use the Keyin event.
Pr ivate Sub IPrim i t i veCommandEvents_Keyin(ByVa l Keyin As Str i ng)
Se l ect Case UCase( Key i n)
Case "PL CLOSE "
Dim myPointString As PointStringElement
pt3Po i nts(UBound(pt3Po i nts)) = pt3BasePo i nt
Set myPointString

CreatePointStr i ngElementl(Nothing. _

pt3 Poi nts . Fal se)

Ac t iv eMode lR ef er enc e. AddE l ement myPoin tS tri ng
Co mmandS t ate. St artD efa ul t Co mm and
End Sel ect
End Sub

Here is the code in the Keyin event. If the user enters "plclose': "PIClose",
"PLClose", etc., we close the point string by placing the base point in the
upper-bound element of the pt3Points variable. We then create a new
PointString element using the pl3Points variable for the vertices of the
point string. We have seen code similar to this but we need to add
something we have not done before. In addition to creating the Point

I Class Module Lifecycle I


String, we add it to the ActiveModelReference. This makes the Point

String a permanent part of the ActiveModelReference.


Multiple points are selected. As the points are selected, we are creating a
Point String element but we do not add it to the model. We only create it
and display it. If at any time the user resets the command, we exit out of
the class and the PointString disappears. When the user enters "plclose"
in the Key-in dialog box and hits <Enter>, we use the vertices that were
selected to create a Point String element and add it to the model.
So, we have seen the class module code and we have seen the results of
the class' work. How do we call it? Differently than any other in this
Sub PlacePointString ()
CommandState . StartPrimitive New PCE_PointSt ri ngTest. True
End Sub

How is this different? We specify that we want to capture key-ins by

providing a value of true for the optional parameter "WantKeyins". The
default value of this parameter is false. So, when we want to capture keyins, we must specify a value of true when we use the StartPrimitive

We want to allow the user to select two points. We will then divide the
space between the two points into equal length segments and draw


I Cha pter 18: Interface Esse ntials I

circles at each vertex of these lengths. Let's begin with the desired
interface and then we will discuss the code.

After the first point is selected, we want to draw a rubber-band between

the selected point and the cursor location.

After the second point is selected, we draw circles dividing the area
between the selected points equally. In this example we specified
dividing the space into four equal segments.
Here is the code for the class module:
Imp l ements I Pri mi tiveCommand Eve nts
Dim pt3BasePoin t As Point3d
Dim boo l Set As Boo l ean
Publ i c l ngDiv i s i ons As Long
Private Sub IPrimitiveCommandEvents_Cleanup()
En d Sub
Private Sub IPrimitiveCommandEvents_DataPo i nt(Point As Point3d. _
ByVal View As View)
If boolSet = False Then
pt3BasePo i nt = Point
boolSet = True
Dim pt3EndPoint As Point3d
Dim dblLineAngle As Double
Dim dblSegDist As Double
Dim DivPoints() As Point3d

I Class Module Lifecycle I


Di m I As Long
ReDim Di vPo i nts(O To lngDivisions - 2) As Point3d
cblLineAngle = Atnpt3EndPoint.Y - pt3Base Doint.Y) /
(pt3EndPoint.X - pt3BasePoint . X))
dblSegDist = Point3dDistanceXY(pt3BasePoint. pt3ErdPoint) /

For I = LBound ( DivPoints ) To UBound(DivPoints)
DivPoints(1) =
Point3dAddAngleDistance(pt3BasePoint. _
dblLineAng l e . dblSegDist * (I + 1) . 0)
Next I
DrawCircle pt3BasePoint. 0 . 25
For I = LBound(DivPoints) To UBound(DivPoints)
DrawCircle DivPoints(1). 0.25
Next I
DrawCircle pt3EndPoint. 0 . 25
End If
End Sub
Private Sub OrawCircle (CenPt As Point3d. Radius As Double)
Dim myEllipse As EllipseElement
Dim rotMatrix As Matrix3d
Set myEllipse

CreateEl li pseElement2(Noth ing. CenPt. Radius. Radius. _

ActiveModelReference.AddElement myE llipse
End Sub
Private Sub 1PrimitiveCommandEvents_Dynamics(Point As Point3d . _
ByVal View As View. ByVal DrawMode As MsdDrawingMode)
Di m myLineElem As LineElement
Set myLineElem = CreateLineElement2CNothing . pt3BasePoint . Point)

myLineE l em . Redraw DrawMode

End Sub
Private Sub 1PrimitiveCommandEvents_Keyin(ByVal Keyin As String)
End Sub
Private Sub 1PrimitiveCommandEvents_Reset()
End Sub


I Chapter 18: Interface Essentials I

Private Sub I Primit iveComman dE ven t s_Start()
End Sub

A careful review of the above code reveals a method named

"DrawCircle': We use this each time we want to draw a circle. This keeps
the DataPoint event a little cleaner by breaking out a specific and
distinct piece of code into its own procedure.
Here are two examples that can be used to work with the PCE_LineTest2
Sub PlaceLine2A()
Dim myLineTest2 As New PCE_LineTest2
myL i neTe st2 .1ngDivi sions = 4
Command State.S tartPrimitive myLineTest2
End Sub
Sub PlaceLine2B ()
Dim myLineTest2 As New PCE_LineTest2
myLineTest2.1ngDivisions = 12
CommandState.StartPrimitive myLineTest2
End Sub

The procedure Pl aceL i ne2A divides the selected points into four equal
segments. Pl ace Li ne2B divides the selected points into 12 equal

Our goal up to this point is to learn how to use the
IPrimitiveCommandEvents Object. We displayed lines, circles, and
polygons as we asked the user to select points. In the most recent
example we divided selected points into a specified number of segments
and placed circles at the segment points. We are going to expand on the
PCE_TestLine2 class in the next example.
PCE_TestLine2 is useful if we want circles drawn at a specific radius at
segment points. If we want to draw squares, we could create a new class,
copy and paste the code from PCE_TestLine2, then modify the new class
to draw squares. We would do the same to draw hexagons. We would
create a new class, copy and paste, then modify the code. To draw
octagons, we would create a new class, copy and paste, then modify the

I Class Module Lifecycle I


code. We could create a hundred new class modules, each drawing a

different type of element at the division points. Or we could create a new
class, copy and paste, then modify the code once so we can use the new
class over and over again.
PCE_TestLine3 is based on PCE_TestLine2, but instead of drawing
circles at the division points, we place the points into a variable that the
procedure which calls the class can use. Let's look at some examples of
how to use the new class. Then we will look at the class itself.
Sub PlaceLine3A ()
Dim myDivPo i nts() As Point3d
Di m myLi neTest3 As New PCE_ LineTest3
Dim I As Long
myLine Test3 . lngDivisions = 12
Co mmandState . StartPrimit ive myLineTest3
While myL i neTe st3 . ClassComplete = Fa l se
myD ivP oin ts = myLineTest3.DivPts
For I = LBound(myDivPoints ) To UBound(myDivPoints)
Dim my El l ip se As Elli pse Ele men t
Dim rotMatrix As Matr ix3 d
Set myEll i pse = Cr eateE lli pseEle ment2(Noth i ng. myDivPoints( I ) . _

0.25. 0.25, rotMatrix )

ActiveModelReference.AddElement myEllipse
End Sub

Let's break this procedure down for discussion.


We declare Variables and initiate the PCE_LineTest3 class with the

use of the New keyword.
Dim myD i vPo i nts() As Point3d
Di m myL i neTest3 As New PCE_Li ne Test3
Dim I As Long

We set the number of divisions we want by setting the IngDivisions

property of the class.
my LineTest3 . lngDivisions


3 We start the PrimitiveCommandEvents object.

CommandState.StartPri mitive myLineTest3


I Chapter 18: Interface Essentials I


We look at the ClassComplete property of the PCE_LineTest3 class

and allow user interaction to continue while the value of the
ClassComplete property is false.
While myLineTest3 . ClassComplete


5 We get the points that had been created by the selection of the two

myLineTest 3.Div Pts

6 We draw circles at each point in the myDivPoints array with a radius

of 0.25.
For I = LBound(myDivP oi nt s ) To UBound (myDivP oi nts)
Di m myE ll ipse As El l i pse El ement
Di m rotMatr ix As Matri x3d
Set myEll i pse

Crea t eE l li pseE l emen t 2C Noth i ng , myDivPoi ntsC I ) , _

0 . 25, 0 . 25, rotMat r i x)

Act i veMode l Reference. AddEl eme nt myEll ip se

Ne xt I

That is the code. One of the benefits of making the changes to

PCE_LineTest3 is that we are using it to return the division points. After
the class returns the points, we can do anything with the points we want
to. Here is another example of using PCE_LineTest3:
Sub Pl aceL in e3B ()
Dim myDivPoints() As Point3d
Dim myLineTest3 As New PCE_LineTest3
Dim I As Long
myLineTest3 . 1ngDivisions = 16
CommandState.StartPrimitive myLineTest3
While myLineTest3 . ClassComplete = False
myDivPoints = myLineTest3.DivPts
For I = LBound(myDivPoints) To UBound(myDivPoints) - 1

I Class Module Lifecycle I


Di m myL i neElem As Line Elem ent

Se t myLine Elem

Cr eate Line El eme nt2( Nothi ng . my DivPoints( I ). _

myD i vPoints(I + 1))

ActiveModelReference.AddEleme~t myLineElem
End Sub

Follow through the code in Pl aceL i ne3B. What is it doing with the points
returned by the PCE_LineTest3 Class?

PlaceLine3B is drawing lines for each segment identified by the

PCE_LineTest3 Class .
Let's look at one more example:
Sub PlaceLine3C ()
Dim myDivPoints() As Point3d
Dim myLineTest3 As New PCE_LineTest3
Dim I As Long
Dim Li neBasePt As Point3d
myLineTest3.lngDivisions = 16
CommandState .S tartPrimitive myLineTest3
While myLineTest3 . ClassComplete = Fal se
myDivPoints = my LineTest3.DivPts
LineBasePt.X = 3: LineBasePt.Y = 4 : LineBasePt. Z = 5
For I = LBound( myDivPoints) To UBound(myDivPoints)
Di m myLineElem As LineElement
Set myL i neE l em = Create Li neE l ement2(Noth i ng. LineBasePt . _
my Di vPoi nt s ( I ) )
ActiveModelReference.AddElement myLine Elem
End Sub

What does this code do? Of course, we are using the PCE_LineTest3
class. But what are we doing with the returned points?


I Chapter 18: Interface Essentials I

We draw lines from each segment point to a single base point.
In each example where we used the PCE_LineTest3 class, we used a
Wh i 1e ... Wend structure to allow the user to select two points. After the
two points are selected, the value of "Class Complete" is no longer false
and we make use of the returned points. Each of the examples works
well without any modification to the class module. This is the most
desirable situation: a class module that can be used in a variety of
circumstances without any modifications.
Reviewing the code above shows that the class module has three
properties. One is named 'IngDivisions: another is named
'ClassComplete', and the last one is named 'DivPts'.
We have seen examples of how we will use PCE_LineTest3. Let's take a
look at the code behind the class module now.
Imp l ements IPrimit i veCommandEvents
Dim pt3BasePoint As Po i nt3d
Dim boo l Set As Boolean
Public lngDiv i sions As Long
Publ i c Di vPts As Variant
Publ i c ClassComplete As Boolean
Priva t e Sub IPr i mitiveCommandEvents_Clean up()
End Sub
Private Sub I Primit i veCommandEve nts_DataPoint(Point As Point3d, _
ByVa l View As Vi ew)
If boolSet = Fal se Then
pt3 Ba sePoint = Poi nt
CommandState . StartDynamics
boo lS et = True
El se
Dim pt3EndPoint As Point3d
Di m db 1L i neAngl e As Double
Di m dblSegDist As Double
Dim DivPoints() As Point3d
Di m I As Long
ReD i m DivPo i nts(O To l ngDivisions) As Point3d
DivPoints(O) = pt3BasePoint
DivPoints(UBound(DivPoints)) = Point

I Class Module Lifecycle I

pt3 EndPoint
dblLineA ngle

Atn((pt3EndPoint.Y - pt3BasePoint . Y) / _
(p t 3En dPoi nt . X - pt3 Base Po i nt . X))

db'SegDist = POint3dDistanceXYCpt3BasePo'nt.

For I


, _

LBound(DivPoints) + 1 To UBound(DivPoints) - 1

DivPointsC I )

Point3dAddAngleDistanceCpt3Base Doint . _

dblLineAngl e. db l SegD i st * (I) . 0)

Next I
Di vPts = Div Po int s
ClassComplete = True
CommandS tat e . StartDefaultCommand
End If
End Sub
Private Sub IPrimitiveCommandEvents_Dynamics(Point As Point3d. _
ByVal View As View . ByVal DrawMode As Msd Dr awingMode)
Dim myLineElem As LineElement
Set myLineE l em = CreateLineElement2(Nothing. pt3BasePoint. _
Po i nt)
myLineElem.Redraw DrawMode
End Sub
Private Sub IPrimitiveCommandEvents_Ke yin(ByVa l Keyin As String)
End Sub
Pri vate Sub IPrimitiveCommandEvents_Reset()
End Sub
Private Sub IPrimitiveCommandEvents_Start( )
End Sub

We have used most of this code before in our previous class

PCE_LineTest2, but let's focus on the DataPoint event. Previously, we
drew circles. Now we are placing the points into an array. Then we are
placing that array into a variable that had been publicly declared as a
variant. Declaring a variable as public in a class module allows it to be
used like a property of an object. In addition to placing the coordinates
into the DivPts variable, we set the ClassComplete variable to true. This
variable, Cla ssComplete, is read by the procedure that calls


I Chapter 18: Interface Essentials I

PCE_LineTest3 in the Whi 1e ... Wend structure. Even though we discussed
Pl aceL i ne3A previously, here is the procedure again:
Sub PlaceLine3A()
Dim myDivPoints() As Point3d
Dim myLineTest3 As New PCE_LineTest3
Di m I As Long
myLineTest3 . lngDiv i sions = 12
CommandState.StartPrimitive myLineTest3
Wh i le myLineTest3.ClassComplete = False
DoEv en ts
Wen d
myDiv Po i nt s = myLi neTes t 3 . Di vPts
For I = LBound(myDivPoints) To UBoun d( myDivP oints)
Dim myEllipse As Ellipse Elemen t
Dim rotMatrix As Matrix3d
Set myEllipse

CreateEllipseElement2(Nothing. myDivPoints(I) , _

0 . 25 , 0 . 25, rotMatrix)
ActiveModelReference . AddE le ment myEl li pse
End Sub

Once we use StartPrimitive, we begin looking at the ClassComplete

property and wait until ClassComplete is set to true. When
ClassComplete is true, we can get the points from the DivPts property of
the class.

Optimizing The Dynamics Event

The Dynamics event is very exciting. Each time the mouse moves even
the slightest degree, the code within the Dynamics event is executed.
What does this mean? Rapid fire VBA code execution!
How 'Rapid Fire'? That's a good question. One test demonstrated that
simply moving the cursor from the left side of a MicroStation window to
the right side resulted in the Dynamics event executing over 390 times.
This implies that you need to be very careful with the code placed in the
dynamics event.
Simple, fast-executing code will not cause problems. However, code that
attempts complex calculations or performs other time-consuming
operations can cause problems. Even though the examples we have

I Class Module Lifecycle I


shown in this chapter work well, we may need to minimize processor

overhead when working in the dynamics event.



ByVal View As View, ByVal

Di m my Li ne El em As Li ne El ement
Set myLineE l em



As Point3d, _

CreateL i ne Element2(Nothing , pt3BasePoint , POint)

my LineEle m. Redraw DrawMod e

End Sub

Each and every time this dynamics event is triggered, we do the


Declare a variable as a LineElement.

Create a LineElement.

3 Redraw the LineElement.

Three lines of code are in the event - each line takes up processor time.
What we don't see in the code is that the LineElement goes out of scope
when we exit the dynamics event. This takes time because VBA has to
dump the memory that had been assigned to the object. Imagine setting
aside memory, drawing a line, and then dumping the memory over 390
times just because the cursor moves from the left to right.
Let's compare "PCE_LineTest" with a different implementation of the
Dynamics event in "PCE_LineTest4".

Imp l ements IPrimiti veComma ndEvents

Dim pt3BasePoint As Poi nt3d
Di m boolSet As Boo l ean
Private Sub IPrimitiveCommandEvents_Cleanup ( )
End Sub
Pr i vate Sub IPrimit i veCo mm andEvents_Data Poi nt( Poin t As Point3d , _
ByVal View As View)
If boo l Set = Fa l se The n
pt3BasePoint = Point
Co mmandState . St artDy nami cs
boo l Se t = True


I Chapter 18: Interface Essentials I

Co mmandState.StartDefau l tCommand
End If
End Sub
Private Sub IPrimitiveCommandEvents_Dynamics(Point As Point3d . _
ByVal Vi ew As Vi ew. By Val DrawMode As MsdDraw i ngMode)
Dim myL i ne Ele m As Li ne El em ent
Set myLineElem = Crea t eLineElement2(Not hi ng. pt3BasePo i nt. Point)

myLineElem.Redraw DrawMode
End Sub
Private Sub IPrimitiveCommandEvents_Keyin(ByVal Keyin As Str i ng)
End Sub
Private Sub IPrimitiveCommandEvents_Reset()
End Sub
Private Sub IPrimitiveCommandEvents_Start()
End Sub

We declare the variable, create the line, redraw it, then terminate it
(because it goes out of scope) each time the dynamics event is triggered.
Now let's look at the difference between PCE_LineTest and

Imp l ements IPrimitiveCommandEvents

Dim pt3BasePoint As Point3d
Dim boolSet As Boolean
Dim myLineElem As LineElement
Private Sub IPrimitiveCommandEvents_Cleanup ()
End Sub
Private Sub I PrimitiveCommandEvents_DataPoint(Point As Po i nt3d. _
ByVal View As View )
If boo lSet = False The n

I Class Module Lifecycle I




Set myLineElem = Create LineElement2(Nothing. Point. Point )

boolSet = True
E1 s e

CommandState . StartDefaultCommand
End If
En d Sub
Private Sub

As Point3d . _
ByVa l View As View . ByVal DrawMode As MsdDrawing Mode)
myLineElem.Vertex(l) = Point
myLineE l em .Re draw DrawMode
End Sub
IPr ' ~itiveCommandEvents _ 8yramics(Point

Private Sub IPr i mitiveComma ndEvents_ Key in (ByVal Keyin As String)

End Sub
Private Sub IPrimitiveCommandEvents_Reset()
End Sub
Private Sub IPri mi t i veCommandEven ts_St art()
End Sub

In PCE_LineTest4, we declare the variable for the LineElement in the

General Declarations area. We create the line once in the DataPoint
event. When we get to the Dynamics event, all we do is change the
EndPoint of the line and redraw it. This is a much more efficient way to
work with the Dynamics event.
PCE_LineTest and PCE_LineTest4 are simple and small. Although it
may not be apparent when using these two classes, PCE_LineTest4 uses
significantly less processor time. Minor changes like the one we made
for PCE_LineTest4 may not make an immediate dynamic difference in
the efficiency of our code, but little efficiencies add up to significant
performance benefits. The opposite is true as well. Inefficiencies add up
to significant performance degradation.


I Chapter 18: Interface Essentials I

We implement interfaces through class modules. Each property or
method of the interface that we implement must be declared in the class
module. When we implement the interfaces discussed in this chapter,
we can have greater control, flexibility, and power in our programming
as our applications become more interactive.


Using MicroStation's
Built-In User Forms
One ways to increase your speed in developing applications is to use of
existing code, objects, and interfaces.
In this chapter:

Declaring MicroStation user form functions


The FileOpen dialog


The FileCreate dialog


The FileCreateFromSeed dialog


The OpenAlert dialog


The OpenlnfoBox dialog


Access MicroStation's built-in User Form functions by using a DLL
(Dynamic Link Library). When functions are wrapped in a DLL, they
must be declared before they are used. Make these declarations in the
general declarations area of a code module.



I Chapter 19: Using MicroStation 's Built-In User Forms I

Declare Functio n mdlDialog_openAlert Lib _
"stdmdlbltin.dl l " ( ByVal s tringP As St r ing) As Lo ng
Here is the declaration for the OpenAlert dialog box. Let's break up the
declaration into its individual parts:
Declare Function

Tell s VBA we a re going to decla re a fun ctio n that

is part of a DLL file .


The nam e of the function we are declaring.

Lib "stdmdlbltin.dll"

The name of the DLL Li brary the in wh ich the

function is contained.

(ByVal stringP as String)

Parameters for the function. Can be empty 0 or

contain one or more parameters.

As Long

The return type of the function.

One of the distinguishing fe atures of a function is that it returns a value.

"mdlDialog_openAlert" returns a Long value. Some functions in DLLs
n eed to return more than one value. They do this by changing the values
of variables we supply into the function's param eters.
H ere is the declaration for the FileOpen Function:

Dec l are Funct i on mdl Dia l og_f il eOpen Lib

"s t dmd l bltin . dll " (ByVal _
f il e Name As St ri ng. ByVa l r Fi le H As Long. ByVal _
resource l d As Long . ByVa l s ugges t edFileName As String . _
ByVal filterString As String. _
By Va l default Di r ec t ory As Str i ng . _
ByVal ti t leS tri ng As Strin g) As Long
To open a file, we use the FileOpen function. How do we know which
file was selected? What if the user clicks the Cancel button? We know
which file had been selected through the use of the fileName parameter.
The Long return value tells us whether the OK or Cancel button was
Let's look at each of the dialog functions one at a time.

The mdiDiaio9_fiieOpen Function

Dec l are Fun cti on mdlDialo9_fileOpen Lib _
"st dmdlb lt in .d l l" (ByVa l _
file Name As St r ing . ByVa l rFi l eH As Long . ByVal _
resource l d As Long . _

I Declaring MicroStation User Form Functions I

By Va l


suggestedFi l eName As String, _

filterString As Stri ng , _
de f aul t Di rectory As String,
titleStr i ng As String) As Long

Our first example prompts the user to select a file . We supply a file
extension, a default directory, and a dialog title.
Sub TestFi l eOp enA()
Dim strFName As String
Di m lngfhandle As Long
Dim l ngrid As Long
Dim retVal As Long
str FN ame = Space(255)
retVal = mdl Dialog_fileOp en(strFName , lngfhandle , lngrid, _
"*.d gn ", "C:\MicroStation VBA ",
"Ope n Fil e " )
Select Case retVal
Case 0 ' Open
strFName = Left(strFName. InStr(l. strFName . Chr(O) ) - 1)
MsgBox "File Selected: " & vbCr & strFName
Case 1 ' Cance l
MsgBox "No File Se l ected. "
End Select
End Sub


I Chapter 19: Using Mi croStation's Built-In User Forms I

When the procedure TestFi 1eOpe nA is executed, we see the following
dialog box:

.<:'.} ...."""._ .. "" . "J''''

'>.;~)....~~"'",~':l "~...,.o:;:~,,"""



Ope!! File -- C:\Micro~~a!io!I.VB.A\':~~"">.:J'.<"'~ '" " , '"


Look in:

Microstation V8A


.... ~ ~





() 1t f"


.. ",t


: ~~

~ filelO.dgn
~ filea.dgn
~ fileb .dgn
~ rast erdocs.dgn

ICl cd material
Ii::l Source Code

Re cent

"""' ... ,...-.. '\;~.

, ft) pics
IC::ll docs

~ file l , dgn
~ file2.dgn
~ file3.dgn
, ~file4.dgn
M~ Documents
. ~file8.dgn
~file9 . dgn

M~ Computer


File name:

Files of \ype:

~~~~.ti:'~E.~~_~~~.~::9.~~L ______________ ~:l




h... !

o apen as !ead-onl~






From this dialog we can see how m any of the Procedure Parameters are
used. We can see the Title, the Default Directory, the Filter (* .dgn) and
we can see that we did not supply a Default File Name because the File
Name is blank.
Up to this point, the only thing we have done is displayed a dialog box.
Now we must ask a few questions.
Did the user click the Open button or the Cancel button?
Select Case retVal
Case 0 ' Ope n
Case 1 ' Cancel
End Select
We look at the return value of the function to see if the Open button was
selected (resulting in a return value of 0) or if the Cancel button was
selected (returning a value of 1).
If the Cancel button was selected, there is little to do because the user
Cancelled the operation. In our example we display a MessageBox
stating "No File Selected".
If the user clicks the Open button, the next question is "Which file was

I Declaring MicroStation User Form Functions I


Use the variable strFName as a parameter when we call the Fi 1eOpen

function. The goal is to fill it with the path and file name that the user
selected. When a variable is declared as a string, it becomes an empty
string. It is a variable that can contain letters, numbers, and other
characters, but it is empty and it remains empty until we fill it with a
string. Before supplying the Fi 1eOpen function (and others like it), we fill
the variable we are using with spaces. This allows the function to
populate the variable and tell us the path and file name.


The Spa c e function fills the variable with the number of spaces specified.
If we provide a space-buffered variable to the 0pen Fi 1e function, the
variable will be filled with the fully qualified path of the file selected.
Supplying a variable with 255 spaces in it returns a variable with 255
characters, even if the path and file name are only 20 characters in
length. Using the Left function, we get everything to the left of the first
Null Character (ASCII Character of 0).
strFName = Le ft(strFName. InStr(l. strFName. Chr(O)) - 1)
MsgBox "File Selected :" & vbCr & strFName

After running the procedure

TestFi 1eOpenA and selecting a
file to open we see the

File Selected:
C:\Microstation VBA\file4.dgn

We can see that we are generating a File Open dialog box. The user
selects a file and clicks "Open" but the dialog box does not open the file.
lt only tells us which file was selected. It is up to us to open the selected
file or perform some other operation on it. In our first examples we will
only display the file name in a MessageBox.
Here is a slight variation on TestFi 1eOpenA . Only one change has been
Sub TestFi 1 eOpenB ()
Dim strFName As String
Dim lngfhandle As Long
Dim lngrid As Long
Dim retVal As Long


I Chapte r 19: Using MicroStation's Bu ilt-In User Forms I

strFName = Space(255 )
retVal = mdl Di alog_fi le Open(st rFN ame. l ng fhandl e. ln gr id. _
"test4 . dg n". "*.dgn ". "C: \ Mic roStat i on VBA ". "Open File")
Se l ect Case retVa l
Case 0 . Open
strFName = Left(strFName. InStr(l. strFName. Chr(O)) - 1)
MsgBox "File Selected: " & vbCr & strFName
Case 1 'Cancel
MsgBox "N o Fi l e Sele cte d ."
End Select
En d Sub

In this example, we are supplying a default file name of test4.dgn. The

dialog box is shown with this default file name in it.

ItJ Microstation VBA

Look in:

'j'!Q Fonts
!Q cd material

IC) Source Code


!!Q pics


!iCl docs

I ~file2.dgn
l ~file3.dgn


My Computer


II ;',

i ~file6 . dgn

. My Documents

~file9 .dgn
~fileb . dgn
~ rasterdocs.dgn

l~ file7.dgn

Ibij) fileS .dgn

. 1________________________________________
I ~I

.. __Ivtl'._~!:~g!~ _I

Files of lYpe:

[.mmm! _
.v ;,



MicroStation DGN Files (". dgnJ

Open as l eadonly

The File Open dialog box is used to

allow the user to select existing files.
In TestFileOp en B, we specify a
default file name of test4.dgn. This
file does not exist in the folder
shown above. If the user clicks
"Open': we see this dialog box:

.~-~"'" <"~."'Z'''..--/:V'''-:''''''' ~ . "J.~' ,~


;-J.; '"

!"';;;:"" ~~ "' .....)'~,~'j' ....

f~.~ - C.:~i~rostation.~~~\:.

'.1;, .. ~

test4 .dgn
File not found.
please verify the correct file name was given.

The File Open dialog box does not allow anyone to "Open" a file that
does not exist. So, if we get a return value of 0, we can be sure that the
file name returned exists.

I Declaring MicroStation User Form Functions I


Our previous example used a file extension, also called a file filter, of
"*.dgn". MicroStation understands that this file extension is a
"Micro Station DGN File" and shows this in the "Files of type" combo
box. TestFi 1eOpenC uses a file filter of "*.xls". This displays Microsoft
Excel files in the dialog box.
Sub TestFileOpenC ()
Dim strFName As String
Dim lngfhandle As Long
Dim ingrid As Long
Dim retVal As Long
strFName = Space(255)
retVal = mdlDialog_fileOpen(strFName. lngfhan dle. lngrid.
"*.xls ". "C:\MicroStation VBA ". "Open File")
Se l ect Case retVal
Case 0 ' Open
strFName = Left(strFName. InStr(l . strFName . Chr(O)) - 1)

MsgBox "File Selected :" & vbCr & strFName

Case 1 ' Cancel
MsgBox "No File Se l ected ."
' User hit the Cance l Button
End Select
End Sub

Now, instead of displaying MicroStation DGN files, Microsoft Excel

(XLS files) display.


I Chapter 19: Using Mi croStation's Bu ilt- In User Forms I


Look in:

Mic,ostation VBA

;Qed material
QSouree Code





~ Enums,xls





~MS VBA version 2.xls

~Microstation Object Model.xls

: ~Learn'ng

MS VBA.xls

M,D"""",,, ' ' -_ _ _ _._______________________--'

My Computer ,
!. File name,

' Files of jype:

.___My !'J.~~\oII.9.Lk__'

' "xis

. 0





0 pen as read-only

Let's look at another example that displays more than one type of file.
Sub TestFi 1 eOpenD ()
Dim st r FName As String
Di m l ng fh andle As Long
Di m lngrid As Long
Dim ret Val As Long
strFName = Space(255)

mdlD i a l og_fileDpen(strFName , lngfhandle, lngrid, "", _

"* . xls; * .mdb; *.dbf", "C:\MicroStation VBA ", "Open File")

Select Case retVal
Case 0 'O pen
str FName = Left(strFName , InStr(l, strFName, Chr(O)) - 1)
MsgB ox "File Selected:" & vbCr & strFName
Case 1 ' Cancel
MsgBox "No File Selected. "
' User hit the Cancel Button
End Select
End Sub

N ow, in addition to Microsoft Excel files, we are allowing for the

selection of Access Databases and .dbf database files.
The last FileOpen example we will work with not only allows for the
selection of a DGN file but opens the file in memory (not in the
MicroStation window) for program use.

I Declaring Mi croStation User Form Functions I


Sub TestFil eOp enE ( )

Di m strFName As St r i ng
Dim lngfhandle As Lorg
Dim lngrid As Long
Dim retVal As Long
strFName = Space(255)
retVal = mdlD i alog_fileOpenCstr FName . lngfhandle . lngrid . _
.... . "*. dgn ". "C: \ MicroStat i on VBA " . "Open Fi le " )
Select Case retVal
Cas e 0 'Open
st r FName = LeftCstrFNa me . InStrC1. strFName . Chr(O)) - 1)
Dim myDesFile As Design Fi le
Set myDe s File = OpenDesignFi l eForPr ogr am (s trFNa me. True )
MsgBox "Do so met hing with th i s fi le ."
myDes Fi 1 e. Cl ose
Case 1 'C ance l
MsgBox "No File Se l ected ."
End Se lect
End Sub

After the file is opened "ForProgram", we display a MessageBox and

then close the file.

The mdlDialo9_fileCreate Function

Just as mdlDialog_fileOpen does not actually open an existing file,
mdlD ialog_fileCreate does not actually create a new file. It only tells us
the file name the user selected or entered. In the event the file already
exists, the user is prompted to overwrite the file before we are returned
the file name and return value.
Here is the declaration for FileCreate. It looks and works very much like
the FileOpen function.

Declare Funct i on mdlDia l og_fileCreate Lib _

"stdmdlb l t i n . dll" (ByVa l _
f il eNa me As Str ing , ByVal r Fi leH As Long, _
ByVa l resou r celd As Long , _
ByVa l suggestedFi l eName As St r i ng , _
ByVa l f il terSt ri ng As Str i ng , _
ByVal defau l tDi r ectory As String, _


I Chapter 19: Using MicroStation's Built-In User Forms I

ByVal titleString As String) _
As Long
Let's take a look on how to use the function.
Sub TestFileCreateA()
Dim strFName As String
Dim lngfhandle As Long
Dim lngr id As Long
Dim retVal As Long
mdlDialog_fileCreate(strFName , lngfhandle, lngrid, _
"", "*.dgn ", "C:\MicroStation VBA", "Create File A" )
Select Case retVal
Case 0 'Open
strFName = Left(strFName , InStr(l, strFName , Chr(O)) - 1)
MsgBox "Fi l e Selected : " & vbCr & strFName
Case 1 ' Cancel
MsgBox "No Fi l e Se l ected. "
' User hit the Cancel Button
End Select
End Sub

The FileCreate dialog has the same look and feel as the FileOpen dialog

iCl MiClOstation VBA

Save jn:




- 7'
My Documents

i '0
if , Fonts
[!Q cd material
IlrtJ Source Code

t~il 0

f} CO


~ rasterdocs ,dgn


i Ibpics

,Iro docs

I i ~ filel.dgn
I i~ file2 . dgn

l ~file3.dgn

1 ~ file4.dgn

il file5.dgn
't IM

, !~ file6 , dgn
i ~ '1 d

I l ilfl e7,gn


: . ~file8,dgn
I ,a fIIe9.dgn
f ,lelO,dgn
My Compute,
, i filea. dgn

. .

" I~



,II -


My NetwOIk




Save as Jllpe:


.. _------:]
DGN Files ["dgnJ
,v ,



I Declaring Mi croStation User Form Fu nctions I

C:\Microstation V8A\file5,dgn already exists,

Do you want to replace it?




The return values of the Create File

dialog box work the same as with
the File Open dialog box. If an
existing file is selected, the user is
asked if the file should be overwritten.

If a file exists, the only way we are returned the file name and a return
value of 0 is if the user clicks "Yes" to over-write the existing file.
Entering additional file extensions in the File Filter parameter will
display the files associated with the entered extensions.

The mdlDialo9_fileCreateFromSeed Function

When we use the standard MicroStation File> New dialog box, we are
given the ability to select a seed file. We can provide the same
functionality by using mdlDialog_fileCreateFromSeed Function.

De cl are Fu nct i on mdl Dialog_fi l eCreateFromSeed Li b _

"st dmdlb lt in. dll" (By Va l f il eNa me As Str ing._
ByVal rFi l eH As Long. ByVal r eso ur cel d As Long . _
ByVal s ugge s t edF i l eName As Str in g . _
ByVa l fil t er Strin g As St rin g. _
ByVa l defau l t Dir ec t ory As St ri ng . _
ByVa l t i t l eStr in g As String. _
ByVal seedF il e As String. _
ByVal seedDirectory As Str i ng. _
ByVal seedFilter As String) As Long
Let's test the CreateFromSeed function now.

Te stFileCreateFromSeedA ()
Dim strFName As String
Dim lngfhandle As Long
Dim lngrid As Long
Dim retVal As Long
Dim strSeedFile As String
Dim strSeedDir As String
Dim strSeedFilter As String
strFName = Space(255)
strSeedFile = "seed2d.dgn"


I Chapter 19: Using MicroStation's Built-In User Form s I

strSeedOir =
"C: \Oocuments and Sett i ngs \A ll Us er s\Ap pl i c ation " &
"Oata \ O oc ~men t s\Bentiey \ Workspace\Syste m \Seed \ "

strSeedFilter = "*.dgn "

retVal = mdlOialog_f il eCreateFromSeed(strFName, lngfhandle, _
l ngr i d , "", "* . dgn ", "C: \M i c roS t at i on VBA ", _
"C re ate Fi l e from Seed ", s t rSeedF i le , strSeedOir , _
s trSeedFilter )
Se le ct Cas e re t Val
Case 0 ' Open
strFName = Left(strFName , InStr ( l , strFName , Chr (O - 1)
MsgBox "File Selecte d :" & vbCr & s tr FN ame
Case 1 ' Cance l
MsgBox "No Fil e Se l ect ed. "
'U se r hi t t he Cance l Button
End Select
En d Sub

Now, in addition to allowing for the selection of a file to create, a seed

file selection button is shown in the dialog.

Ie Jrom'Seed":',(::\MlCrostation
VBA\: ,"W;f't.,<~W'4.i<'!:V
;~~':' ' ;,;:
'f' '/ ;-. ':',.;Ji'V-,
': '"
~' (reate
.. '''j} ~;. ", ~J,~
:iN """,,,~. "i~ ,,..,,4 ,..
(..---t..-'.~, \". ~ ~ H' 5,;"(~'I,W"',,<,'''~~
.. 't. ""l~}..A<"1\{~.f_'"t;'o\}M~,~ )".)11 ~.:t.",,~

< j

IC) Microstation VBA

Save in:


EJ Ii ei~~~f~]


; ICl cd material
rQ Source Code
D pics

t;.lll 0


B' tm) T

~ lJ


rQ docs
I ~filel.dgn



My Documents

i ~ file2. dgn
! ~file3.dgn
I ~ file4 . dgn

! ~ fileS.dgn
~ file6.dgn

I ~ file7 . dgn
t ~ file8 .dgn

~file9. dgn
~ filelO . dgn

My Computer




... ~y. N ~twor~ ,. : Save as ll'pe:


I1--- ----- - --- ---- - - -,vi]

. MicroS tation DGN Files (". d g n j v_

1~:'D?::uments a~d.. Set~in~~~II. Users~pliGat~0!l




I Declaring MicroStation User Form Functions I


The mdlDialo9_openAlert Function

The mdlDialog_openAlert function generates a standard MicroStation
dialog box which allows the user to select "OK" or "Cancel".

Declare Function mdlDialog_openAlert Lib

"stdmd lblt in.dll"
(ByVal stringP As String) As Long
One parameter specifies what to display in the box and the return value
tells us whether the OK button or the Cancel button was clicked.
Sub TestOpenAlertA()
Dim re tVal As Long
retVal = mdlDialog_openAlert( "Standard Message Box")
Select Case retVal
Case 3 'OK
MsgBox "User cl icked 'OK '"
Case 4 ' Cance l
MsgBox "User c l icked ' Cancel '"
End Se lec t
End Sub

Standard Message Box

The mdlDialo9_openlnfoBox Function

The Information dialog box provides Information and so has no need of
a Cancel button. The only button shown is the OK button. It is useful,
however, because it follows MicroStation's native interface more closely
than a MessageBox.

Declare Function mdlDia log_openlnfoBox Lib

"stdmdlbltin.dl l " (ByVal _
stringP As String) As Long
Sub TestOpenlnfoBox ()
Dim retVal As Long


I Chapter 19: Usin g MicroStation's Bui lt-In User Forms I

retVal = mdlDialog_openlnfoBox( "This is a test. " )
Sel ec t Case retVal
Case 3 'OK
MsgBox "User cl i cked 'OK '"
End Select
End Sub
- .





'"/1 '"




This is a test.

After declaring the functions that display standard MicroStation dialog
boxes, using them is simple. There are other ways to display File Open,
File Create-type dialog boxes (such as using the Windows API) but
using the standard MicroStation dialog boxes is the preferred method
when developing in VBA and is so easy to implement.


Class Modules
Class modules have a variety of purposes with these primary beneficial
IB To encapsulate similar functionality into a single object.
IB To create an object with properties, methods, and events.
IB To create class modules specifically for custom collections.
Each benefit will be the focus of a section in this chapter. The code we
write will target the use of the MicroStation Built-in dialog boxes and
other MicroStation-specific objects and functionality.

In this chapter:
IB Encapsulating similar functionality
IB Creating objects with properties, methods, and events
IB Using class modules with collections



I Chapter 20: Class Modules I

We can design classes with the intent to encapsulate similar
functionality in a single class or object so we can reuse our code, which
is faster than rewriting code. Let's begin with a class named
clsUStationDialog that will be used to display the dialog boxes used in
the chapter for FileOpen and FileCreate.
Before we begin looking at the code, let's identify what we want this class
to do.

Display File Open Dialog for MicroStation DGN files .


Display File Open Dialog for Microsoft Excel files.


Display File Open Dialog for ASCII .txt files.


Display File Open Dialog for custom file extensions.


Property needed for File Name.


Property needed for Path Only.


Property needed for Path / File Name.


Property needed for Size of selected File.


Registry Entries to be used to store most recent path.


Registry Entries to be used to store most recent file.

You have accomplished each of the desired tasks already in this book.
The focus of this exercise will be to wrap it all into a single class.
We want to display.dgn files, .xls files, and .txt files. We also want to
display multiple custom file extensions in the dialog box. We could have
an "OpenDGN" method, an "OpenXLS" method, an "OpenTXT"
method, and an "OpenCustom" method. The main difference between
these methods would be the file extension(s) supplied. So, instead of
creating new methods for each file type we may want to browse, we will
work with one method, named OpenOi a log, that handles any number of
file extensions.
Let's begin by working with the file extensions. There are usually
multiple ways to accomplish the same task when working with VBA. We
will use a dynamic array in our class to store the desired file extensions.
We need to allow the user (in this case, it is us as developers) to add file
extensions and clear the file extension list.

I Encapsulating Similar Functionality I


' Gene ral Dec l a r ati ons

Dim pFileExts() As Str i ng
Private Sub Class_Initialize()
ReDim pF i 1e Exts(O)
Er.d Sub
Pub l ic Sub ClearFileExts()
ReD i m pFileExts(O)
End Sub
Public Sub AddF i leExt(Fi l eExt As Str i ng)
Di m I As Lo ng
Di m tmpFileExt As String
t mpFi leEx t = LCase(Rep l ace( Fi l eExt ,
"U ))
For I = 1 To UBo und(pF il eE xt s)
If tmpFileExt = pFileExts( I ) Then
Ex it Su b
End If
Nex t I
ReDim Preserve pF il eExts(UBound ( pFileExts) + 1)
pF ileE xts( UB ound(pF il eExts)) = tmpFi l eExt
End Sub

We declare the variable pFileExts as a dynamic array in the General

Declarations area of the class module. When the class is initialized, we
redeclare pFileExts with an upper-bound of zero (0). If we use the
C1 earFi 1eE xts method, we redeclare pFi leExts to an upper-bound of
zero. This clears the list of file extensions because we are always going to
leave the first element in the array (index of 0) an empty string.
When you attempt to add a file extension, first look at the existing
extensions to see if it already exists. If the extension that is being added
already exists, exit the procedure doing nothing to the file extension list.
If the file extension did not exist, increase the size of your array and
place the file extension in the upper-bound element of the array.


I Chapter 20: Class Modules I

You could write all of the code for this class and then try it out after all of
the code has been entered, but it is better to write smaller chunks of
code and test them before continuing. Here is the code to test the file
extension functio nality. It is placed in a code module.
Sub TestFileE xt s()
Dim MyUSD As New clsUStationDialog
MyUSD . Add Fi leExt ". dgn "
MyUSD.AddFileExt " .DWg "
MyUSD . AddFileExt "Xls "
MyUS D.ClearFileExts
End Sub

As we step through the code, you can see the effect of adding file
extensions by adding a watch to the variable MyUSD.

The first element in the array is always an empty string. As you add file
extensions, remove the period character and add it as lowercase.
When you call the C1ear Fi 1eExt s method, remove all elements except
the first element by redeclaring the variable pFileExts with an upperbound index of zero (0).
You are able to add file extensions to our class module now. You can also
clear the list. Give yourself the ability to discover what and how many
file extensions have been added by adding two properties to the class
module. The first property is "ExtCount" which tells how many file
extensions have been added to the class; the other is "GetExts" which
returns an array of all file extensions added to the class.
In the past, we created properties for our class modules by declaring a
variable as public. This works but there is a better way to work with

I Encapsu lating Simi lar Functionality I


The ExtCount property tells how many file extensions have been added
to our class. If you declare a variable named Ext Cou nt as public, you will
be able to read and write to the variable. This is not good because the
property's value should be based on the actual number of extensions
that have been added. You do not want to be able to write to the
property since it should be read -only.
Property Get ExtCount( ) As Long
ExtCount = UBound(pFileExts)
End Property

Now we are using true property code, because the property "ExtCount"
is based entirely on the number of file extensions we added to our class.
Specify the Read/Write capabilities of a property using Let and Get
statements. If you have a "Property Get" statement without an associated
"Property Let" statement, the property is read-only. If you have a
"Property Let" statement but do not supply an associated "Property Get"
statement, you are creating a write-only property. Write-only properties
are uncommon but can be used for storing confidential information
such as a password. You may want to be able to write to the property so
the class can use it but do not want to be able to read the property. And,
when you supply a "Property Get" as well as a "Property Let", you create
a read/write property.
Now get the file extensions with the "GetExts" property. This property
will be read-only, so use a "Property Get" statement.
Property Get GetExts() As Str in g()
If UBound(pFileExts) = 0 Then
Exit Property
En d If
Dim t mpGetExts() As String
ReOim tmpGetExts(UBound(pF ile Exts) - 1) As String
Dim I As Long
For I = 1 To UBound(pF i leExts)
tmpGetExts(I - 1) = pFileExts(I)
Next I
GetExts = tmpGetExts
End Property


I Chapter 20: Class Modules I

First check to see if any file extensions have been added. If the upperbound element of the array pFileExts is zero (0), nothing has been
added so immediately exit the property. Otherwise, create a new
tem porary dynamic array to hold the file extensions that have been
added. Since the first element in the array pFileExts is empty, loop
through pFile Exts elements beginning with the second element (an
index of 1) and loop to the upper-bound element in the array. After you
populate your temporary dynamic array, set the values into the property
"GetExts" which is returned to the code asking for the property.
Here is the code that asks for the GetExts property:
Sub TestGetExts ()
Dim MyUSD As New cl sUStationD i alog
Dim Fi l eExts() As Stri ng
MyUS D. AddF il eExt ". dgn "
MyUSD . Add Fil eExt "DGN "
MyUSD . Add FileExt ". DWg "
MyUSD . AddFi l e Ext "Xl s "
Fi leExts = MyUSD.Ge t Exts
End Sub

Notice how we are attempting to add the .dgn file extension twice. If the
AddFileExt method is working properly, you see only one dgn
Here is a view of a
Watch added to the
variable "FileExts".

Three unique file extensions were added and they are properly retrieved
by the GetExts property.
It is now time to allow the user to set and get the default directory for the

File Open dialog box. Make this property read/write using "Property
Let" and "Property Get".
Declare a variable named pDefFilePath in the General Declarations area
of your class.

I Encapsulating Similar Functionality I


Private pDefFilePat h As String

This variable will be used to store the default path.

Begin with the Property Get statement.
Property Get DefaultPath() As String
Defau l tPath = pDefF il ePath
End Property

This is easy enough. Place the value stored in the variable pDefFilePath
into the property "DefaultPath". Let's take a look at the "Property Let"
statement now for the DefaultPath property.
Property Let DefaultPath(strPathln As String)
pDefFilePath = strPathln
End Property

Here is the Let statement. Take the value supplied to us and place it into
the Private variable pDefFilePath.
The Let and Get statements work just fine. Let's try it out. This next code
should be placed in a code module.
Sub TestFilePathA ()
Dim MyUSD As New clsUStationDialog
MyUSD . DefaultPath = "abc : \/?test "
End Sub

If we run the code, "abc: \/?test" is set as the default path in our class. The
code worked exactly as designed. It took the value supplied and plugged
it in. So, if the code worked, we are in good shape. Right? Wrong.
Is "abc: \/?test" a legitimate path? At the time of the printing of this book,
it is not. So, what are we to do?
When a property is read/write, we could get away with declaring a
variable as public in the class. This allows us to read from and write to
the variable, making it behave like a property. But the properties of our
objects (classes) must be more than variables we can read from and
write to. Before any property is truly implemented, consider whether
you need to validate the supplied data. In this example, we need to make
sure the path exists. There are several ways to do this. Here is one way.


I Chapter 20: Class Modules I

Modify your "Property Let DefaultPath" statement.
Property Let DefaultPath(strPathIn As String)
If Dir(strPath l n, vbDirectory) <> "" Then
pDefFilePath = strPath l n
End If
End Prope r ty

Now, instead of blindly applying whatever path we are given, we check

to see if it is a valid directory. If it is, apply it to the variable
pDefFilePath. If it is not a legitimate path, do nothing.
Let's run TestFi 1ePathA and see what happens.
We get an error, not
because the path does not
exist, but because the
supplied path is not a
potentially legitimate path.
Let's create a new testing
procedure to verify.











Microsoft Visual Basic

Run-time error .'52': '

Bad file na~e

or nUmber

, ~nd


Sub TestFilePathB()
Di m MyUSD As New clsUStationDia l og
MyUSD.DefaultPath = "c : \test54321 "
End Sub

Now, even if the path "c:\test54321" does not exist, it is a path that could
be created because it meets the rules for drive letter and folder name. So,
even though we expect that we will feed our class legitimate paths, we
should handle error 52 just in case.
Property Let DefaultPath(strPathln As String)
On Error GoTo errhnd
If Di r(strPathln, vbD i rectory) <> "" Then
pDefFilePath = strPathln
End If
Exit Property
errhnd :
Select Case Err,Num ber
Case 52 ' Bad file name or number
End Select
End Pr operty

I Encapsulating Similar Functionality I


Now, even if we supply an illegitimate path, the program will not crash.
Let's implement the "DefaultFileName" Property.
Property Get DefaultFile() As String
DefaultFile = pJefFileName
End Property
Property Let DefaultFile(strFileln As String)
pDefFileNa~e = strFileln
End Property

Let's review what we have accomplished thus far. We have taken care of
the file extensions. We can set the default path. We can also set the
default file. This is all we need to do to begin work on displaying the
FileOpen dialog box.
We need to declare the function "mdlDialo~fileOpen" in the General
Declarations area of the class module as follows:
Private Declare Functio n mdlDialog_fileOpen _
Lib "stdmd l bltin . dll " (ByVal fileName As String . _
ByVal rFileH As Long. ByVal _
resourceld As Long . ByVal suggestedFileName As String. _
ByVal filterString As String . ByVal defaultDirectory As String._

ByVal titleStr i ng As Str i ng) As Long

Now that the function is declared, we can use it.

Sub OpenDi al og()
Di m tmp Filter As String
pRetVal = 1
tmp Filter = " * ." & Join(GetExts. " * ." )
pF i leNameSelected = Space(255)
pRetVal = mdlDialog_f i leOp en(pF il eNameSelected . O. O. _
pDefF il eName . tmpF i lter . pDefF i le Path . "Open Fi le " )
Select Case pRetVal
Case 1 ' Cancel
Case 0 ' Open
Dim tmpFile As St r ing
Dim xSplit As Variant
tmpFile = Left(pFileNameSelected . InStr(l . _
pFileNameSelected. Chr(O))


I Chapter 20: Class Modules I

xSplit = Split(tmpFile, "\ " )
pFileName = xSplit(UBound(xSplit))
xSplit(UBound(xSplit)) =
pFi l ePath = Join(xSplit, "\ " )
End Select
End Sub

We need a couple of additional properties for the class:

Property Get Se l ectedPath() As St r ing
SelectedPath = pFilePath
End Property
Property Get SelectedFile() As String
SelectedFile = pFileName
End Pro perty
Prope r ty Get OpenSuccess() As Boo l ean
Sel ect Case pRetVa l
Case 1 ' Cance l
Cas e 0 ' Open
Ope nSuccess
End Se l ect
En d Proper t y

We discussed a great deal of code so far with this class. Let's take a look
at the code in its entirety just to make sure we haven't missed anything.
' General Declarations
Private Declare Function mdlDialog_fileOpen Lib "std mdlbltin . dll " (ByVal _

fileName As String, ByVal rFileH As Long, ByVal _

resourceld As Long, ByVal suggestedFileName As String, _
ByVal filterString As Str i ng. ByVal defaultD i rect o ry As String. _

ByVal titleString As String) As Long

Pri vate

pFilePath As String
pFileName As String
pDefFi l ePath As String
pDefFileNa me As String
pFileNameSelected As String
pRetVal As Long

I Encapsulating Similar Functionality I


Private pF i leExts() As St r i ng
Property Get SelectedPath() As String
SelectedPath = pFilePatr
End Property
Property Get SelectedFile() As String
SelectedFile = pFileName
End Property
Property Get OpenSuccess() As Boolean
Select Case pRetVal
Case 1 ' Cancel
Open Success
Fa 1 se
Cas e
Open Success
End Select
End Property

Sub Openoialog()
Dim t mpFilter As String
pRetVal = 1
tmp Filter = " *." & Join(GetExts, " *. " )
pFileNameSelected = Spac e (255)
pRetVal = mdloialog_fileOpen(pFileNameSelected, 0, 0, _
poefFileName, tmpFilter, poefFilePath, "Open File")
Select Case pRe tV a l
Case 1 ' Cancel
' Open
Dim tmpFile As String
Dim xSp l it As Variant
tmpF i le = Left(pF i leNameSelected, InStr(l, _
pF i leNameSe l ected, Chr(O))
xSplit = Split(tmpFile, "\ " )
pFileName = xSpl i t(UBound(xSplit))
xSpl i t(UBound(xSplit)) =
pFilePath = Join(xSplit , "\ " )
End Select
End Sub


I Chapter 20: Class Modules I

Pr ope r ty Get Defau l t Fi le() As St ri ng
DefaultF i l e = pDefFileName
End Property
Property Let DefaultFile(strFileIn As String)
pDefFileName = strFileIn
End Property
Property Get DefaultPath() As String
DefaultPath = pDefFilePath
End Property
Property Let DefaultPath(strPathIn As String)
On Error GoTo errhnd
If Dir(strPa t hIn , vbDi r ectory) <> nn Then
pDefF i lePath = strPathIn
End I f
Exit Pr operty
Select Case Err.Nu mber
Case 52 ' Bad fi l e name or number
End Select
End Property
Property Get ExtCount() As Long
ExtCount = UBound(pFileExts)
End Property
Property Get GetExts() As String()
If UBound(pFi l eExts) = 0 Then
Exit Property
End If
Dim tmpGetExts() As String
ReDim tmpGetExts(UBound(pFileExts) - 1) As String
Dim I As Long
For I = 1 To UBound(pFileExts)
tmp GetExts(I - 1) = pFileExts(I)
Next I
GetExts = t mpGetExts
End Property

I Encapsulating Similar Functionality I


Privat e Sub Cl ass_ I nitialize ()

ReD i m pFil eExts (O )
End Sub
Pub li c Su b Cl earFileExts()
ReDim pFileExts(O)
End Sub
Public Sub AddFileExt(F i leExt As Str i ng )
Dim I As Long
Di m tmpF i l eExt As String
t mpFi l eExt = LCase(Rep l ace( Fi l e Ext.
"H ) )
For I = 1 To UBou nd(p File Exts)
If tmp Fil eExt = pFil eExts( I ) Then
Exit Sub
End If
Next I
ReDim Preserve pFileExts(UBound(pFileExtsl + 1)
pF i leExts(UBound(pF i leExts)) = tmpFileExt
End Sub

The code that uses this class module is in TestShowDialogA.

Sub TestShowDialogA()
Dim My USD As New clsUStationDialog
MyUSD.AddFileExt "dg n"
"c:\ "
MyUSD.D ef au l t Path
MyUSD . DefaultF i l e = "test.dgn "
MyUSD . OpenD i alog
Se l ect Case MyUSD.OpenSuccess
Case True
MsgB ox My USD.SelectedPath & My USD. Sele ct edFile
End Sel ect
End Sub

Let's try a variation on Tes tShowDi a1ogA just to make sure everything is
working properly.
Su b TestShowDialogB ()
Di m MyU SD As New cl sUSt ati on Dial og
MyUS D.Ad dFil e Ext "dgn "


I Chapter 20: Class Modules I

MyUSD.AddFileExt "dwg "
MyUSD .A ddFile Ext "dx f "
" c:\MicroStatio~ V8A "
MyUSD.DefaultFile = "test.dgn"
Select Case MyUSD.OpenSuccess
Case True
Msg80x MyUSD.SelectedPath & MyUSD.Sele ctedF ile
End Select
End Sub

Each file extension added to the class displays in the FileOpen dialog





C l1r fjl



[ ,-_._._._..._--_. __. _-_._-----_. _. _---_... _. __ ... _--_ ......_---_. __ . . _. __._----_.._._--_..__...__ . __....._------'-'j

f !~ Fonts
ED file 7.dxf


! ifO cd material


HD source Code

@ Ij~ file1.dgn

irj ~ file2 . dgn


l~ file3 .dgn


!~ file4 . dgn

~ file8.dgn

~ file9.dgn


~;: :!~~~~n
~ fileb.dgn
~aa . dgn

~ rasterdocs .dg n

i l~ file5.dgn


~ ,~ file6 . dgn
y ocumenls j~ file 7.dgn

I!m file7.dwg
Ii' - - - - - - - - - - - - - - - -- - - - ---'

My Compuler

f Filename

., My


! Files of lI'pe:



t~~~~::5!"'~~::.d~f ___________________________~_~J

Open .



Open as leadonly

Our focus in this section is grouping specific types of functionality into

a single class module. We already added functionality for FileOpen, now
let's add FileCreate functionality.
First declare the Function md 10i a109_ f i 1eere ate
Declarations area of the class module.


the General

Private Declare Fu nction mdlD i alog_f il eCreate Lib

"stdmdlblt in .dll" (ByVal _
fileName As String. ByVal rFi l eH As Long._
ByVal resourceld As Long.
ByVal suggestedFileName As String .

I Encapsulating Similar Functionality I


SyVal f ilterString As String, _

SyVal defa ultD i rector y As String,
SyVal titleString As String) As Long
Next, create a new method in the class module using variables and
properties that have already been declared.
Sub CreateDialog()
Dim tmpFilter As String
pRetVal = 1
tmpFilter = "*." & Join(GetExts . " *.")
pFi l eNameSelec t ed = Space(2 55 )
pRet Val = mdlDialog_f i leCreate(pF i leNameSelected, 0 , 0, _
pDefF i le Name , tmpF i lter . pDefFi l ePath. "Cr eate File " )
Select Case pRetVal
Case 1 . Cancel
Case 0 ' Open
Dim tmpFile As String
Dim xSplit As Variant
tmpFile = Left ( pFileNameSelected, InStr(l,
pFileNameSelected, Chr( O))
xSplit = Split(tmpFile. "\ " )
pFi 1 eName = xSp l it (UBound (xSpl it) )
xSplit(UBound(xSplit)) =
pFilePath = Join(xSplit. "\ " )
End Select
End Sub

It is now time to test the CreateDialog method of our class.

Sub TestShowDialogC ()
Dim MyUSD As New clsUStationDialog
MyUS D.AddFileExt "dgn "
MyUSD.Defau l tPa t h = "c : \ "
MyUSD . Defau lt Fi 1 e = "test. dgn "
MyUSD . CreateDialog
Select Case MyUSD . OpenSuccess
Case True
MsgBox MyUSD . SelectedPath & MyUSD.SelectedFile
End Select
End Sub


I Chapter 20: Class Modules I

TestSh owDialogC is almost an identical copy of TestShowDialogA. The
only difference is we are using CreateDialog instead of OpenDialog.
Everything else is the same.
Copy and paste TestShowDialogB to create TestShow Dialog D and make
the same change.
Sub TestShowD i alogD ()
Dim MyUSD As New clsUStationDia l og
My USD . AddFi l eExt "dgn"
MyUSD.AddFileExt "dwg"
MyUSD.AddFi l eExt "dxf"
MyUSD.Defau l tPath = "c:\MicroSta t io n VBA"
MyUSD.Defa ul tFile = "test.dgn"
Select Case MyUSD.OpenSuccess
Case True
MsgBox MyUSD.SelectedPath & MyUSD.SelectedFile
End Select
End Sub

Once again, the only change using CreateDi alog instead of OpenDi alog.
Let's expand on Tes tShowDi a1ogA in a new procedure TestShowDi alog E.
Sub Test ShowDia log E()
Dim MyUSD As New clsUStationDialog
MyUSD . AddFileExt "dgn "
"c: \ "
MyUSD . De f aultFile = "test.dg n"
Select Case My USD.OpenSuccess
Case True
MsgBox "Open" & MyUSD.SelectedPat h &
Case False
If MsgBox( "Create a new file? ", vbYesNo)
vbYes Then
My USD.CreateDialog
If MyUSD . OpenSuccess
Tr ue The n
Msg Bo x "Create " & MyUS D.Selecte dPath &
My USD.Se l ectedFi l e
End If
En d I f

I Encapsu lating Similar Functiona lity I


End Select
End Sub

What are we doing in TestSh ow DialogE? We prompt the user to select a

file to open. If the user selects an existing file, we display the file name in
a MessageBox. If the user clicks the Cancel button, the
MyUSD.OpenSuccess property will be False, so we to ask if the user
wants to create a new file. If the user answers No, do nothing. If the user
answers Yes, display the FileCreate dialog box and allow the user to
create a new file. If the user successfully creates a file using the
FileCreate dialog box, display the file path and name in a MessageBox.
Notice how easy it is to add Fi 1eCreate functionality to the class module
and to use the Crea teDi alog method in the procedure. When you outline
functionality to include in class modules, you can expand them quickly
and easily.
From this point on, any time you need to use the FileOpen or FileCreate
dialog box, you can use the class you just created. But how can you use it
in future projects? Do you need to copy and paste the code each time
you want to use it? There is a better way.
From the VBA Project Window (not to be confused with the VBA
Project Manager), right-click on the class and select Export File.

Chapter20 Propf),rties ...

i [lsert
Import File ...

&emove ellsUStal:i6nDialoo.
~rint" .

. Doc!>;ble


I Chapter 20: Class Modules I

Select a location and save the file.

Save in:

~.!:!!croslalion V8A


!Q Source Code
IO docs

File name:

Save as I~pe:


[Clas:.~~.:rcl~l._______. _ ~

Lookin: eJ Microslalion V8A

' ~ J::" rr:m~
ftiSl Font;--------- ----------- ------------- -- - -- -----.----'-----


File> Import .

!t:Jlcd material
Source Code
IC:l docs




File name:
Files of I~pe:

N OW, as you create a

new VBA Project,
use the VBA menu

Open "



Help .

Select the class just

exported and it will
be imported into the
new project.



This concludes the discussion on creating class modules to wrap similar

functionality into a single object or class. Let's look at another way to use


MicroStation gives us a large number of classes (or objects) with which
to work. For example, we used the LineElement object., which has
properties such as the Start Point and EndPoint, but not a MidPoint. Nor
does it have ChangelnX, Change In Y, or ChangelnZ properties. Let's

I Creating Objects with Properties, Methods, and Events I


create a new clsLineElem Class and include a few properties that are not
a part of the MicroStation Object Model.

EndP o int As Poi nt3 d

To simplify matters, we will have our Start and End Point properties
implemented by declaring two variables as public within the General
Declarations area of the class module.
To make sure things are working correctly, create a test procedure to
work with the class.
Create a MidPoint property next.
Sub TestNewLineA ()
Dim myLE As New clsL i neElem
End Sub

When you run this macro, you will see the following MessageBox:
~~!~~'11?}2~:~~~~"'r"-:7'~~~~~F 7TP7-::~';7:~ ~~: < ""~~;~~~
A,c;: f,'" <,< y. 'F~' ': ,,'" :.ttM('{"
0"J.~1~.t.;;'.~../ .;""{~'vd~>]~''Y d~\, .fj>){:.% ~

,U}1~!OS~,-t!1~!lf~;;Q~"!~ ~t.t4~;g./:;Y;'}""'''JkjfrM''J''. ji"< ~I~"


Compile error;
Constants, fixed-length strings, arrays, user-defined types and Declare statements not allowed as Public members of object modules



What is the problem? In the General Declarations area we declared two

variables as public with a type of Point3d. The Point3d variable type is
not a standard VBA variable type, so we cannot declare it as we have.
Change the declaration to private and change the name as follows:
Private pStartPoint As Point3d
Private pEndPo i nt As Point3d

Now when you run TestNewLineA, we do not get the error.

So how do we implement the StartPoint and EndPoint properties? After
the pStartPoint and pEndPoint variables are declared in the General


I Chapter 20: Class Modu les I

Declaration area, we could implement the StartPoint and EndPoints
properties like this:
Property Let StartPoint(StPt As Poi nt3 d)
pStartPoint = StPt
End Property
Pro perty Get Star t Po i nt() As Po i nt3d
StartPoint = pStartPo i nt
End Prope r ty
Property Le t EndPoint(EnPt As Poi nt3d)
pEndPo i nt = EnPt
End Property
Pr op er t y Get EndPo i nt ( ) As Po i nt3d
En dP oint = pE ndPo i nt
End Pr op e r t y

Let's test the properties by creating a procedure in a code module.

Sub TestNewLin eB()
Di m my LE As New clsL i neElem
myLE . StartPoint = Point3 dFromXYZ(4, 4 , 4)
Point3dFromX YZ(10, 10, 10)
End Sub

The code runs without any

errors. To make sure, place a
Stop (or Break Point) on the
End Sub line, which allows us
to add a watch to the variable
myLE. Here's what we get:



Point 3d

Now that the StartPoint and

EndPoint properties seem to
be working, let's move on to
the MidPoint property. The
MidPoint property is calculated from the StartPoint and EndPoints.
Since it is calculated, make it read-only by implementing a "Property
Get" without an associated "Property Let".

I Creating Objects with Properties, Methods, and Events I

Property Get MidPoint() As Point3d
Dim tmpPoint As Point3d
tmp Poi nt.X
Star tP oi nt.X + ( En dPoin t. X
StartPoint . Y + (EndPoint. v
tmpPoint . Z StartPo i nt . Z + (EndPo i nt . Z
tmpPo i nt
End Pro pe rty


St ar t Point.X ) / 2
StartPoint.Y) / 2
StartPo i nt . Z) / 2

A walk through the TestNewLineB procedure displays the results of the

MidPoint property.




The calculations seem to be working correctly. Let's add a few more

properties: ChangeInX, Change In Y, and ChangeInZ.
Property Get Change ln X() As Double
ChangelnX = pEndPo i nt . X - pStartPoint.X
End Property
Property Get ChangelnY() As Double
ChangelnY = pEndPoint.Y - pStartPoint.Y
End Property
Property Get ChangelnZ() As Double
ChangelnZ = pEndPoint.Z - pStartPoint.Z
End Property

Add two more properties: the LineAngleRads and LineAngleDegs

Property Get LineAngleRads() As Double
LineAngleRads = Atn((pEndPoint.Y - pStartPoint.Y) / _


I Chapter 20: Class Modules I

(pEndPoint.X - pStartPoint.X))
End Property
Property Get LineAr.gleDegs() As Double
LineA~gleDegs = Degrees(LineAngleRads)
End Property

Enough with properties. Let's look at creating methods. The first one
will be DrawLine.
Sub DrawL i ne()
Di m LineElem As LineE l ement
Set LineElem = Create Li neElement2(Nothing. _
pStartPoint . pEndPoint)
ActiveModelReference.AddElement LineElem
En d Sub

The DrawLine method creates a line element based on the pStartPoint

and pEndPoint variables. The line is then added to the active model in
MicroStation. Now for an example, add the procedure TestNewL i neD to a
code module:
Sub Test NewLineO ()
Dim myLE As New clsLineElem
myLE.StartPoint = Point3dFro mXY Z(0. O. 0)
myLE .End Point
Point3dFromXYZ(10. 10. 0)
my LE. Dr awL i ne
Point3dFromXYZ(-10. 10.0)
myLE . DrawLine
En d Sub

Set the Start and End Point values, then we use the DrawLine method.
That is simple enough. Try creating another method for our class
This is a method named "DrawLinePerp". It draws a line perpendicular
to the one defined by the pStartPoint and pEnd Point properties of the
class through the midpoint of the line.
Sub OrawLinePerp ()
Dim PerpSt As Point3d
Dim PerpEn As Point3d
Dim PerpMid As Point3d

I Creating Objects with Properties, Methods, and Events I


Di m Line Ang As Doub le

Di m LineLength As Do uble
LineAng = LineAngleRads
PerpMid = MidPoint
LineLength = Point3dDistance(pStartPoint. pEndPoint)
Point3dAddAngleD i stance(PerpM i d . Li neAng + Pi / 2.
LineL ength / 2 . 0)
Pe r pEn = Poin t 3dAddAng l eD i sta nce ( PerpMi d . Line Ang - Pi / 2. _
LineL en gth / 2 . 0)
Dim Li neElem As LineE:ement
Set LineElem = CreateLineElement2(Nothing .
Perp St . PerpEn)
Act i veModel Reference . Add El ement LineE l em
End Sub

Now, to test the DrawLinePerp method, create a new procedure in a

code module.
Sub TestNewLineE ()
Dim myLE As New clsLineElem
myLE.StartPoint = Point3dFromXYZ(O. O. 0)
myLE .E ndPoint = Point3dFromXYZ(8. 8. 0)
myLE .Draw Line
End Sub

Two lines are drawn. One from (0, 0, 0) to (8, 8, 0) and another
perpendicular to the first one through the mid-point of the first one.
Add another method to the class module. First the code, then the
Sub DrawCircle ()
Dim CircE l em As EllipseElement
Di m Ro tM atrix As Matr i x3d
Set Ci rcElem = CreateE ll ipseElement2(Noth i ng. MidPoint. _
Po i nt3dDistance(pStartPo i nt . pEndPoint) / 2. _
Po i nt3dD i stance(pStar t Po i nt . pEnd Point) / 2 . RotMatrix)
Act i veMode l Reference . Add Element Ci rcElem
End Sub

DrawC ire 1e draws a circle through the end points of the LineElem object.


I Chapter 20: Class Modules I

One of the great things about the class module is that we can add
methods and properties whenever we see a need. At this point, the three
methods in our class module may be all we need in the application right
now. Later, if the next application needs a m ethod named
"DrawLineSegments" to specify the number of line segments between
the start point and end point, we can add it. We can add any number of
methods and properties to our class module but we need to be careful.
Be even more cautious when mo difying existing methods and
For instance, we have a property named StartPoint declared as a Point2d
type that works in many circumstances. Changing it to a Point3d type
might seem as though it would support more methods and properties.
But changing a variable from a Point2d to a Point3d may cause code
already using the Class Module to fail because functions or procedures
in the existing code expect a Point2d type. Careful planning helps avoid
making changes to class module properties and methods after they are
in use.
We covered properties and methods, but what is an event? An event is
triggered when a specific activity takes place. We usually write code in
events to react to user interaction.
Let's create an event in our class to be triggered whenever a line is
created and drawn in MicroStation.
In the General Declarations area of the class, the following code creates
an event:
Public Event Li neAdded(Added Li ne As Line Element)

An event named "LineAdded" is now part of the class. But how do we

trigger this event?
Su b Dra wL ine()
Dim Li ne Elem As LineElement
Set Line El em = Cr ea t eL i ne El ement2( Noth i ng ,
pS ta r t Poi nt , pEndPoint)
ActiveModelReference . AddElement LineElem
RaiseEvent LineAdded( LineElem)
End Sub

I Creating Objects with Properties, Methods, and Events I


Whenever you want to trigger an event, use the RaiseEvent statement.

Supply the variable LineElem as a parameter, so when you capture the
event in code that uses this class module, you are given the line element
that was drawn in MicroStation.
Now that we have an event declared and raised in our class module,
what's the next step? How do we get code into the event so that when a
line is added, we can do something? That is a good question. To use the
event we just created, declare a variable as the class clsLineElem in
either another class module or a form "WithEvents". It is easier to
demonstrate the use of this event by using a fo rm.
Insert a new form into your VBA Project. Declare a variable
"WithEvents" in the General Declarations area of the form's code as a
clsLineElem Object.

When you declare a variable this way, the variable's events are available
like the events of a CommandButton.
The object myLE (it uses the variable name, not the class name) is now
available in the Object ComboBox of the form's code.

cl s Lin e El em

> ..


I Chapte r 20: Class Mod ul es I

After selecting myLE in the Obj ect ComboBox, you can see the
clsLineElem Events in the Procedure ComboBox.

sub mYLE_LineAdded(AddedLine As LineElement)

Now use the event created in the clsLineElem class.

Let's review the four components required to make use of an event.

Declare the event with "Public Event" in the class module.

2 Raise the event with "RaiseEvent" in a class module property or


3 Declare a variable "WithEvents" as the class in the General

Declarations area of a class or form .

Enter code in the event in the class or form.


We used class modules to wrap functionality and to create new objects
through the use of properties, methods, and events. You can also use
classes with custom collections.
A collection is a container that holds objects. Custom VBA collections
can contain any type of object. Let's look at an example of creating a new
collection to hold MicroStation Level objects.
Sub TestCollectionA ()
Dim myColl As Ne w Co llect ion
Di m my Level As Leve l
For Each my Level In ActiveMode l Reference . Levels
myColl .Add myLeve l
End Sub

I Using Class Modu les with Collections I


The declaration fo r the Add m ethod of a collection is:

Sub Add(! t em, [Key ] , [ Before ] , [A f ter])

In our first example, we added Levels to a collection by supplying the

Item (a Level in this case) to the Item parameter of the Add method. We
did not specify a key or a position. We will look at the Key, Before, and
After parameters later.
Add a Watch to the collection myColi , then step through the
Te st eo 11 ect i onAprocedure line by line by pressing the <F8> key.

msdLevelElementAcce ssAIl

MsdLevelElementAccess t

Linest yleilineStyle






IsFr omLe velLibrary








IsLa eked

"Levell "








Parent Level












~ em

Variant lObjectilevel


V ariantlObjectilevel


Each added object shows up as an item in the collection. You can see the
type of object in the Type column. All of the object's properties display
with their values.

Accessing Objects in a Collection

After objects have been added to a collection, you can access them
different ways.

TestCo ll ect i onB ()

Dim myColl As New Collection
Dim my Level As Level


I Chapter 20: Class Mod ul es I

For Each myL ev el In Activ eMode l Refere nce . Lev els
my Coll . Add myLev el
'Now that the collection is populated,
' access the objects with For Each . . Next
Dim myLeve 12 As Level
For Each myLeve 12 In myCo l l
Debug .Pri nt my Le ve 12 . Name
End Sub

One way is to use the For Ea ch ... Next statement. The example above
populates a collection with the levels in the active model. Then you
access each object in the collection using For Each ... Next .
When objects are added to a collection, the properties, methods, and
events of the objects are live. That is they are not static variables holding
values. Be careful when accessing the objects that you do not modify
properties inadvertently.
Use For Ea ch ... Ne xt for easy access to each object in the collection.
Here is another way to access objects in a collection:
Sub TestCollectionC ()
Di m myCo ll As Ne w Coll ec t io n
Dim myLev el As Level
For Ea ch myLevel I n ActiveMo de l Reference . Leve l s
myCol l .Add myLevel
' Now t hat t he collection is populated ,
' acce s s t he objects us in g t he i tem I ndex .
Dim myL eve1 2 As Lev el
Di m I As Long
For I = 1 To my C01 1 . Count
Set my Leve l 2 = my Co l 1 ( I )
Debug. Pr i nt my Leve12 . Name
End Sub

We are still cycling through each item in the collection but now we are
accessing each item by addressing it by the item's index in the collection.
Sub TestCollectionD ( )

I Using Cla ss Modules with Collections I


Dim myCol l As New Collection

Di m my Level As Level
For Each myLevel In ActiveModelReference.Levels
myCo11 .Add myLevel
'N ow that the collectio n is populated,
'access the objects using the item Index.
Dim myLeve12 As Level
Dim I As Long
For I = my Col 1 . Co unt To 1 St e p - 1
Set my Level 2 = my C0 11 ( I )
Debug.Print myLeve12.Name
End Sub

Now, instead of addressing the items from the top of the list down,
address the items from the bottom up by using a For ... Next statement
using "Step -1".
Let's take another look at the Collection Add method declaration:
Sub Add( Item , [Key]. [Before]. [After])

If you add objects to a collection and provide a key (a unique string in

the collection), you can address objects through their key as well as their
index. Here is an example of adding levels to the collection myCol1 and
using the level's name as th e item's key in the collection.
Sub TestCollectionF()
Dim myColl As New Collection
Di m my Level As Level
For Each myLevel In ActiveModelReference.Levels
myCol l .Add myLevel . myLevel.Name
' Now that the collection is populated.
' address a Level by it's Key.
Dim myLeve12 As Level
Set my Leve12 = myCo ll ( "Default")
MsgBox myLeve12.Number
End Sub


I Chapter 20: Class Modules I

We know each MicroStation DGN file has a level named "Default".
Because we may not know what its index in the collection will be, access
the level in the collection through the object's key.
Keys must be unique strings. In other words, no two objects in a
collection can have the same key. Keys are not case-sensitive so you
cannot have a key of "test" and a key of "TEST" in the same collection.

Removing Objects from a Collection

You can remove objects from a collection using its key or its index.
When removing by index, objects with an index higher than the object
removed decrease their indexes by one. For this reason, to clear an entire
collection, begin with the highest index and work to the lowest (always
an index of 1) using a "Step - 1" statement.
Here is an example of populating a collection with levels, then removing
all of the Items using the Remove method.
Sub TestCollectionE()
Dim myCo ll As New Collection
Dim my Level As Level
For Each myLevel I n ActiveModelReference.Levels
myCo ll .Add myLevel
'Now that the co l lect ion is populated,
'remove the objects using the item I ndex.
Di m I As Long
For I = my Co l 1 . Co unt To 1 St e p - 1
myCo ll .Remove I
Nex t
En d Sub

Using Custom Class Modules

Let's use the clsLineElem class now.
Sub TestCo l lectionG ()
Dim myLE As c l sL i neElem
Di m XYA As Double
Dim XYM in As Double
Dim XYMax As Double

I Using Class Modules with Collections I


Dim myCo ll As New Co ll ec ti on

XYMi n = 0
XYMax = 10
For XYA = XYMin To XVMax
Set myLE = New clsLineElem
myLE.StartPoint = Point3dFromXY(XYA, XYMin)
myLE . EndPoint = Point3dFromXY(XYA, XYMax)
my Col 1 . Ad d my LE
Set myLE = Nothing
Set myLE = New clsLineElem
myLE . Start Poi nt = Po i nt3dFr omXY(XY Min, XYA)
my LE.EndPoint = Point3dFromXY ( XY Max , XYA )
myCo l l . Add myLE
Set myLE = Nothing
Next XYA
For Each myLE In myColl
End Sub

In this example, we create multiple clsLineElem objects and add them to

a collection. Then we use a For Each ... Next statement to access each
clsLineElem object in the collection and use the Drawl i ne method of
each object in the collection.
After the macro
is run, we
should see a
grid like this:

Notice that if we do not use the DrawLine Method on each clsLineElem

Class, none of the lines will be drawn.
Let's take a look at another example of using the clsLineElem class in a


I Chapter 20: Class Modules I

Sub TestCollect i onH ()
Dim myLE As clsLineElem
Dim XYA As Do~ble
Dim XYMin As Double
Dim XYMax As Double
Dim xClose As Double
Dim yClose As Double
Dim myColl As New Collection
XYMin = 0
XYMax = 10
For XYA = XYMin To XYMax
Set my LE = New cl sLineElem
my LE.StartPoint = Point3dFromXY(XYA. XYMin)
myLE.EndPoint = Point3dFromXY(XYA. XYMax)
my Col 1 . Ad d my LE
Set myLE = Nothing
Set myLE = New cl sL in eElem
myLE.StartPoint = Point3dFromXY(XYMin. XYA)
myLE . EndPoint = Point3dFromXY(XYMax. XYA)
my Col 1 . Ad d my LE
Set myLE = Nothing
Next XYA
xClose = CDbl(InputBox( "Enter X Value:"))
yClose = CDbl ( I nputBox( "Enter Y Value: " ))
For Each myLE In myCol l
If myLE.StartPoint.Y = myLE.EndPoint.Y Then
Set myLineE le m = CreateLineE l ement2(Nothing. _
myLE.StartPoint . Point3dFromXY(xClose . yClose

ActiveMode l Reference . AddElement my LineElem

Set myLineElem = CreateLineElement2(Noth i ng. _
myLE.EndPoint . Point3dFr omXY(xClose . yClose

ActiveModelReference . AddElement myLineElem

End If
End Sub

In this procedure, we create a collection of clsLineElem objects. This is

the same code that generated the grid in a previous example. Then we
look at each clsLineElem in the collection and look at the Y components
of the Start and End Points. If they are equal, we know we are dealing
with a horizontal line. For each myLineElem Object that is horizontal,

I Using Class Modules with Collections I


we draw a line between the entered X and Y values and the Start and
End Points of the myLineElem object.

Enter X Value:


[ Cancel

Enter Y Value:



I Cancel I

The user is prompted to enter an X and Y value. Then we draw lines

between the entered X, Y pair and the Start Point and End Point of each
c lslineEle m object in the collection.


I Chapter 20: Class Modules I

This example demonstrates how we can place multiple objects in a
collection, then use the collection to evaluate objects within the

Classes can encapsulate similar functionality, create objects with unique
properties, methods, and events, and group similar objects for a variety
of purposes into collections. The more you implement classes in your
programs, the more you are following the ideals of object oriented


VBA for CAD Managers

VBA is not just for programmers and not just for MicroStation users it is a powerful tool for CAD Managers as well.
In this chapter:
[8 Using VBA for maintaining standards
[8 Using VBA to facilitate cross-company standards
[8 Using VBA to track time in drawings
[8 Auto-loading and auto-running macros
[8 Protecting projects
[8 Distributing VBA projects
[8 Working in high-security mode



Because standards differ from company to company, VBA can be an

important part of customizing and maintaining standards for your
individual company. Let's talk about some ways to use VBA to review
DGN files and report problems.



I Chapter 21: VBA for CAD Managers I

NOTE: For more information on maintaining standards, look at the
Standards Checker Interface which provides powerful functionality
with built-in reporting capabilities.
The first thing to create is a procedure that looks for unsupported levels.
Sub FindUnsupportedLevel s()
Dim GoodLevels(O To 4) As String
Dim GoodLevelsJ As String
Di m my Leve 1 As Leve 1
Goo dLevel s( O)
Goo dLevel s( l )
GoodLeve l s(2)
Good Le ve l s(3 )
GoodLevelsJ = UCase( "- " & Join(Good Levels. "- " ) & "- ")
For Each my Level In Act i veDesignFi l e . Levels
If InStr(l . GoodLevelsJ. "- " & UCase(myLevel . Name) & "_ oJ - 0 Then
Debug. Pri nt "Unsupporte d Level Found : " & myLevel . Name

End If
End Sub

Five supported levels are specified. Join the array of level names
separated by tilde characters (- ). Then look in the joined string using
the InStr function. If the level is not found in the supported level name
string, print the level name to the Immediate Window.

Unsuppo rted
Unsuppo rted
Unsuppo r te d
Unsuppo rted
Unsuppo rt e d

<::F I

Le vel

Fo und:
Fo und:
Fo und:
Fo und:
Fo und:
Fo und:

Level 1
Level 20
Level 23
Le vel 38
Level 39
Level 40
Level 41
Level 42
Leve l 47




I Using VBA for Maintaining Standards I


This works well for finding un-supported levels. What do you do if a file
is supposed to have levels but they aren't there? Look for missing levels
Sub FindMissingLevels ()
Di m GoodLevels(O To 4) As Str i ng
Dim Le velFound(O To 4) As Boolean
Di m my Le ve 1 As Level
Dim I As Lon g
Goo dLev el s(l)
GoodLeve l s(2)
"GAS "
GoodLevelsJ = UCase("- " & Join(GoodLevels, "- " ) & "-")
For Each myLevel In ActiveDesignFi l e.Levels
For I = LBound(GoodLevels) To UBound(GoodLevels)
If StrComplGoodLevelslIl , myLevel .N ame, vbTextCompare l

Leve lFound(I)
End If
Next I

0 Then


For 1= LBoun d(GoodLeve ls) To UBound(GoodLevels )
If LevelFound(I) = False Then
Debug.Print "MISSING LEVEL: " & GoodLevels(I)
End If
Next I
End Sub

Above, the code is ready to report missing levels to the Immediate

When is the last time a CAD Manager turned in a report consisting of a
screen capture of the Immediate Window in VBA? Let's add some code
to copy and paste versions of the two above procedures to write to an
ASCII text file.
Sub FindUnsupportedLevelsB ()
Dim GoodLevels(O To 4) As String
Dim GoodLevelsJ As Str i ng
Dim my Level As Level
Dim FFile As Long


I Chapter 21 : VBA for CAD Managers I

"GAS "
GoodLevelsJ = UC ase(" - " & Join(GoodLevels, "- ") & "- " )
FFile = FreeFile
Open "C; \M icroSta tion VBA\LevelsUnSupported.txt" For Output As # FFi le

For Each myLevel In ActiveDesignFile.Levels

If InStr(l , GoodLevelsJ. "- " & UCase( myLevel . Name) & "_H) = 0 Then
Print #FFile . ActiveDesignFile . Path & ActiveDesignFile .Na me & _
vbTab & "Unsupported Level Found: " & myLevel . Name
En d If
Close #FFile
End Sub
Sub FindMissingLevelsB ()
Dim GoodLevels(O To 4) As String
Dim LevelFound(O To 4) As Boolean
Dim myLeve l As Level
Dim I As Long
Dim FFile As Long
"GAS "
GoodLeve l s(4)
GoodLeve l sJ = UCase("-" & Join(GoodLevels. "-") & "- ")
FFile = FreeFile
Open "C;\MicroStation VBA\LevelsMissing.txt " For Output As #FFile

For Each myLevel In ActiveDesignFile .Le vels

For I = LBound(GoodLevels) To UBound(GoodLevels)
If StrComp(GoodLevel s( I ). myLevel. Name. vbTextCompare)

End If

0 Then


For I = LBound(GoodLevels) To UBound(GoodLeve l s)
If LevelFo und (I) = False Then
Print #FFile. ActiveDesignFile.Path & ActiveOesignFile.Name & _
vbTab & "MISSING LEVEL : " & GoodLevels ( I )

I Using VBA for Maintaining Standards I


End If
Next I
Close #FFile
End Sub

You are writing to an ASCII file. Place a tab between the filepath/name
to make the file tab-delimited. Why tab-delimited? Because it is easy to
import the file into Microsoft Excel and other programs capable of
reading tab-delimited ASCII files.
What's next? Consider the pain involved in opening hundreds of DGN
files and running this macro one-by- one. VBA is supposed to solve
these types of problems and make life easier and more pain -free.
Make a small change to your procedure to append the ASCII file when it
is opened. When you open a file for output, the existing file (if it exists)
is overwritten. When you open a file for append, the existing file (if it
exists) is appended to and created if the file did not previously exist.
Sub FindUn su pportedLevel sC ( Fi l eToQuery As Design File )
Dim GoodLe vel s (O To 4) As String
Dim GoodLevelsJ As String
Dim my Level As Level
Dim FFile As Long
GoodLevels(O J
Goo dLe vel s(3)
"GAS "
GoodLeve l sJ = UCase( "- " & Join(GoodLevels . "- " J & "_ H)
FFile = FreeFile
Open "C: \MicroStation VBA\LevelsUnSupported . txt " For Append _
As #FFile
For Each myLevel In FileToOuery . Levels
If InStr( 1. GoodLevel sJ. " - " & UCase(myLevel . Name) & "- " )

0 Then

Print #FFile. FileToOuery.Path & Fi leToOuery . Name & _

vbTab & " Unsupported Level Fou nd: " & myLevel . Name

End If
Close #FFile
End Sub
Sub Fi ndMi ss ingL e vel sC(F i le ToOu e ry As Des ignFi le)


I Chapter 21: VBA for CAD Managers I

Dim GoodLevels(O To 4) As String
Dim LevelFound(O To 4) As Bo ol ean
Dim my Level As Level
Dim I As Long
Dim FFile As Long
GoodLevel s(3)
GoodLevelsJ = UCase ( "-" & Join(GoodLevels. "-" ) & "-")
FFile = FreeFile
Open "C:\M i croS tati on VBA\LevelsMissing.txt" For Append As #FFile

For Each myLe vel In FileToQuery.Levels

For I = LBound(GoodLevels) To UBound(GoodLevels)
If StrComp(GoodLevels(I). myLevel.Name. vbTextCompare)

Level Found( I)
End If
Next I

0 Then


For I = LBound(GoodLevels) To UBound(GoodLevels)
If LevelFound(I) = False Then
Print #FFile. FileToQuery.Pat h & FileToQuery.Name &
vb Ta b & "MI SSING LEVEL: " & GoodLevels(I)
End If
Next I
Close /fFFile
End Sub

In addition to opening a file for Append, we are supplying a parameter

to these procedures now. This allows us to specify a DesignFile object to
query for levels.
How do we open hundreds of files in a single directory? Before
continuing, add a reference to our VBA Project. In the VBA menu,
choose Tools > References and select "Microsoft Scripting Runtime"
from the alphabetized list. Once a reference has been made to the
Microsoft Scripting Runtime, we can use the File System Object to
traverse files in a folder.
Sub DoFileslnFolder ()
Dim MyFSO As New FileSystemObject

I Cross-Company Standards I


Di m myFolder As Fo l der
Di m my Fi 1e As Fi 1e
Set myF ol der

MyFSO . GetFolder( "C: \Documents and Settings \ " & _

"A'l Users\Application Da:a\Documents\Bentley\WorkSpace\" &

For Each myFile In myFolder . Files
Select Case UCase(Right(myF i le.Name . 3))
Case "DGN "
Di m my DGNF ile As De sig nFile
Set myDGNFile = OpenDesignFileForProgram(myFile.Path)

Find Un s up portedLeve l sC my DGNFile

FindM i ss i ngLeve l sC myDGNFi l e
En d Sele ct
End Sub

The procedure DoFi 1esIn Fol der takes a hard -coded file path and opens
each DGN file in the path "ForProgram': This means it is not opened in
the MicroStation editor window but we can manipulate the files using
The example shown opens files added to our hard drives when
MicroStation is installed. In less than one second, seven files are opened,
levels are identified, and text files are written.
Use the examples shown here to spark your creativity. There are other
elements, in addition to levels, you could use to maintain standards.
Indeed, an entire book could cover examples of verifying a multitude of
criteria. Since this is not the focus of this book, I will leave additional
functionality to your imagination.
For more related information, see Chapter 30, "Batch Processing:'

Two companies need to work with each other's files but one company
has a level named "STREET" while the other has one named "Level 20".
How can VBA help companies work with different standards?
The procedure Leve 1SpecA translates one standard to another for level
names. When the procedure finds "Level 20", it changes the name to
"STREET". The other level name mappings are easy to see.


I Chapter 21: VBA for CAD Managers I

Sub Level SpecA ( )
Dim myLevel As Level
Dim I As Long
Fa r I = 1 To ActiveDesigrFile . Levels . Count
Set my Level = ActiveDesign Fi le .L evels(I)
Select Cas e myLevel . Name
Case " Le ve l 20 "
my Leve l . Name = "ST REE T"
ActiveOesignFile . Levels.Rewrite
Case "Leve l 21 "
myLeve l . Name = "SIDEWALK "
ActiveOes i gnFi l e . Levels.Rewrite
Case " Level 23 "
myLevel . Name = "G UTTER "
ActiveOesign File.Levels . Rewrite
Case "Le ve l 38"
myLeve 1 . Name = "STRIPING"
Case "L evel 39 "
my Level . Na me = "SEWER "
ActiveOesignFile . Levels .Rewrite
Case "Lev el 40"
myLevel . Name = "PHONE "
Act iv eOes ign Fi le.Levels .Rewrite
Case "L evel 41"
my Level . Name = "ELE CTRIC "
Act i veOes i gnFi l e . Leve l s.Rewr i t e
Case "Leve l 42 "
my Leve l . Name = "NATGAS "
Ac t iveOesignF i le . Leve l s .Rewr ite
Case "Level 47"
myLeve l . Name = "FIB ER"
Act i veOes i gn Fi l e . Levels . Rewrite
End Select
Next I
End Sub

After this procedure is run, the design file meets the company's
standards for level names. We can work on the file with our customdeveloped VBA tools. Our designers and drafters do not need to refer to
"cheat-sheets" to remember which level name goes with which level

I Cross-Company Standards I


name. They see what they are accustomed to seeing and are much more
productive as a result.
Before returning the file to the originating company, it would be polite
for us to set the Level names back to what they had been.
Sub LevelSpecB ()
Dim my Leve 1 As Level
Dim I As Long
For I = 1 To ActiveDesignFile.Levels.Count
Set my Leve 1 = ActiveDesignFile.Levels(I)
Select Case myLeve l. Name
Case "STRE ET"
myLevel . Name = " Level 20 "
Act i veDes i gnFi l e.Le ve l s . Re wri t e
X myLevel . Name = "Leve 1 2l "
myLevel . Name = " Level 23"
ActiveDesignFi l e.Levels.Rewrite
myLevel . Name = "Leve 1 38 "
ActiveDesignFi l e . Levels . Rewrite
Case "SEWER"
myLevel . Name = "L eve 1 39 "
ActiveDesi gnFile. Lev el s .Rewrite
Case "PHONE "
myLevel . Name = "Leve 1 40 "
ActiveDes i gnFile . Levels . Rewrite
myLeve l . Name = "Level 41 "
Act i veDes i gnFile .L evels . Rewrite
Case "NA TGAS "
myLevel . Name = "Leve 1 42 "
ActiveDesignFile . Levels.Rewrite
Case "FIBER "
myLevel . Name = "Leve 1 47 "
ActiveDesignFile . Levels . Rewrite
End Select
End Sub


I Chapter 21: VBA for CAD Managers I

Does the code work? Of course! It works great! It is fast ! It is amazing! It
is a lot of hard-coded mapping that will be difficult to maintain!
What happens when another level name translation is introduced? You
have to change the code and re-distribute the VBA Projects to everyone
who uses it. Right? Maybe not.
Let's think about what we can do to create a level mapping file that tells
what level to look for and what its new name should be.
If this chapter were about working with databases, we could do the
mapping in a database. If this chapter were about working with
Microsoft Excel, we could do the mappings in Excel. But since these
topics will be covered later, we will read from a simple ASCII text file.
Now for the file format for our level mapping file. Each line in the file
represents one mapping. Each line contains two fields separated by a tab.
The first field will be the old level name, the second field will be the new
level name.

Let's look at the code that makes use of our LevelMap file.
Sub LevelSpecFromFile ()
Dim myFile As String
Dim OldLevel() As String
Dim NewLevel() As String
Dim FFile As Long
Dim txtI n As String
Dim xSplit() As String
ReDim Old Level(O)
ReDim NewLevel (0)
Dim my Level As Level
Dim I As Long
Dim J As Long
myFile = "C: \MicroStation VBA\LevelMap.txt "
FFile = FreeFile

I Cross-Company Standards I


Open my File For In put As #F Fil e

While EOF(FFile)


Line Input #FFile, txtIn

If InStr(l, txtIn, vbTab)


0 Then

Sp l it(txtIn , vbTab)

OldLeve l(U Boun d( OldLeve l)) = xS pl i t (O)

New Leve 1 ( UB ound ( New Level ))

xSp 1 i t ( 1 )

ReD i m Preserve OldLevel (UBound(OldLevel) +


ReDi m Prese r ve New Level (UBound(NewLevel ) + 1)

End If
ReDim Preserve OldLevel (UBound(OldLevel)


ReDim Pr eserve NewLevel (UBound(NewLevel) - 1)

Cl ose #F Fil e

1 To Ac ti veDesignF i le . Levels . Count

Set myLevel = ActiveDes i gn Fi l e.Levels( I )
For J = LBound(OldLeve l ) To UBound (01 d Leve 1 )
If StrComp(O l dLevel (J) , my Level.Name) = 0 Then
myLeve l . Name = NewLevel (J)
ActiveDesignFile . Leve l s.Rewrite
Ex i t For
End If
Next J

End Sub

Here is the code. It does not matter how many levels are in the text file.
There can be 5 or 5,000 - the code doesn't change.


I Chapter 21: VBA for CAD Managers I

Time is money, right? How many times have we heard that? Perhaps the
reason we have heard it so many times is because it is true. One benefit
of learning VBA, is that you can do things many times faster with VBA
than without it.
The concern about spending time in a drawing or working on a proj ect
is different from person to person and is often defined by the
relationship we have with the drawing.

A drafter may look at the time spent in a drawing as the basis for how
much money will be paid for the work in the drawing. "The more time
spent in a drawing means more money in the paycheck:'
Another drafter may look at the time spent in a drawing as an indication
of productivity. "I am twice as productive as any other drafter:'

A manager may look at the amount of time spent in a drawing as an
indication that a drafter or designer needs additional training. Or
perhaps a drafter/designer needs to teach others in the company to be
more productive.
Another manager may look at the amount of time spent in a drawing in
terms of progress on a project.

An accountant may look at the time spent in a drawing in terms of how
much money to invoice a customer.
Another accountant may look at the time spent in a drawing for
considering raises and setting salaries.
It doesn't matter what role we play in a company, the basics of tracking
time is the same. And if We are working hard (until our backs ache and
our tired muscles knot), accurate time reporting will always be on our

ITracking Time I


So what are the basics of tracking time? Who did what and when? Any
time MicroStation is open, you can find out who is logged into the
computer. Any time you want to capture data, you can get the current
date and time. So the only question you need to answer is "what?". vVhat
events do you want to capture?
Previously, we discussed using interfaces to capture user input, element
selection, etc. You could log each and every command started by the
user. It may be helpful at some point to do a usability study on how
MicroStation is used, but that is probably overkill for what we are
attempting to accomplish here.
You could capture File Open and File Closed events. This would be
useful to know but would be insufficient or misleading, especially for
billing. What if someone opens a file at 4:59 PM on Friday then leaves
for the weekend?
Better to capture something else while the file is open to know if the file
is being worked on. Let's try watching the View Update event. This event
is not triggered so often that logging information will be a performance
To capture events, you need a new class module. Name it clsTimeTrack.
Here is the code in the class module:
Implements IViewUpdateEvents
Dim WithEvents MSApp As Applicat i on
Private Sub Class_Initialize()
Dim FFile As Long
FFile = FreeF i le
Open "C:\MicroStation VBA\TimeTrack.txt" For Append As #FFile
Print #FFile , "INIT " & vbTab & Now
Close #FFile
Set MSApp = Application
End Sub
Private Sub Class_Terminate()
Dim FFile As Long
FFile = FreeFile
Open "C: \M icroStation VBA\TimeTrack.txt " For Append As #FFile
Print #FFile, "TERM " & vbTab & Now
Close #FFile


I Chapter 21: VBA for CAD Managers I

En d Sub
Private Sub IViewUpdateEvents_AfterRedraw(TheViews() As View. _
TheModels() As ModelReference. _
ByVal DrawMode As MsdDrawingMode)
If Dr aw Mode = msdDr awi ngModeNormal Then
Dim FFi le As Long
FFile = FreeFile
Open "C: \ Mic r oSta t ion VBA\T im eTrack . txt " For Append As #FF i le

Pr int #FFile . "REDR " & vb Tab & Now & vbTab &
Application.UserName & vbTab &
ActiveDes i gnFile.Name
Close #FFile
End If
End Sub
Private Sub IViewUpdateEvents_BeforeRedraw(TheViews() As View. _
TheModels() As ModelReference. _
ByVal DrawMode As MsdDrawingMode)
End Sub
Private Sub MSApp_OnDesignFileClosed(ByVal DesignFileName As Str i ng)

Dim FFile As Long

FFile = FreeFile
Dim XSplit() As String
XSplit = Split(DesignFileName. "\ " )
Open "C:\MicroStation VBA\TimeTrack.txt " For Append As #FFile
Print #FFile. "CLOS " & vbTab & Now & vbTab &
Application.UserName & vbTab &
Close #FFile
End Sub
Private Sub MSApp_OnDesignFileOpened ( ByVal DesignFileName As String)

Dim FFile As Long

Dim XSplit() As String
XSplit = Sp l it(Des i gnFileName. "\ " )
FFile = FreeFile
Open "C:\MicroStation VBA\TimeTrack.txt" For Append As #FFile
Print #FFile. "OPEN " & vbTab & Now & vb Tab &
Appli cati on . UserNam e & vb Tab &
XSplit( UBou nd ( XSp l it ))

ITracking Time I


Cl ose #FFile
End Sub

Let's look at the events we are capturing:


Triggered when th e cl ass is fi rst


Class_Te rminate

Triggered when the class is

te rminated.


A member of the IViewUpdateEvents

interface. Add this class as an
EventsHand ler. This event is triggered
when a view is redrawn .

OnDesign FileClosed

A member of the MicroStation

app lication object. When a fi le is
closed, we are given the name of the


A member of the MicroStation

application object. When a fi le is
opened, we are given the name of the

As the code suggests, we write our time logging data into an ASCII text
file and give each captured event a four-letter abbreviation, such as
"INIT", "TERM", "REDR", "CLOS", and "OPEN': This tells us what
happened at the date and time specified. A tab character separates each
field in the text file .
Since the code is written in a class mo dule, use code in a code module to
call up the class so the class can capture the events.
Sub TestTi meTrack ()
Set myME = New cls TimeTra ck
AddModelAc ti vat eEv ents Hand l er my ME
End Sub


I Chapter 21: VBA f or CAD Managers I

Here is
output of
t he Class:

Eile !;.dit FQ.rmat I'.iew t!elp


9/11/2 0 05
9/11/200 5
9/11/ 20 05
9/11/20 05
9/11/20 05
9/11/200 5

3:08 : 13
3:08 : 20
3 : 08 : 23
3 : 08 : 24
3: 08 : 24
3: 08 :3 2
3:0 8 : 38
3 : 08 :5 2


Admi ni s trator
Admini strator
Admi ni s t r ator
Admin i str ato r
Administrat or
Admin is trat or
Admi nis trator

cogo . dgn
cogo. dgn
cogo . dg n
co ns t r aints . dgn
constrain ts. dgn
cons t rai nt s . dgn
co ns train t s.dgn
con s trai nts . dg n
co nst r ai nts.dg n



Make a small change to log the full path of the DGN file. Instead of
splitting the file name given to us, use the parameter "DesignFileName"
in your print statement.


Few things strike more terror into the eyes of computer users than
telling them that software is tracking their computer usage. If you test
the code above, you will see that it works but you don't want the user to
be responsible for turning it on. To be effective, load and execute
without any user intervention.







~'y~~I?~pj~ct,"Manager ?:,,,i'~~, ,,/,-,:~,i<:",.. \.. . . J~










I Description i Location

: Auto-Load

Loading a project automatically is the easy part. In the VBA Project

Manager (Utilities> Macro> Project Manager), click in the Auto Load
column to toggle the AutoLoad status of any project. The next time
MicroStation opens, any VBA project marked as AutoLoad will be
Now that our project is AutoLoading, how do we automatically run a

Sub OnProjectLoad ()
TestTimeTra ck
End Sub

I Auto-Load and Auto-Run I


The OnP raj ect Load feature in MicroStation VBA is easy to use. Enter the
procedure named "OnProjectLoadO", then anything placed in the
procedure will be executed when MicroStation starts.
Of course, even though OnProj ectLoad is an incredible feature, use it
with restraint. VBA does not ask what the code inside OnPraj ectLoad is
doing. It just begins executing it. If we have code that begins processing
data, opening and closing files and a host of other things, it could cause
problems. The result could be that, when a user starts MicroStation, it is
executing code placed in an OnProjectLaad procedure in an AutoLoad
Project but looks as though MicroStation has locked.
After saving your project, exit out of MicroStation. Opening
MicroStation again causes the IN IT event to be logged into your logging
So, at this point the code works on your development machine. Imagine
being so excited about this new project that you immediately place it on
a shared drive so everyone can begin using it today. You go from
machine to machine, load the project, and set it to AutoLoad. You don't
tell the users why you are doing this, but ask them to shut down and
restart MicroStation. One by one, you do this but all of a sudden
something happens.
Windows allows us to share a folder and we can also share a file. But
when more than approximately 20 people attempt to open a file at the
same time, Windows begins to complain. So how do you support 100
users? Place the VBA Proj ect on their individual machines? No. Please,

The MS_VBA_OPEN_IN_MEMORY configuration variable allows us

to specify when VBA Projects should be opened in memory instead of
maintaining a handle on the project file. By default, this variable is not
defined because opening the file from disk is not normally a problem.


I Chapter 21: VBA for CAD Managers I

Let's discuss the acceptable values for this variable, define it, and set its

Opens all VBA Projects in memory. This remove s the 20-user

limit and prevents users with this setting from editing
existing VBA Projects because they wi ll only be in memory.


Opens ReadOnly VBA Proj ects in memory. ReadWri te

Project s are opened fro m fil e.


VBA Projects are never opened in memory. All projects are

opened fro m f il e.

To add the MS_ VBA_OPEN_IN_MEMORY variable, go to the

MicroStation Workspace> Configuration menu item, then scroll down
to the Visual Basic for Applications category

"""'~'y.:".v:n-"t"'''' -".~-"'_.::r:,,:~v-


.:: ."; . .


~H."', "'~ ~,~ '~',

Cqf!figl!r!l~ton ~; lIJie,~ [!l!1titled]" " " ,,,,' " " ',' ,"

~"H~~~~V<"'"W""''''' ~~,.,


p,' '."


, , "




. , '



'~~:c;--==~'i E~~t~l:::::~~:::::~~;;--s-y~~:--;-'--':'l- --~:-~~


, Names of standard profect s

DirectOries to search for VBA

- Development

~:~~~/l mages

IlI ing

~l' I [


I f!e'~, ~

I I SelecL I

C:\Documents and SeUlngslA1i UserslApphcalion Data\Documents\Bentley\\


i.ii !



r,1 1Ewansion

i,dards Checker
:em Env

- - - - - - - - --- - - - - - - - - - - - - '








,:~~i~:arch Paths





i Directory that is used when a new project is created,

and Backup Files

,slation,I GE S

t ~II
ill i

r Commands


\_1, 1

~: !I

~i ~_~~~;~:_';';J l:!-

. c-lic- k-o-n-th-e -ca-te-go-rY- Ii-st-at-le-n-, - - - - - - - - - - '

Fo-r -mo-re- o-Pt-io-ns-

You can see configuration variables already in use by MicroStation that

pertain to VBA.

I Auto-Load and Auto-Run I


Click the New button to add a new configuration variable.


. . .0'















New Confjgu~a!!~_~ ~~ri'!!J!e.~ . __ ..... ~~=.... ~'-=,.~~-"A.<,~

Variable: [}IS_VBA OPEN IN MEMOR'!: _ _ _ _._________


"~VO~ [ .,_


E:<pansion: readonl;-------------------P----------t

The name of the variable is MS_VBA_OPEN_IN_MEMORY. We

discussed the values: here enter "readonly" as the value to manage which
files will be opened in memory by setting the ReadOnly flag in
Windows. Click the OK button to return to the Configurations dialog
box. Click OK in the Configurations dialog box to save the
configuration .









Save changes to your configuration file, [C:\Documents and
Settings\AII Users\Application
Data\Documents\Bentley\Workspace\users\untitled.ucf] ?

Click Yes to save the configuration file. Files marked as "ReadOnly" will
be opened in memory.


I Chapter 21: VBA fo r CAD Manage rs I

Marking a file as ReadOnly is simple. First unload it in the VBA Project
Manager. After the file closes, browse to it in Windows Explorer, rightclick on the file, and select Properties.


ICustom Ii Summary I


~_~"!.'_ter2 :_~~~_~ ___________________ _

---_._----------_._--------Type of file:


Opens with:

Unknown application


C:\Documents and Settings\AII Users\Application C


69.0 KB (70,656 bytes)

Size on disk:

72 .0 KB (73,728 bytes)

Change .. .




[AI;!vanced ...






The Read-only CheckBox can be selected or deselected. Do not select it

at this time because you have more changes to make to this project. It is
a good idea when you finish a VBA project to make it "Read-only". Click
the OK or Cancel button to exit out of the File Properties dialog box.
Open the file again using the VBA Project Manager to continue.

Normally, we discourage writing passwords on pieces of paper. Why?
Because we don't want anyone to find the paper and discover the
password. Although the concept of protecting password is correct,
forgetting a password to a protected project can be ... can be ...
hmmmmm, well, devastating.
Password-protecting a finished project is a good idea. Whether we are
managers or marketers, we don't want our hard-earned code to be
available to just anyone.

I Protecting Projects I


In the VBA menu, select Tools>

"~-",,("'--,~-"-"'-- -,--- ~'::"''''l''f'''"'

ettings\AU Us~rs~ppl!!=d!io~~

Project Properties.

~ Add-Ins


The Properties menu item is

different from project to project.


f!.ddi\:onal Controls ..
[:1acros ...

2 Click the Properties menu item to

Chapter2 1 Prop ~rties ... ~

display the VBA Project Properties





Qigital Signature ...


[~~----------. -.- -.------.--.------~~-1

"*"" ,,0;;0",
r~~~itiO.~ ~I_~~~.':il_~ t~o_~~r~~~=~~s_:_.___ _ ______

Project Help



. __________ ... ______ ,


_._-_ . _ -------------_.._______1
~ .


. :11




Two tabs appear. Use the first, General, to give your project a name
and description. The Help and Compile areas are outside of the
scope of this book so we won't discuss them here.


I Chapter 21: VBA for CAD Managers I

~ Protection


Use the Protection tab

to set the Project's

' Lock project


Password to view project properties - '-

- - - - _._- - - - - - ,

~-----~--------~I I


~onfirm password 1-1__________---'1 1

,---..-----.--.-.--.-------.-.-.-c __ ..__.-1





I .Cancel II


Projects set as "Locked

for viewing" can be
loaded and procedures
can be executed from
Manager but they
cannot be opened in
the VBA environment
password is supplied.

Lock your current project for viewing by selecting the CheckBox

and entering a password. Use the super-secret password "dorami".
Entering a password and clicking the OK button locks the project
for viewing.

After saving the project, unload it and then reload it using the VBA
Project Manager.
When you get back into VBA after
loading your project, you see the
project in the project list with the
project tree compressed.

''''1~''''':'f~-It '':~ ~~'''''''- ~';Y

5 Click on the Plus symbol to

. . ~",

Chapter214Pilli~rd ""., .,\(' .'> ""'"



expand the project results in a

request for the password. Now
what was that password? Was it
written down somewhere? Ah,
there it is: "dorami':

After successfully entering the password, you again have access to

the code in your project.
Remember, you cannot modify projects opened as Read Only or
opened in memory.

I Distributing VBA Projects I




VBA projects are contained in a single .mvba file. This makes them easy
to distribute. Just e-mail a file to anyone in the world who has the same
version of MicroStation and they can use your program. Or can they?
From the VBA menu, select Tools> References to display the References
dialog box.
A portion of the
References dia log box:

/!vailable References:

~ Visual Basic For Applications

~ Bentley ~licroStation DGN 8.0 Object library
~ OLE Automation
lAS Helper COM Component 1.0 Type Library


Each selected reference refers to a .dll or .exe file with functionality for
use in our programming. The top three items appear in every
MicroStation VBA project. We added the Microsoft Scripting Runtime
library earlier in the chapter. Before distributing projects, look at the
references added to the project because their absence on someone else's
computer will cause problems.
For example, while working with Microsoft Excel we add a reference to
the "Microsoft Excel 11.0 Object Library". This helps us develop more
quickly and accurately as we work with Excel. The program works great
on our development machine but, when placed on a computer without
Microsoft Excel installed, strange things happen (and not just with


I Chapter 21: VBA for CAD Managers I

regard to the Excel code). Functions such as UCase, LCase, Trim, etc.,
display errors telling us their library is not loaded.

flvailable References:



D Microsoft Exchange Event Service Config 1.0 Type Li '~i

D Microsoft FrontPage 4.0 Page Object Reference Lib"
D Microsoft FrontPage 4.0 Web Object Reference Librc
D Microsoft Graph 11.0 Object Library
D ~licrosoft H323 Service Provider I. a Type Libr ary
D Microsoft Help Data Services 1.0 Type Library
D Microsoft HT~IL Object Library
~'I Priority
D Microsoft InkDivider Type Library, version 1.5
D Microsoft InkEdit Control 1.0
-+ I
D Microsof t Internet Controls
D ~Iicrosoft IP Conf erencing Service Provider 1.0 Type
DMicrosof t Jet and Replication Objects 2.6 Library
Mirrn.:;;nft I TSrnmmnn OhiArt I ihr ?rv







6 11

, Microsoft Excel 11.0 Object Library


C:\Program Files\Microsoft Office\OFFICEII \EXCEL.EXE


_" _______ ...... _ ..;0_..... _./

So, when distributing VBA Applications, make sure you know exactly
which references are selected and let users know what they need to have
installed for your program to work correctly.


There are benefits to starting MicroStation in High Security mode. Here
are two:

attempting to enter the VBA area by clicking Utilities> Macro>

Visual Basic Editor results in the following error:

af Operation not allowed because MicroStation is running in secure mode.

attempting to open an unsigned project results in this error:

'CD" The proieet could not be loaded because its digital signature could not be verified.
Why are these benefits? vVe can't run our code.
Because it is easy to write code that can damage MicroStation DGN files
and the Windows system as well. High Security Mode prevents unsigned
projects from being loaded and executed. Of course the next question is,

I Review I


"How do I sign my Projects?" That is a good question. The answer,

however, will not be provided here because explaining how to sign a
VBA Project in this book would result in every VBA Project being
signed and would defeat the purpose of using High Security Mode as
well as the purpose of signing VBA projects.
The main thing to keep in mind is that High Security means high
security - VBA projects we create will not run in MicroStation when
MicroStation is in High Security Mode.

From a CAD manager's point of view, VBA can do a lot to aid in
maintaining CAD standards. Productivity is important to maintain and,
at times, measure. Securing VBA projects means getting productivity
from the VBA projects and keeping wandering eyes from playing with
your code. Auto-load and auto-run code is powerful and easy to
implement. The most difficult thing is knowing when to use these
powerful features to your best advantage.


I Chapter 21: VBA for CAD Managers I


MicroStation File-Based
You can execute code whenever a specific event happens, such as writing
a file name to an ASCII text file each time a file is opened. This chapter
deals with MicroStation file-based events using some very powerful
programming techniques.
We looked briefly at two of these events in Chapter 14 but will cover
them in detail here.

In this chapter:

The "OnDesignFileOpened" event


The "OnDesignFileClosed" event


The "ISaveAsEvents_BeforeRemap" event


The "ISaveAsEvents_AfterRemap" event


The "ISaveAsEvents_AfterSaveAs" event



I Chapter 22: MicroStation File-Based Events I

The OpenDesi gnFi 1eOpened event is part of the MicroStation Application
Object. Each time a design file is opened, the OnD es i gnFi 1eOpened event
is triggered. Let's add some very simple code to this event.
To begin, this event is only available when a variable is declared as an
''Application'' type of object using the "WithEvents" keyword.
Create a new class module and name it clsSaveAs.
Dim WithEvents myMS As Application

Where is this declaration made? In the General Declarations area of the

class module clsSaveAs.
When the variable myMS is declared, the events of this object display in
the Procedure ComboBox in the class module.

Dim WithEvents myMS As Application

Selecting OnDesignFileOpened in the ComboBox automatically declares

the OnDe sign Fi 1eOpen ed Event.
Priv ate Sub myMS_OnDesignFileOpened(ByVal
DesignFileName As String)
Dim ffile As Long
ffile = FreeFile
Open "C: \Mic ro Station VBA\Fi l eOpen.txt" For Append As #ffile
Print #ffile, Now & vbTab & Des i gnFileName
Close #ffile
End Sub

Here is the code in the OnDesignF i leOpened event. We write the date/
time and the file name to an ASCII Text file at
C:\MicroStation VBA \FileOpen.txt.

Writing the code is simple but you cannot execute the code in an event
in the same manner as when you place code in a code module. We will
discuss how we get a class "up and running" later in the chapter.

I OnDesignFileClosed I


This event is triggered when a file is closed. Your code could write to an
ASCII file just as it did with the OnDes i gnFi 1eOpenec event but there are
far more powerful and useful things you can do.
Let's brainstorm on what to do with the OnDesign Fi l eCl osed event. A
user closes a file in MicroStation. Is it the end of the day? Is this the last
time the file will be opened this week? What changes were made while
the file was open? Who was using the file when it was closed? What time
was it closed? Had the file been opened earlier in the day?
We could write 100 different applications using the OnDes i gn Fi 1eC 1osed
event but let's pick one: capture a file when it is closed. Of course, you
can copy the file to a folder but that could take more hard drive space
than should be dedicated to this purpose. If you zip (compress into a .zip
file), you would save some disk space. If you place the file in an existing
zipped folder or file, this may be even more helpful.
So, how do you compress multiple MicroStation DGN files into a single
zip file? VBA is not supposed to be that powerful, right? Well, we could
spend money on a third-party DLL or ActiveX Control, but do we want
to spend money when we don't need to?
Windows XP introduced compressed (zipped) folders, essentially a zip
file (.zip extension) that Windows treats as a folder. You can copy files to
and paste from a compressed folder using standard Windows
functionality. Let's leverage this new Windows XP functionality to zip
files using VBA.
Before you write any code, add a reference to the "Microsoft Shell
Controls and Automation" object in VBA (Tools> References). This
gives you access to some powerful features developed by Microsoft for
developers (in this case, we are the developers). The Shell32.dll Object
can do a lot of things. We will only scratch the surface as we discuss a
couple of events in this chapter.
As you will see later in this chapter, it is easy to add files to an existing
zip file (compressed folder); but if the file does not exist, we need to
create it.
Nearly all files we use on a daily basis have two identifying features. The
first is the file extension. For example, we instinctively refer to a file with
a .dgn file extension as a MicroStation file but there is nothing keeping


I Chapter 22: MicroStation File-Based Events I

us from changing the file extension of a .txt file to .dgn. So, even though
the file extension is a good indication of file type, there are no
guarantees. The second identifying feature is the file header, consisting
of a specific number of bytes at the beginning of a file, that helps
programs verify the file type.
A zip file (or a compressed zipped folder) header consists of 24 bytes.
How do we know this? Create a new zipped folder in Windows, then
open the file in a Hex editor to see the byte values of each byte in the file.
The first four byte values in a zip file are 80, 75, 5, and 6. The next 20
bytes have byte values of 0 (zero). So, we can create an empty zip file by
writing Chr(80), Chr(75), Chr(5), Chr(6), and then 20 Chr(O) values.
This writes a zip file header to a file which Windows XP recognizes by
file extension and by file header. Other zip file readers/writers also
recognize the file as a legitimate zip file. Of course, a zip file is not very
useful if it is empty.
After the zip file is created (if it did not already exist), you can copy files
to the 'file/folder' by using the Shell library.
Place the procedure CopyFi 1eToZi pFi 1e in the class module, not the code
Sub CopyFileToZipFi l eCZipFile As String, FileToCopy As Str ing , _
CopyFileAs As Str i ng)
Dim ffile As Long
Dim myShell As New Shell
Dim zip Fold er As Shel132.Folder3
ffile = FreeFile
If Dir(ZipFil e ) = .... Then
Open ZipFile Fo r Output As #ffile
Print #ffi le, Chr(80) & Chr(75) & Chr(5) & Chr(6) &
Chr(O) & Chr(O) & Chr(O) & Chr(O) &
Chr(O) & Chr(O) & Chr(O) & Chr(O) &
Chr(O) & Chr(O) & Chr(O) & Chr(O) &
Chr(O) & Chr(O) & Chr(O) & Chr(O) &
Chr(O) & Chr(O) & Chr(O) & Chr(O)
Close #ffile
End If
Set zipFolder = myShell .N amespace(Z ipF i le )
If StrComp( File ToCopy , CopyFileAs) <> 0 Then
Fil eCopy FileToCopy, CopyFileAs
zipFolder.CopyHere CopyF il eAs

I OnDesignFileClosed I


Kil l CopyFileAs
zipFolder.CopyHere CopyF i leAs
End If
End Sub

The first thing we do is check to see if the .zip file exists. If not, we create
the file and print to the file using standard File I/O (Input/Output)
commands we have used multiple times in this book.
Our function Copy Fi 1eT0Zip Fi 1e allows us to specify the file to copy and
the file name to use inside the zip file. Later you will see why this ability
to specify the file name used by the .zip file is important. The variable
myShell is declared as a "New Shell" object. This exposes the referenced
object's methods, properties, and events. We use the zip file name to get
a folder using the Namespace method of the shell object. If the
FileToCopy variable is not the same as the CopyFi leAs variable, we copy
FileToCopy to CopyFileAs, and then copy the CopyFileAs file to the zip
file . The last thing to do is kill (delete) the file. If, however, the
FileToCopy variable is the same as the CopyFi leAs variable, simply copy
the design file to the zip file without doing any other copying or killing
(that function sounds so violent, doesn't it?).

NOTE: The web page at!csharp/

Decompress WinShellAPICS.asp describes using the shell object to
work with Windows XP's Compressed (zipped) folders.
The CopyF i 1eToZi pFi 1e procedure is now ready to be used by the
OnDesi gnFi 1eCl osed event.
Private Sub myMS_OnDesignFileClosed(ByVal _
DesignFileName As String)
CopyFileToZipFile "C: \MicroStation VBA\". _
DesignF il eName . DesignFileName & "." & CLng(Timer * 1000)
End Sub

We use the CopyFileToZipFile procedure by specifying the zip file the

design file is to be copied into, the file name (full path) to use as the
source file, and a destination file. The source and destination files can be
the same. So, why would we want a different filename for the zip file?
We want to supply different file names so that a design file (tasks.dgn
for example) can be in the same zip file multiple times. We do this by
appending a number to the end of the file. What number? In this


I Chapter 22: MicroStation File-Based Events I

example, we use the Timer function, which tells the number of decimal
seconds that have transpired since midnight, and multiply it by 1,000, to
get the number of milliseconds since midnight. This gives us confidence
that we will not attempt to copy the same file with the same numeric
appendage into the same zip file.

The ISaveAsEvents interface includes ISaveAsEvents_BeforeRemap,
ISaveAsEvents_AfterRemap, and ISaveAsEvents.
The ISaveAsEvents_BeforeRemap event is a member of the
ISaveAsEvents interface. To use members of an interface, we declare the
interface in our class module. Here is the declaration we need to put into
the class module with which we have been working:
Implements ISaveAsEvents

As in previous discussions on interfaces, we need to declare all events

(methods) of the interface before putting code into any of them.
Private Sub ISaveAsEvents_BeforeRemap( _
ByVa l TheOesignFile As OesignFile. _
ByVal SavedFormat As MsdOesignFileFormat. _
ByVal OestinationFilename As String)
End Sub
Private Sub ISaveAsEvents_AfterRemap( _
ByVa l TheOesignFile As Oes i gnFile. _
ByVal SavedFormat As MsdOesignFileFormat. _
ByVal OestinationFilename As String)
End Sub
Private Sub ISaveAsEvents_AfterSaveA s()
End Sub

IISaveAsEvents Interface I


The three events belonging to the ISaveAsEvents Interface are sh own

above in the order in which they are executed.
Since the Afte r SaveAs event is the last event executed, this is where we
place the code that actually does something. But before we do so, let's
look at the event.
In the BeforeRemap an d AfterRemap events, we are told the DesignFile,
SavedFormat, and Destination Filename parameter values. Since the
AfterSaveAs event does not tell us any of this, we need to capture the
data when it is given to us. We will declare three variables in the General
Declarations area of the class module to hold the values given in the
AfterRe map event to use the information in the AfterSaveAs event.
Di m Fi l eSave d As Str i ng
Dim Fi le Form at As Long
Di m Fil ePre vi ous As Strin g

Now that the variables are declared, they can be used in the Aft er Remap
event as follows:
Private Sub ISaveAsEvents_After Rema p(
ByVa l TheDesignF il e As DesignFile . _
ByVal Sav ed Form at As MsdDes i gn Fi l e Format . _
ByVal Dest in ati onF il ena me As Str i ng)
Fi l eSaved = Desti nat i onF il ename
FileFormat = SavedFormat
FilePrevious = TheDesignFile .F ul l Name
End Sub

OK. We captured the DesignFile going into the SaveAs command as well
as the file format the file is going to be saved as, and the
DestinationFilename. What can we do with this?
In a previous example, we added design files to a zip file when the
design files were closed. This was a powerful example of the ease with
which great functionality can be implemented with only a few lines of
code in VBA (and of course, a little knowledge mixed in for good
measure). Can we top that functionality in the AfterSaveAs event?


I Chapter 22: MicroStation File-Based Events I

It's time to brainstorm again as the possibilities are endless. Each
function we discuss could be performed on the "TheDesignFile"
parameter or the "DestinationFilename" parameter. We could FTP the
file to a server half-way around the world. We could e-mail the file to
different people based on which project the file is in. We could open the
file and extract information and place it in a database. Each of these
would be useful and could be easily implemented. Let's do something
When a user performs a SaveAs, the file that had been opened is given a
new file name and, optionally, saved as a different file type. In our
scenario when a file experiences a "SaveAs" and is saved as an AutoCAD
.dwg file, we take the original design file (it could already be an
AutoCAD .dwg file) and write it to a CD.
Again, we are using functionality introduced in Windows XP which
allows us to copy and paste files to a CD- or DVD-writing drive. When
we do this, the files are placed into a temporary storage location until we
decide to actually write them to a CD. When we 'Send' a file to our CD
writer, we get a message saying we have files waiting to be written to CD:

You have files wai ting to be written to the CD.

So, how do we get files into this temporary storage location? Where is it?
Let's use the shell object we just finished working with in the last
example in this example.
We need to create a new procedure in the class module we used in the
previous example. This procedure is named CopyFi 1eto CD and it takes
one parameter, the file that is to be copied to the CD. Here's the code, a
discussion follows.
Sub CopyFileToCD (File ToCopy As String)
Dim myShell As New She l l
Dim cdFolder As Shel132.Folder3
Set cdFolder = myS hell.Nam pspace(59)
If Not cdFolder Is Nothing Then
cdFolder . CopyHere Fil eToCopy , 0
End If
End Sub


IISaveAsEvents Interface I

The first thing we do is fin d the temporary storage location for the user
who is logged in. Now, how do we do that? We supply the number 59 to
the Namespace method of the shell object. If we do this and the returned
folder is not "Nothing" (in other words if the folder is found), we copy
the supplied file into the cdFolder. That's all there is to it.
One of the great things about this procedure is that we don't need to
purchase CD writing add-ins. If the user has Windows XP, we can use
this procedure.
Remember that the CopyFi 1eToCD procedure
does not actually burn the CD - it only
copies the file to the staging area to burn the
CD. When you are ready to burn the CD,
place a CD- R or CD- RW into the CD burner,
select the CD drive in Windows Explorer, and
go to the Explorer menu File > Write these




~ OVO/CO-RW.Orive (0:)

files to CD .

These menu picks begin the process of writing the files in the temporary
CD folder to the CD.
So we now have code that prepares files to be written to a CD. How do
we use it? We use it in the ISaveAsEvents_AfterSaveAs event.
Private Sub ISaveAsEvents_A f te r SaveAs()
Se l ect Case Fi le For mat
Case msdDesig nFileFormatCurrent
Case msdDesignFileFormatDWG
CopyF i leToCD File Previous
Case msdDesignFileFormatDXF
Case msdDesignFileFormatUnknown
Case msdDesignFileFormatV7
Case msdDesignFileFormatV8
End Select
End Sub


I Chapter 22: MicroStation File-Based Events I

The AfterSa veAs event is the last event to execute when a SaveAs is
executed by the user. But the event itself does not tell us the source file
name, destination file name, or format. We collect that information in
the AfterRemap event to use it in the AfterSaveAs event.
The example above shows how to use the CopyFi 1eToCD procedure we
created, but it only executes it when the SaveAs command is used to save
an AutoCAD .dwg file. We could place the same line of code under
other "Case" statements to accommodate other file formats or we could
get rid of the Select Case structure altogether and use CopyFileToCD
every time a SaveAs occurs.
We addressed the functionality in our new class module clsSaveAs in a
couple of sections. Now let's look at the entire class module from
beginning to end. Remember, the variable declarations appear in the
General Declarations area of the class module. Pay particular attention
to the use of the variables FileSaved, FileFormat, and Fi lePreviou s.
Here is the class module code from beginning to end:
Dim WithEve nts myMS As Appl i cation
Implements ISaveAsEvents
Dim FileSaved As String
Dim Fil eFor mat As Long
Dim FilePrevious As Str i ng
Private Sub Class_ Init i alize()
Set myMS = Application
End Sub
Private Sub ISaveAs Events_AfterRe map( _
By Val TheDes i gnFil e As Des i gnFile . _
ByVal SavedFormat As MsdDesignF i leFormat. _
ByVal DestinationF i lename As String)
FileSaved = DestinationFilename
FileFormat = SavedFormat
FilePrevious = TheDesignFile . FullName
End Sub

! ISaveAsEvents Interface !


Private Sub ISaveAsEvents_AfterSaveAs()

Se l ect Case Fi l eFormat
Case msdDesignFileFormatCurrent
Case msdDesignFileFormatDWG
CopyFileToCD FilePrevious
Case msdDesignFileFormatDXF
Case msdDes ig nFileFormatUnknown
Case ms dDesignFileFormatV7
Case msdDesignFileFormatV8
End Select
End Sub
Private Sub ISaveAsEvents_Be fore Remap( _
ByVa l TheDesignFi l e As DesignFile, _
By Val Save dFor mat As MsdD esign Fil eFo r mat , _
ByVal Dest i natio nFilename As String)
End Sub
Pri vate Sub myMS_On Des i gnFi l eC lo se d( ByVal _
Desig nFi leNa me As
Str i ng )
CopyFileT oZip File "C:\MicroS ta tion VBA\ FileCl osed .zip ", _
Desig nF i l eName, Des i gnF i l eName &
& CLng ( Ti mer * 10 00 )
End Sub
Pri vate Sub myMS_OnD esignFileO pen ed( ByVa l _
Desi gnFi leN ame As
Str ing)
Dim ffile As Long
ffile = FreeF ile
Open "C:\MicroStat ion VBA\FileOpen.txt" For Append As #ffile
Print #ffile, Now & vb Tab & DesignFileName
Close #ffile
End Sub
Sub CopyFileToCD(File ToCopy As String)
Dim myShell As New Shell
Dim cdFolder As Shel132.Folder3
Set cdFolder = myShell .Namespace(59 )
If Not cdFolder Is Noth i ng Then
cdFolder.CopyHere FileToCopy, 0
End If
End Sub


I Chapter 22: MicroStation File-Based Events I

Sub CopyF i leToZ i pFi le(ZipF il e As String , Fil eToCopy As String,
Copy FileAs As String)
Di m f f i le As Long
Di m myShel l As New She l l
Dim zipFolder As Shel132 . Folder3
ffile = Free File
If Di r (Z i pFi le ) = "" Then
Open Zi pFi l e For Output As #ffi l e
Print #ffile, Chr(80) & Chr(7S) & ChriS) & Chr(6) &
Chr(O) & Chr(O) & Chr(O) & Chr(O) &
Chr(O) & Chr(O) & Chr(O) & Chr (O) &
Chr(O) & Chr (O) & Chr ( O) & Chr (O) &
Chr(O) & Chr(O) & Chr(O) & Chr (O) &
Chr ( O) & Ch r(O ) & Chr( O) & Chr(O)
Close #ffile
End I f
Set zip Fol der = myS he ll . Namespace(Z ip Fi l e)
If StrComp( Fi l eToCopy , Copy Fi l eAs) <> 0 Then
Fil eCo py Fi leToCopy , Co pyFi l eAs
zipF older . CopyHere CopyF il eAs
Ki 11 Co py Fi l eAs
El se
zi pFolder.C opyHe r e Copy Fi leA s
End If
End Sub

Thus far we have written a lot of code but we have been unable to run it
because the code is contained in a class module, so let's discuss how to
use the class module code.
You must call up the code in a class module by code in a code module or
UserForm. We will use a code module for this example.
Sub Te s tSaveAs ()
Dim mySaveAs As New clsSaveAs
AddSav eAsEven t sHandler mySaveAs
End Sub

I Review I


This is pretty simple. We declare a variable as a clsSaveAs (the name of

the class module we just finished working in) and add it to the
SaveAsEventsHandler. Then the clsSaveAs object is brought into
memory and begins responding to the SaveAs events as well as the
OnDesignFileOpened and OnDesignFileClosed events.

In this chapter, you zipped files (compressed them) and prepared them
for burning to a CD using Windows XP. You can accomplish the same
tasks in other operating systems but would need to program differently.
You now have the knowledge to execute code when the user performs a
SaveAs or when a file is opened or closed no matter what the operating
system or the desired functionality.
Before beginning any project, take time to think through the
functionality you want in the project. Think about the best possible
program. Don't limit yourself because you are unsure how to accomplish
a task or two, or three, or more. Even the oldest dogs can learn new
tricks - in this case, VBA tricks.


I Chapter 22: MicroStation File-Based Events I


Responding to
Attachment Events
These next four chapters continue with responding to events in
MicroStation beginning with the IAttachmentEvents interface. Five
events are exposed.
In this chapter:

The IAttachmentEvents Interface


After Attach


After Detach

[8 AttachmentModified
[8 BeforeAttach
[8 BeforeDetach



I Chapter 23: Responding to MicroStation Attachment Events I

Let's create a new class module named clsAttcchmentEvents. You must
declare each event implem ented by the IAttachmentEvents interface.
Add simple Debug.Print statements inside each event to help us
understand the order in which events are triggered.
Priva t e Sub IAttachmentEvents_AfterAttach(ByVal _
The Atta chme nt As At tach ment)
Deb ug . Pr in t "Af t er Attach "
End Sub
Private Sub IAttachmentEvents_AfterDetach(ByVal _
TheAttachmen t As Attachment)
Debug.P r i nt "Af terDetach"
End Su b
Priv ate Sub IAtt ach me nt Event s_A ttachm entModified ( ByVa l _
TheA t ta c hment As At t achme nt )
De bu g . Prin t "Att ach ment Mod i f i ed"
End Sub
Private Sub IAt t achment Events_BeforeA t tach ( Fil eName As Str i ng . _
All owAt t achment As Boolean)
Debug . Print "B eforeAttach"
End Sub
Private Sub IAttachmentEvents_BeforeDetach(ByVal _
TheAttac hment As Attac hment)
Debug . Print "BeforeDetach "
En d Sub

Two of the events refer to attaching a reference file and two refer to
detaching a reference file .

Private Sub IAttachmentEvents_AfterA t tach(ByVa l _
TheAttachment As Attachment)
End Sub

I AfterAttach I


After a DesignFile is attached to the current design file, the After Attach
Event is triggered. When this event occurs, we can be certain the file is
actually attached and is available for processing. The parameter
"TheAttachment" is provided in the event so we can begin working with
the attachment immediately.
The "TheAttachment" Parameter is declared as an Attachment type of
Object. Perhaps understanding this object a little better will help us to
know what we can do with this event. The list is several pages long so we
will just take a look at a few of the properties and methods.

Sub AddElement(Element As Element)


Sub AddElements(ElementsO As _Element)


Property AttachName As String {read-only}


Property Description As String


Property DesignFile As DesignFile {read-only}


Property DisplayFlag As Boolean


Property Is3D As Boolean {read-only}


Property IsActive As Boolean {read-only}


Property IsReadOnly As Boolean {read-only}


Property IsTrueScale As Boolean {read-only}


Property Level As Level


Property Levels As Levels {read-only}


Property LogicalDescription As String


Property LogicalName As String


Property MasterOrigin As Point3d {read-only}


Sub Move(Offset As Point3d, ApplyToClipElement As Boolean)


Property Name As String


Function Reattach(FileName As String, ModelName As_

String) As Attachment


Sub Redraw([DrawMode As MsdDrawingMode =_



I Chapter 23: Respond ing to MicroStation Attachment Events I

[8 Sub Rem oveElement(Element As Element)
[8 Sub ReplaceElement(OldElement As Element, _

NewElement As Element)
[8 Property RevisionNumber As String
[8 Sub RewriteO
[8 Sub Rotate(Pivot As Point3d, AboutX As Double, AboutY _

As Double, AboutZ As Double, ViewSpecifier As Variant)

[8 Property Rotation As Matrix3d {read-only}
[8 Property ScaleFactor As Double
[8 Function Scan([ScanCriteria As ElementScanCriteria]) _

As ElementEnumerator
[8 Sub SelectElement(Element As Element, [DisplayAsSelected _

As Boolean

= True])

[8 Property Transparency As Double

As you look at these properties and events, think about how to use them
AFTER the attachment is attached.
Let's look at the DesignFile property. When an attachment takes place,
we can get the DesignFile of the attachment and get the DesignFile's
path. Let's add a Date/Time stamp with the attachment's file name to the
active model.
Private Sub IAttachmentEvents_AfterAttach(ByVal
TheAttachment As Attachment)
Dim TxtPt As Point