You are on page 1of 933

l'

LEARNING

MICROSTATION"VBA
bV Jerrv Winters

Learning
MicroStation VBA
Jerry Winters

B!tley Institute Press


Exton, PA

2006

LEARN I NG MICROSTATION

VBA

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
material.
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
www.bentley.com

B!tley Institute Press


Printed in the U.S.A.

Foreword
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

ii

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.

Contents
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

iii

iv

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

vi

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

vii

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

viii

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

ix

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

xi

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

xii

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

xiii

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

xiv

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

xv

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

xvi

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

Introduction
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
text:
Filename or URL

Menu and menu items

Fu nct io n

Object

Fu nctionlndex

Variable

Keyboard key

xvii

xviii

I Introduction I
ACCOMPANYING

CD-ROM

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.

BENTLEY INSTITUTE
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 http://bentleyinstitute.bentley.com.

ABOUT THE AUTHOR


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

xix

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
chickens.

ACKNOWLEDGMENTS
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.

xx

I Introduction I

Introducing VBA
"LET'S START AT THE VERY BEGINNING. IT'S A VERY
GOOD PLACE TO START."
[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.

WHAT IS VBA?
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


WHY LEARN

VBA?

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.

WHEN SHOULD You USE VBA?


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!

How Do WE USE VBA?


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 ...


.Cel~tor
image
Render
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
Files:

UDkQ.c!~.J!lyqiL_______
Inlroduction.mvba

Directories:
D:\MVBA Files\
~D:\
~ MVBAFiles

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

Default
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

Module1.PlocedureA
Module1.ProcedureC

Delete

.1

.wI

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


,

f.:i

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.
3

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.

WHAT DOES VBA LOOK LIKE?


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
development.

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.

REVIEW
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
keyboard.
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


Manager
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

projects.
[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.
9

I Chapter 2: The VBA Project Manager I

10

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

VBA PROJECT MANAGER FUNCTIONALITY


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

Begin a New VBA


Project

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


session.

(lose VBA Project

This button is enabled only when an existing project is


selected.

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


Editor

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

IVBA Project Manager Functionality I

11

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
Macro

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


Macro

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


Pause / Resume Recording.

Auto-Load

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.

12

I Chapter 2: The VBA Project Manager I


MACROS DIALOG

Box

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.

Mac[oname:

Run

lii'U.
ProcedureA
ProcedureC

Macros in:

I
I

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

13

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.

REVIEW
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
theVBAPM.
III To enter the VBA Editor from within the VBAPM.

14

I Chapter 2: The VBA Project Manager I

The VBA IDE


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
Milk?
The VBA IDE is where we do our VBA programming work. As with
most Windows programs, the VBA IDE is composed of three elements:
[8

Menus

[8

Toolbars

[8

Windows

1S

16

I Chapter 3: The VBA IDE I

i E.ila

gdit

S!jew

insert

FQ.r(llat

Q,ebug

&un

Iools

8ddlns

~indow

lielp

MENUS
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

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

Ctrl+M
Ctrl+E

Remove

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 .. ,

Ctrl+p

[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

17

[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


operations.

,f,

[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 ~()

11.8

[B Clear deletes selected text or

objects.

Ctrl+Z

Can't Redo

':ut

,: ttl+X

~opy

Ctri+C

e.'~$ bE!

Ctrl+v

Clear

Del

Select all

Ctrl+A

~l ::~:'~;ext

,:trl:

,l R~DI~~8' "

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

~~lj ~~~

__~hift:Tab _

~"l list Propertiesl~letb.ods


Lt, List Constants

i1kj Q:ick Info

Ctrl+ J

Ctrl+Sh,ft+ J
Ctrl+1

iit',l Parameter Info

Ctrl+Shift+1

A~'l Complete 'liard

Ctrl+Space

[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
left.
[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

18

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
:rifI:

~ode

I~

OWect

F7
Shift+F7

~efinition

Shift+F2

Last P05itio!l

Cttl+Shift+F2

~ Qbject Browser

in'

[mmediate Window

Local~ Window

&}'

Watch Window

Call

Stac~" .

~. E,roject Explorer
~, Properties Window

"~ TOolbO~
,

F2
Ctrl+G

Ctri+ L
Ctri+R

F4

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
Ioolbars

3. MicroStation

Alt+FIl

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
forefront.

I Menus I

19

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.

Module

~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
8!ign
Make Same Size
'X 'f

Size to

Fi~

Size to Gri!J.
tlorizontal Spacing

Ilertical Spacing
~enter

Use the Format menu to perform standard


formatting when editing a User Form.

in Form

Atr ange Buttons

~.

.roup

!fl

klngroup

Qrder

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:'

c:;]

Step into

l:ll

St~p

c!-=

StepOl,!t

-"

Qver

F8

5hift+F8
Ctrl+5hift+FB
Ctrl+F3

add Watch .. .
~dit

6d

W.,tch .. .

Quick Watch .. .

ctrlHV
Shift+F9

-t:.~ loggle Br~ akpoint

;$,

gear All Breakpoints

Ctrl+Shift+F9

1Set N>3 <t statement

Cl:rI+f9

Sholl'" Ne~t 5tat~ment

20

I Chapter 3: The VBA IDE I


Run Menu
~

Run Macro

UJ

8rea~

F5

ctrl+8reak

la

B.eset

III

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 ...

t
,

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
Default.
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

21

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-'

OK

Cancel

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
~.Jiti9=
'H_.ll_ _ _--,
SQ.i,t

Tile tiorizontaliy
Tile 'lerticaliy

These are the standard menu items


available in nearly every Microsoft
Windows program.

hascade
~

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

F1

8,bout Microsoft Visual Basic, , ,

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

I Chapter 3: The VBA IDE I

22

TOOLBARS
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 .
~:'llI

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

23

UserForm toolbar
UserForm

"1 J:) ~

... .

0(: ,i."

\ ~ ;"';,T

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

The UserForm toolbar exposes


functionality found in the Format
menu.

WINDOWS
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.
Forms
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!

24

I Chapter 3: The VBA IDE I


Object Browser

Classes
(; <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
ActiveWorkspace
AddAttachmentEventsHand ler
AddChangeTrackEventsHandler
AddLevel ChangeEventsHandler
AddModalDialogEventsHandler
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

25

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

UserForm1.Cornrnand8utton1_Click
DesignFileJDesignFile
UserFonn1.Cornrnand8utton1_Click
ModelReferenceilvlodelReference UserForrn1 .Cornrnand811tton1_Click
ActiveWorkspace

Settings,Settings

UserForm1.Cornrnand811tton1 _Click

WorkspaceNVorkspace

UserForrn1 .Cornrnand8uttonI _Click

AttachedCeliLibr6 <No cell libra Cell Library


8spline,8spline
CadlnplllGlIelieiCadlnplrtGlIeue
"graph paper Siring
CornrnandStatelCornmandSiate

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

Long
UserForm1.Cormnand8utton1_Click
CursorlnforrnationiCursorlnformati UserForrn1 .Cornrnand811tton1 _C lick
ObjectN8Project

UserForrn1.Cornrnand811tton1 _Click

"C:'Prograrn I String
800lean
HasActiveDesign True

UserForrn1 .Cornrnand8utton1 _Click

H asAct iveMode l~

800lean

UserForrn1 .Corn rnand8utton1_Click

Long

FuliNarne

Height

True
1201

UserFonn1.Cornmand8utton1_Click

IsAcadernicVersi, False

800lean

UserForrn1.Cornrnand8utton1_Click
UserForrn1 .Cornmand8utton1_Click

IsCeliLibraryil.ttac False

800lean

UserFonn1.Command8utton1

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.

26

I Chapter 3: The VBA IDE I


Locals Window

DesignFilelOesignFile
ModelReferencelModelRefen
SettingslSettings
WorkspaceNVorkspace
No cell library>

CeliLibrary
Bsplinefaspline
CadlnputQueuelCadlnputQue

"chapjer03.dgn (2D -

va Dor string
CommandstaielCommandstal

CurrentOraphicOroup

Long
CursorlnformaiionlCursorlnfo

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

Learning

~1icroStation

VBA

27

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

Call Stack Window


Call Stack

~
Show

'.1

Close

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.

1
~ Aabl~1ffiI
Controls

P(o';::!L1-l
..!..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

28

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 (

C~ncel

."

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 __,_,_,._,,_,_
..

.....

0;

'

...

. ... .
.

...

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

, '-

I Windows I

29

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.

View

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
them?

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

30

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.

REVIEW
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
quickly.

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:
[B

Terminology

[B

Help Files

[B

The Net

[B

The Object Browser


31

32

I Chapter 4: Finding Help I


TERMINOLOGY
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
,
ComboBox
icon in the Toolbox. If
we
ask
about
a
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.
--ToolboH

---~

----

---

- -

--

---

---

"

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

33

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
helpful.
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.

34

I Chapter 4: Finding Help I


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

Hide
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

ttl.
tt ll .

Set'3.t\J::;O

1il

Specifit;:s

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:

I
I
.'

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 .

fal

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
Reference.

I Help Files I

35

Contents tab
'ml

Hide

[iJ

if

{l,

<?

s>

.,

[1J

Jt.

Locate

Pr>;:.j!Cri.;~

N'::-i!t

Back

Forward

Stop

Refresh

Home

Font

().,

~o';ients I Index

I Search I Favorjtes I

id

Il Visual Basic HowTo Topics


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

Il . , Mrcrosoft Forms Desrgn Reference


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

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

Print

ComboBox Control
Se e Als o

Exam pl e

Properties

~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
support,

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
file.

36

I Chapter 4: Finding Help I


Index tab
Contents

!;;ontents [ Index l l iarch

I~

Type in the key~ord to find:


Iistring

[Itext

CompareMode property
Filter function
InStrRev function
Replace function
Split function
wildcards
string conversion
String data type
String function
String keyword
String$ function
strings
aligning
character
comparing
concatenating
converting
data types
fixed-length
formats
iustifying
leftmost characters
length
manipulating
matching
middle characters
removing spaces
repeating
replacing
returning from functions

clearing
color
comparing
converting
culling and pasting
entering
from numbers
importing
looping
searching/replacing
5 tring data type
text files
inserting
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
adding
difference
Time keyword
time stamp
Time statement
Time$ function
Timer function

~!

timer s

reversing

times
adding
converting
creating
Date data type
determining

rightmost characters
searching
spaces
substrings
variable-length
Display

Display

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

37

Search tab

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

lli.~~r!~e.~J

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

Toolbox
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

I
I
,
I

I
I
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 .. .

Display

1
2
3
4
5

6
7
8
9
10

11
12
13
14
15
16
17
18
19

20
21
22

23
24
25
26
27
28

29

B Match similar words

o Search titles only

Favorites tab
~

Hide

Locate

it
r~it.!;\';[';l.l~

~l
~h;'.t

<?

Back

Forward

Stop

ITl

Refresh

Home

J[,

Font

Print

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

&W data in a ListBox or


~~ifil;
S

Tallies:

-.">.I
,

to

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

I"

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

38

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

39

MicroStation VBA Help File


ml

riJ
to.

'?

I::;>

Bxk

F'Jrh!,:'hi

m1r.

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

Locate

!;ontents

Plint

Qptions

,;:

IIndex I ~ealch I FavOljtes I

II-licroStation V8
Visual Basic for
Applications

8 ~~
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

rl

Visual Basic and


VBA are thoroughly
modern objectoriented
programming
environments used
by both professional
~ ~
application
developers and
"-'
casual
programmers,
Visual Basic is the
primary
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

CJ

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.
-r"'"--';~-.r-,..,'--'l"

..'r.-.,.....,r:'.

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

N.",<_~

></<~--~~'Z"""""Y"l".,.,.,~,,,,,,,,..,.,,'

Ili},M!craSla!ianV.8,Vi~ua!Basi~,f~~~llpl!cali~ns, I:f'tlp
s;
[li]
<?
~
Hide

Locate

Back

,--' -"'-'"

~_

.. ,.. ..

-~

___

__

,';

Y_~_~"_

~[9]~

FCO;'Nl'l:d

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

38

_.'~"_'V

~,'

Vertex List Example

[,amples
~ 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

00

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
segment.

ill

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
.~.J

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

',I

>.

,~1,:

" . :,: "--,,:,~,: :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.

40

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.

THE NET
Got Net? http://msdn.microsoft.com/isv/ technology/ vba/default.aspx
gets us to Microsoft's VBA web site. If this URL is difficult to remember,
you can also use http://msdn.microsoft.com/vba, 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
environment.

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

n,

E2!Jill'!i.
'"~""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.

GIi!

VI\lIoI Uatl <

)'OUfS

itud ~lJl,ItU"J

lotl.ay!

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

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


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

I~Q~Q""

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

hpl"",,,

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

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

~MtrlbM

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- ,.y.il~blll 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"tt.to!~II., ~~ ..

a..,.."

Ml!!rn~t (I t tf'J. Hit;I'on!t \1ft:. J"4'tto.e, 'I*~"T' .


..bl)t.It ~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

wn

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\.

Lt~.U.I!I.Q..,eArtntli

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


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

pr(llflJtt~

~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

'!'Ii:;

(,)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 l1n.o..er.'tt'OJ Ctt

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

I The Net I

41

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

~BENTLEY

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

~{markets

Search

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

1vlicroStation.
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

Among

Ptl'J<lCY

the

Bentley

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

Discussion
Groups
we
find
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

42

able to help others by answenng their questions in this Discussion


Group.

~dit

Eile

~ Back

~iew
T

t}

Fgvorites
T

lools

tielp

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

-j AQ.dress !~ http://www.bentley .com/en-us/communitY/DiScussion+GroupsIDirectory/

.~. ~Go

iLinks

~BENfLEY

Discussion Group Directory


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

ft'Onl

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

NicroStation
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_

THE OBJECT BROWSER


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

43

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'
I1i'
I1i'
I1i'

Gl <global s>

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

ActiveMode lReference
ActiveSettings
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'
~

ti:I
ti:I
~
~

li!'J
~

li!'J
~

ti:I
ffl

ACSManage r

Properly Ac1iveDesignFile As Desi<tnFile


read-only

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'

Classes

:.~

~ ComplexSh ape Element

i..>

~ Comp lexS trin gElemen t

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

ti:I
i!J
i!J
ti:I
ti:I
ti:I
ti:I
;ef!

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

.:JiOesignFiie

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

",~

. I1i'

I1i'
I1i'

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

'

mDimensionStyle
i!l

DimensionStyles

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

I1i'
y) .0<'\

Editor
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

44

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.

REVIEW
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:
[B

Modules

[B

Forms

[B

Classes

[B

Procedures and Functions

MODULES
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

45

46

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
this:
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

47

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
wish.

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

. I

: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

48

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.X
LineSt . Y CenY
CenZ
LineSt.Z
Lin eEr.X
CenX + 1
LineEn . Y CenY
Li ne En.Z
CenZ
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
LineSt.Z
CenZ
Li neEn . X CenX
LineEn.Y
CenY - 1
LineEn.Z
CenZ
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

49

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.

FORMS
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

50

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

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,
click
the
Run
button.
The
form
is
displayed so we can
enter numbers for X,
Y, and Z coordinates
to place our targets.

S1

Macro name:
Main

'

"

"

,
Step Into
Edit

Macros in:

[----------------::::1
<All Standard Projects>
v,

Delete

IDescription:

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

52

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.

CLASSES
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

S3

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.X

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

LineEn.X
LineEn.Y
LineEn.Z

X + 1
Y
Z

=
=

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

Z
X
Y - 1
Z

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


Application,ActiveHodelReference , AddElement HyLine
'Create Circles

CenPt.X

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.

RotMatrix)

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

S4

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
LineSt.X
X- 1
LineSt.Y
Y
LineSt.Z
Z
LineEn.X
X+ 1
LineEn. Y Y
LineEn.Z
Z
Set MyLine = Application.CreateLineElement2(Nothing, LineSt, LineEn)

Application . ActiveModelReference.AddElement MyLine


'Create Vertical Line
LineSt. X X
Lin eSt.Y
Y+ 1
LineSt.Z
Z
Li ne En. X X
Y- 1
LineEn.Y
Lin e En.Z
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.Y
Y
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

55

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.

PROCEDURES AND FUNCTIONS


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

S6

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.X
0
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.X
X
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

57

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
Optional
Parameter is
declared in
the
Procedure,
any
parameters
after it must
also be
optional.

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

58

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

59

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

60

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
expecting).

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:

The
underscore
C) character
allows one
line ofcode to
span multiple
lines.

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
PPoint(O)
X + XChange
PPoint(l) = Y + YChange
PPoint(2) = Z
PolarPoint = PPoint
End Function

I Procedures and Functions I

61

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
function.
Sub TestPolarPoint ()
Dim StartCen As Point3d
Dim CenPt As Point3d
Dim RotMatrix As Matrix3d
Dim X As Variant
StartCen.X
2
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.X
X(O)
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

62

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
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
function:
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

63

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
example.
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")
Cel13
End Sub

64

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)
X

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

65

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
dblX
X
db l Y Y
dblZ
Z
dblX
1
db l Y 2
dblZ
3
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
parameters.
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)
X

Y 2
Z 3
End Sub

Taking the additional step of declaring a parameter as ByVal guarantees


the integrity of the parameters.

66

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

67

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
Explicit"
m
the
General
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 [

Help

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


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

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

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

68

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


[B

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.

[B

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
declaration.

Variables
1+N+3=7

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?


"IS".

In this chapter:
[B

Standard VBA Variable Types

[B

MicroStation-Specific Variable Types

[B

Assigning Values and Setting Objects

[B

Arrays

[B

Constants

[B

Variable Names

[B

Option Explicit

[B

Using Variables

69

70

I Chapter 6: Variables I
STANDARD VBA VARIABLE TYPES
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
N

" 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.

Integer
Di m PageNumber as Integer
PageNumber

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.

Long
Dim MySalary as Long
MySalaray

123456

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

71

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


should only use it when we need it.

Double
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
N

1.23456789012346

12 . 3456789012346

123 . 45 678 9012346

1234 . 56789012346

12345 . 6789012346

123456 . 789012346

1234567 . 89012346

12345678 . 9012346

123456789 . 012346

1234567890 . 12346

12345678901 . 2346

72

I Chapter 6: Variab les I


N

123456789012.346

123456789 0123 . 46

12345678901234.6

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.

Boolean
Dim ICanLearnThis as Boolean
ICanLearnThis

True

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

Date
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.

String
Di m MyLevel Name as St r ing
MyLevelName

"utilElectricity"

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.

Object
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

73

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.

Variant
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.

MICROSTATION-SPECIFIC VARIABLE TYPES


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.

Application
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:
[B

Get the ActiveDesignFile property

[B

Get the ActiveModelReference property

[B

Get the ActiveSettings object and its properties

[B

Get the ExecutingVBProject object and its properties

[B

Get the UserName property

[B

Get the left, top, width, and height properties

74

I Chapte r 6: Vari ables I


OesignFile
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.
[8

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


Keywords, Manager, Subject, and Title Properties

[8

Get the ForrnatMajorVersion and FormatMinorVersion


properties

[8

Get the Levels collection

[8

Get the Models collection

[8

Get the N arne and Path properties

ModelReference
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.

Level
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:
[8

Description

[8

ElernentColor

[8

ElernentLineStyle

I MicroStation-Specific Variable Types I


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

LineElement
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.

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

CreateEllipseElement2(Nothing.

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.

ArcElement
Dim MyArc As ArcElement
Dim RotMatrix As Matrix3d
Set MyArc

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

75

76

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.

TextElement
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.

ASSIGNING VALUES AND SETTING OBJECTS


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
Level.
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

77

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
Set.

ARRAYS
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
StartPoint(O)

4.5

StartPoint(l)

5. 6

StartPoin t( 2 )

6.7

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

78

I Chapter 6: Variab les I


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

3.~4159

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.

VARIABLE NAMES
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
VBA.

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

79

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

~yL'ne

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
variable.
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:

http://msdn.microsoft.comllibrarylen-uslmodcore!html!
decon VariableNames. asp
Another way to become familiar with naming conventions is to search
online for "variable naming convention" or "Hungarian Notation".

80

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.

OPTION EXPLICIT
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

81

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

i4

~ Auto Quick I nfo


~ Auto Data Tips
Window Settings

~Drag-and-Drop Text Editing


~ Default t o full Module View
~ Procedure Separator

OK .! I

Cancel

II

Help

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


"Option Explicit" for us.

USING VARIABLES
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

1234567

MyHourly

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

82

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

REVIEW
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
MicroStation
VBA".
Four
different functions are then
used
with
the
variable
BookTitle as a parameter and
the result displays in four
MessageBoxes.

LEARNING ~IICROSTATION IIBA

OKJI

Learning

83

~lic

oStation IIBA

learning microstation "ba

84

I Chapter 7: Working With Text I

VBA STRING FUNCTIONS


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

UCase
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:

"j

[ Cancel)

Isidewalk
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
file.

-..,=-="~''''~

..

.. ,~,

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

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


Levels . Filter

Edit

'@ 'f(I I Ii@

ern

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

ISymbology IByLevel :\'j II !~ (no

chapter07.dgn

Name

j Number

[ @All Levels
- [> Fillers

,.,.!'::-~~~~

. (.:".

Default

LCase
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

Debug.Print
is used to
place text in
the
Immediate
Window. It is
often used to
display text to
aid in
debugging
our
applications.
To view the
Immediate
Window, go
to the VBA
menu View>
Immediate
Window.

85

lcase

<

lo~ers

capital letters.

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


the text to a variable.

StrConv
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

Learning

~1icrostation

Vba

This example uses the vbProperCase constant


to capitalize the first letter of each word.

OK]

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

86

I Chapter 7: Working With Text I


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

Monday

Mon

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

OK

MonthName
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.
March
April

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

I-Iay

June
July
August
September
October
November
December

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

87

StrComp
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.
Sub

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
Next
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.
Sub

TextWork08 ( )

Debug . Print
Debug . Print
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 "

,
,
.
.

vbTextCompare)
vbTextCompare)
vbBinaryCompare)
vbBinaryCompare)

88

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

alphabetically.
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
strNms(O)
"Jerry "
strNms(l)
"Ca ndice "
strNms(2)
"Brandon "
strNms(3)
"Kyle "
"B enjam i n"
strNms(4)
"J acob "
strNms(5)
st r Nms(6)
"Nathan "
"Ol i via "
strNms(7)
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
Wend
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 "
"Brandor"
str~ms(2)
strNms(3)
" Ky 1 e "
"Benjamin"
strNms(4)
"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
Wend

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.

89

90

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
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.

Left
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.

Right
Function Right(String. Length As Long)

I VBA String Functions I

91

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
Wend
End Sub

This

procedure

displays

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.

Mid
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

92

I Chapter 7: Working With Text I


We used the Mi d function three times in
the above example, each time a little
arning
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.

Replace
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.

InStr
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

OK"n")

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

93

''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, "\ " )
FirstFolderPath
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

94

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
We'1d
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.

InStrRev
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

95

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
backslash.

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:

NewTextFilePath

""

Variantistring(O to 3)

XSPIrt

xSplrt(O)

"C:"

String

xS plrt(1)

"Microstation VBA"
"docs"

String

xSplrt(2)
xSplrt(3)

"chapter07.dgn"

string

string

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

96

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
variable.
Here is what the
fi le looks like
when it is opened
in Notepad.

EJle

!;.dit

FQ.rmat

yjew

tielp

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

97

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.

169

170
171
172

173 174
175 176
177

v,
.>.i

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


MSForms.Returnlnteger)
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

98

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.

FormatCurrency
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

$123,456.78

$123,457

$.12

$0.1 2

OK .. ~

FormatNumber
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

99

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

m
123,456.73

123,457

.12

0.12
OK

'J

FormatDateTime
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

vbGeneralDate)
vbLongDa t e)
vbLongTime)
vb ShortDate)
vbShortTime)

100

I Chapter 7: Working Wi th Text I

1/1 /20054:45:00 PM

C OK

-j

Saturday, January 01, 2005

K"'---OK-"-"-ll

4 :45:00 PM

r--' OK -'-

16:45

1/1/2005

_.
OK

r-

OK

..

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

already

have

examples

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.

vbCr
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

101

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

REVIEW
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.

102

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.

NUMERIC FUNCTIONS
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.

103

104

I Chapter 8: Working With Numbers I


Addition
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
Loop
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

105

2
3

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.

Subtraction
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.

Multiplication
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

106

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.

Division
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

107

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

108

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
XChange
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
Debug.Print
Debug.Print
Debug.Print
Debug.Print
End Sub

109

"XC hange = " & XChange


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

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

~I

(,

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 ,

>

110

I Chapter 8: Working With Numbers I

The values used


for the previous
exampl es make
use of th e right
triangle:

4
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
results.

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

36.869897645844

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

111

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.
YChange

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.

CLng
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

112

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
Debug.Print
End Sub

CLng(4OOOO.56)
CLng(4OOOO.23)
CLng(-4OOOO.56)
CLng(-4OOOO.23)

40 000
-4 0 001
-40000
V

,...;:;f.;

Fix
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
Debug . Print
Debu g .Pr int
Debug . Print
End Sub

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

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.

CDbl
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

113

Val
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
found.

IsNumeric
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

114

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.

False
True
False
Fa l se
False

,I

Round
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
3
1. 46
1. 5
1
2

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
denominator.
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

115

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

-1
0
1

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
Randomize
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
Next
End Sub

116

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.

REVIEW
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.

MESSAGEBOXES
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.

117

118

I Chapter 9: Standard VBA Calls I


Sub TestMessageBox2 ( )
Di m MsgResp As VbMsgB ox Result
MsgResp
MsgBox("Unab l e to open file.", vbAbortRetryIg~ore)
MsgResp
MsgBox( "F ormat Hard Dri ve?", vbOKCancel)
MsgBox( "New Level Added .", vbOKOnly)
MsgResp
MsgResp
MsgBox("Not Connected to Internet . ", vbRetryCancel)
MsgBox( "Do you want to continue? " , vbYesNo)
MsgResp
MsgResp
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 [

B.etry

Not Connected to Internet.

II

Ignore

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 + _
vbQuestion)
End Sub

120

I Chapter 9: Sta nda rd VBA Ca lls I

Unable to open file.

Format Hard Drive?

C OK
. ,.
~

It3

New Level Added.

Mi~roStation.

""

".

J[

Cancel

Do you want to continue?

r. y~~ ] I

No

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
~<''''''''

~'?>rnN"""'>:'--'~)~~"'<""""

'''- ,

;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).

INPUTBox
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

121

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:

OK
[ 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:

OK

.1

Cancel )

I~

____J

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.

122

I Chapter 9: Standard VBA Cal ls I


Now!

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

DateAdd
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

5/28/2005
5/28/2005
5/28/2005
5/28/2005
5/28/2005
5/28/2005
5/28/2005
5/28/2005

& 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
11:40:54
11:40:54
11:40:54
11: 40:54

AN
AM
AJI'!
AJI'!
AM
AM

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

123

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

DateDiff
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.

Hour-s
8718445
Minutes 5 2 3106644
seconds 31386398632
Months
11936
weeks
51895
Year-s
995
Quar-te r- s
3979

<

>,-

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

124

Timer
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.
43466.31

Sub TestTimer ()
MsgBox Timer
End Sub

FileDateTime
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~~~--/"

<~'M."""'-'"

..;~.,.~~~=

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

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

.'"

.'. ~

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

C6K-..~

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

Size: 976896

r--OK-

M
]

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

MkDir
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

125

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

RmDir
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

Dir
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
DirReturn
Dir
We nd
End Sub

126

I Chapter 9: Sta ndard VBA Ca lls I

C:\Program
C:\Program
C:\Program
C:\Program
C:\Program

Files\Bentley\.
Files\Bentley\ ..
Files\Bentley\Documentation
Files\Bentley\Licensing
Files\Bentley\MicroStation

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
Wend
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 on.host 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

~I

v .

>1

Now we are looking


folder for all files (*.*).

III

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

127

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
Wend
ReDim Preserve DgnFiles(UBound(DgnFiles) - 1)
End Sub

DgnFiles(3)
DgnFiles(4)
DgnFiles(5)

"C:llvlicroStation
"C:llvlicroStation
"C:llvlicroStation
"C:'lvlicroStation
"C:llvlicroStation

VBA 'Docs'chapter04 .dgn"


VBA'Docs'chapter06.dgn"
VBA'Docs'chapter07 .dgn"
VB A'Docs'chapter08.dgn"
VBA'Docs'chapter09.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".

Kill
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
caution.

128

I Chapter 9: Standard VBA Ca lls I


Beep
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
something.
Sub TestBeep()
Beep
End Sub

SaveSetting
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

E.i1e

~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".

GetSetting
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

MicroSt~tion

VBA" , "Chapter g", _

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

IlnputBox I

129

DeleteSetting
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
one.
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.

GetAIiSettings
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

130

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

"Save Setting"

"ttWorks"
"SaveSetting2"

"ttWorks2"
"SaveSetting3"

"ttWorks3"

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


and values of the results of the GetAllSettings call.

READING AND WRITING TO

ASCII FILES

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

131

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.

FreeFile
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

132

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
files.
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

133

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. "
</Row>"
exeFile = Dir
We nd
Print #FFileA." </Table> "
Print #FFileA. " </Worksheet> "
Print #FFileA. "</Workbook> "
Close FFileA
End Sub

Excel is particular about the


formatting
of
the
XML
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 ~ ~

A1

' ''' '

A
~

c.alsexi3.
E.~~!iT1!l P ex e

r.R~ ~~~~:f~~!;:

~ crdaemon exe

1~

~ r:1

f,,-; accwiz. exe


B

313112003 60000 AM
- jI311Qo~- 6 oQ~'~ci .A~v1

;~~~~~; ~.~~.~~ ~~

3131120036:0000 AM
~ ",,~,c'~'i '~ EXE Files /."_~=c"c,.,_,.,."c~.,-,,-c __ ,,.
Ready

134

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
Wend
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
example.
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

135

TextPo i nt.X
TextPoin t .Y
TextPoint.Z
Set TextElem

XSp l it(O)
XSplit(l )
XSplit(2)
= App l ication.CreateTextElementl(Nothing, _
XSplit(3), TextPoint, RoHlat)
ActiveModelReference .AddElement TextEle~

Wend
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
specified.

Note 4

Note 1

Note 3

CONTROLLING CODE EXECUTION


It is essential that we know how to loop through code multiple times and

execute code based on certain conditions.

136

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

137

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
OOOGCD
i ' , . , . , ', 'C:DC
',
CDOOO
OOOOOC
OO' OO"OC

. ..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
values.

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

138

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.X
XSplit(O)
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
Wend

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
Loop
Set CellElem = Application.CreateCellElement3( "Column ", _
InsPt , Tru e)
ActiveModelReference.AddElement CellElem
End Sub

I Control ling Code Execution I

139

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
cell.
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
keyboard.
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
Loop
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

140

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
Do
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"
text.
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
true.

I Control ling Code Execution I

141

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
Next
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
v

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
Else

142

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)
Else
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
statement.

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

143

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 ."
Err.Clear
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

144

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

!;.nd

tleip

As you become more experienced in programming, you are better able


to anticipate potential data entry problems and other issues that cause
errors.
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.

'-oK-O.Oll

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

145

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

End

Debug

: )1 [

Help

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
errhnd:
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

146

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

147

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
raised.

148

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"
Err.Clear
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,
not
a
although
comprehensive list, of
useful and commonly
used VBA calls. You can
use the Object Browser
in VBA to display all
VBA
calls
natively
available to us. We will
Object
discuss
the
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
member.

Function DateDiff(lnleNal As String, Date1, Date2,


[FirstDayOtweek AsVllDavOfWeek = vbSunday],
[FirstWeekOfYear AsVIlFirS1WeekOfYe,n = vbFi rstJan1])
Member of VBA.OateTime

I Review I

149

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

E:<ample

:':, ..

Returns a Variant ( Long ) specifying the number of time interva ls between two specified
dates.
Syntax
DateDiff(interval, date1, date2[, firstdayof,,'eek[. firstweekofl1ear]J)

The DateDiff function syntax has these named arguments :

Part

Description

interval

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.

Settings
The interval argument has these settings:

REVIEW
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.

150

I Chapter 9: Standard VBA Calls I

10

Visual Interface
It is time to begin working with the
-- Insertion Point
1 X 10

lz
Cells

]0
\0

--.-------------.--..--... --.-;

\ 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.

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

151

152

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.

Conlrols

,~- A

I
abl ~ [ffij

17r.;=!O-,

"",~'J(T.i'~" )~""7"'-""'~'r j\"''''''''''''''~'''" ."'.......-'l:~~,

Ce"iLlnsertion ,
Level
Cells

.~.

\:

.. "

I SIDEWALK
I Column

" l'8J
3
3

Insertion Point

jJ~~_~__~J
Insert

Cancel

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 .

..!..l":'};:.YJ1dl

PROPERTIES, METHODS, AND EVENTS


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.

Properties
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>
key.

CommandButton 1
False

: 0

&H8000000F&

153

-.:J

1 - fmBackStyleOpaque
False
CommandButton 1

v:

At run-time, properties can be set as follows:


CommandButtonl . Enabled

True

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
@'Width

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

154

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
below).

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.

NewLevelName

I Common Control Properties I

155

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~~

. _. ___._._. .

._

_._ .

...:J

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.

Sub

(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
pressed.
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.

COMMON CONTROL PROPERTIES


Before discussing each control individually, let's talk about properties
and events that nearly all controls have in common.

156

I Chapter 10: Visu al Inte rface I


Name
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

Name:"

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

157

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.

Visible
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.

Enabled
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.

TabStop
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
tabbing.

Tablndex
The TabIndex property determines the order in which controls receive
focus as you Tab from control to control.

158

I Chapter 10: Visual Interface I


Tag
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.

ControlTipText
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:

r-

~
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.

Label
Labels help users know what to enter or select. Properties of note are as
follows:

abl

Captio n

The text displayed to the user.

Font

The font used to display the Caption.

TextBox
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

159

Properties
Text

The text disp layed in t he TextBox.

Locked

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.

MaxLength

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
WordWrap

When True, text inside the TextBox is wrapped to the


next line. When False, text is scro lled on one line.

PasswordChar

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.

160

I Chapter 10: Visual Interface I


Events
KeyPress

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.

Properties
Text

Text selected or entered in the ComboBox.

Style

O=Combo (select from li st or type into a ComboBox)


or 2=List (user must select from list).

Listlndex

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.

ListCount

Number of items in the ComboBox.

ColumnCount

Number of columns per row in the list.

I ListBox I

161

Methods
Addltem

Adds a List Item to the ComboBox.

Removeltem

Removes an item from the ComboBox. Specify the list


index of the item to remove.

Clear

Clears all items from the ComboBox list.

Events

Cli ck

Occurs when user cli cks on an item in t he ComboBox.

Change

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.

llSTBox
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".

Properties
Text

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.

Methods
Add ltem

Same as ComboBox

Removeltem

Same as Combo Box

Clear

Same as ComboBox

I Chapter 10: Visual Interface I

162

Events
Click

Same as ComboBox

Change

Same as ComboBox

p" CHECKBox
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.

Properties
Caption

Text displayed next to the CheckBox.

TrippleState

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.

Value

True, False, or Null.

Events

r.

Click

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).

Change

Value of the CheckBox changes.

QPTIONBuTTON
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

163

Properties
Caption

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.
If

If

If

If

lf

If

Events
Click

See CheckBox Click Event.

Change

See CheckBox Change Event.

DblClick

This event is triggered when a user double-clicks the


control.

~ TOGGLE BUTTON
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
italicized.

Properties
Caption

Text disp layed on the Toggle Button.

TrippleState

See CheckBox.

Value

See CheckBox.

Events
Click

See Option Button.

Change

See Option Button.

DblClick

See Option Button.

I Chapter 10: Vi sua l Interface I

164

XV"

[J

FRAME
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.

Properties
Caption

The Caption shown in the upper left-hand corner of the


frame.

Visible

True or False.

.-J COMMANOBuTTON
Use CommandButtons to give users something to click on, such as these
(commonly captioned): "OK", "Cancel", "Print", "Open", and "Close".

Properties
Captio n

Text displayed in the Button.

TakeFocusOnClick

Determines whether the Button receives focus


when the user clicks the button or if focus remains
with the previous ly selected contro l.

Events
Click

Triggered when the user clicks the button.

....!J TAB STRI P


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

165

Properties
MultiRow

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
row.

Selected Item

Which tab is selected?

Style

0= Tabs, 1 = Buttons. Style controls how the Tab Strip


is to be displayed.

TabOrientation

0= Top, 1 = Bottom, 2 = Left, 3 = Right.

Methods
Tabs.Add

Used to add Tabs to the Tab Strip.

Events
Change

..:J

Triggered when the active Tab changes.

MULTIPAGE
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.

Properties
Value

0= First Tab, 1 = Second Tab, etc.

Pages.Count

The Number of Pages.

MultiRow

See TabStrip.

Methods
Pages.Add

Used to add pages to the MultiPage control.

I Chapter 10: Vi sual Interface I

166

Events
Change

.:!:I

Triggered when the Active Page changes.

SCROLLBAR
Scroll Bars allow the user to select and change numeric values. The
rectangle that moves as the value changes is called the Thumb.

Properties
LargeChange

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.

Min

The Minimum Va lue the Scroll Bar can have when the
Scro ll Bar is Horizontal.

Max

The Maximum Va lue the Scro ll Bar can have.

Value

The Numeric value of the Scroll Bar.

Events

Change

Triggered when the value changes by clicking in the Large


Change area or on the Small Change arrows and after the
Thumb is released.

Scroll

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.

I SPiNBuTTON
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

167

Properties
Delay

Time in mill iseconds after the user begins ho lding


down a button before t he val ue beg ins scroll ing up or
down

Min

Minimum Spin Button Value.

Max

Maximum Spin Button Value.

Small Change

Amount value changes as use r cl icks butt ons.

Value

The Value ofthe Spin Button.

Events

GZJ

Change

Trigge red when value changes.

Spin Up

Triggered when the value increases.

SpinDown

Triggered when the value decreases.

IMAGE
Use the image control to display images in your interface. Acceptable file
formats are .bmp, .gif, .jpg, .wmf, and .ico.

Properties
BorderStyle

0= None, 1 = Single.

Picture

The file to display in the Image Control.

PictureSizeMode

0= Clip, 1 = Stretch, 3 = Zoom.

PictureTiling

True to tile pictures inside the Image Control, False


for no tiling.

USER INTERFACE EXERCISES


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

168

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.
~':J~~~

,.",~

('{~"'~-"~""';:<"""7~C?.,..,."J, .... -;;,'"!:"'~'f'-"""'"

:~~!1n~~!tiqn ;,' :.,_.;":.,~.,,,.. /,'.;.. ~


level I
-=oJ
Cells

--------=~:;

1-1

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
Z
10
ComboBox 1 and ComboBox2 .
names
.
..'.
Change the names to cmbLevels and
Insert
Cancel
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
"Cells':
PICK

..,

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
"Pick".
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
Dim
Dim
Dim
For

Sub UserForm_InitializeC)
MyLevel As Leve l
MyCellEnum As CellInformationEnumerator
MyCell As Cell Information
Each MyLevel In ActiveDesignFile.Levels
cmbLevels.AddItem MyLevel.Name

Next
Set MyCellEnum = _
Application . GetCellInformationEnumeratorCTrue. True)

I User Interface Exercises I

169

While MyCellEnum .M oveNext


Se t MyC el l = MyCell En um . Current
cmbCells.Addltem ~yCell .Na~e
Wend
End Sub

Here is the code in the Initialize Event of the UserForm. It should look
like this:

Private
Di m
Dim
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

Next
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
Wend
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
ActiveDesignFile.
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.

170

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
0
KeyAscii
End Select
End Sub

171
0

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
0
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
0
KeyAscii
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

172

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
InsPt.X
CDbl(txtX.Text)
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
th
___ 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

173

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
Do
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 <>
Then
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
nn

nn

174

I Chapter 10: Vi sua l Inte rface I


End Select
Loop
Exit Sub
errhnd:
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:
[E

Place the selected X, Y, and Z point elements into the three text
boxes.

[E

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.

POINT LIST READER


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

175

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

2
2
3
3,5
4
5

3
3
3

3
3

Remove Selected

0
0
0
0
0
0
0

Plot Points

1
2

3
4
5

6
7

Cancel

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 )

176

I Chapter 10: Vi sual Inte rface I


lstPoints . AddItem PointSplit(O)
lstPoints.List(lstPoints.ListCount
1. 1)
lstPoints.List(lstPoints.ListCount
1. 2)
lstPoints . List(lstPoints.ListCount - 1 . 3)

PointSplit(l)
POintSplit(2)
PointSplit(3)

End If
Wend
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.
Private
Dim
Dim
Dim
Dim
Dim
For

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

177

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
Next
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
display.
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.

WRITE OUT FILE


The next macro we are
going to write utilizes
CheckBoxes
and
OptionButtons. Here's the
visual interface:

r
O

"

i
!

Items To Write

"" File Format

rv Levels

P' Line Styles

I P' Text Styles


II P'Views
II P'
! .

-------"-000"]

r. :~~siij~:~:tl
[

""C_~~~~__"""_" __ """"_
OK

Author

i P' Subject
1L_.
P'Title
_ _ _ _ _ _ _l

Cancel

178

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
cmdCancel.

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
m.
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
tomorrow.
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

179

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

180

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
Next
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
Next
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
Next
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

181

182

I Chapter 10: Vi sua l Interface I


ZOOM AND PAN
Here is a little program that provides real-time interactive panning and
zooming of the views of the ActiveDesignFile.

Views
Zoom

OUT

11

O!J

IPan I

...iI

...tJ

Views

11

Zoom

Pan

~
IN

...J

...iI

.::l
...tJ

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.

r:
~:

.... .

~:.

~ ::..

. .. . . .. .

~ove...

..

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
Next

I Zoom And Pan I

183

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
srcY.
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
value.
We are going to create two procedures for performing the zoom and pan
operations:
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.X
XPan
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.

184

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

185

REVIEW
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
interface.
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.

186

I Chapter 10: Visual Interface I

11

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
VBA.

In th is chapter:
[B

The Object Browser

[B

Auto List Members

[B

The MicroStation VBA Help File

[B

Adding Watches

[B

The MicroStation Object Model

187

I Chapter 11: The Mic roStation Object Model- Objects I

188

THE OBJECT BROWSER


i

...s

i!lJndow

tlelp

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
keyboard.

Classes
<globals >
~ AccuD rawHints
~ ACS Manager
Q

Members of 'Application '

,hI! @i'

~!
'-

~ Gt!!jllliiMi~!i!_

ACSManager

@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(

~i

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
ListBox.
The
Members
ListBox
displays
the
Properties, Methods, and
Events of the selected Class.

IMembe rs of 'App lication'


@I Name

AI

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

189

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.
IMicroSbrtionDGIl

v-I

~"' i

I~.I It\

Iitll .~

f-R

text
.- .. Search Resu lts - ..... -.-.... ..... :. Jh
Hi~
(de-=s-ea-:rch-:R-es-:ultc-ls ..- .......--..... -.-........_-........... _.-.-..
Library

, Class

Ii

, Member

A,

1\
'lCj~I!lII@!
J. . . .
..,~ ,
i
1\ MicroStationOGN ~ Appli cationElement
~. AsTextElement .='=') I
1\ Mi croStati onOGN @! ArcEl em ent
@i' AsTextElement
I
1\ Mi croStationOGN !el1l.uxiliarICoordinateSvstemEIE~ AsTextElement :"-: "
Members of 'Text Node Element'

Classes

@! TagOefinition

:'SI . ,~ AddOa tabaseLink

\i!a TagOefinitions

" ,~

..,~

@! TagElernent

AddTag
lI.ddTags

\i!a TagSet

~, ."~[A(j(jTeXiLine

~ TagSets

Y:i

..........J

" '~ 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.

190

I Chapter 11: The MicroStation Object Model- Objects I


AUTO LIST MEMBERS
VBA gives us help as we write our code at design time.

~:~B~[~~~a=:~~~it~~~~~~n
End Sub

jl

@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
I.I.

l'lsgBo}{ myapp. Acti veDesignF ile.


End Sub

n.

~~:AddNewLevel
.~~

AllachColorTable

@i' Author
@i' Client
.,,~ Close
@i' Comments
@' Company

The ''Auto List Members" list allows us to 'drill down' through an Object
Model.

MICROSTATION VBA HELP FILE


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

191

Object, Property, Method, or Event that was selected in the Object


Browser.

~~.--~~- ~?'r-;-:~

;-M'J""iRe,. ... ..,

"'~<-~-,...~"'-..;,.'" ~>

''''_''fi;;:;<p

r'

~Tq

h fl

-"

-~,""""'f'''' "~~',;:

~~->

,.

~t~

OJ

r-

"< ""'~

-----,.

&1 MicrC!SIi!tiop VB V}sual Basic for AppiicaticlV5 Help.,;.,., .,,,,,~\;,~;,,,, ...":.,:'",< ;:{~.'~h"""" :"'o>~2{,","""~ ,;,' ~l1~
~1Il

c:i>

<?

~~.

-~~--~ LOC~~ _ _8_ac_k__ ~~~~~rP_ri....nl~-=O:::p.::.tio::.n.::.s_ _ _ _ _ _ _- - - - - - - - - - - - - - - - - - - : -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
~..

r-

"

ApplicationElement Object
..... :,
App!icationlO Property
ApplicationObiectConnector Obje

,e.ctl 'leDeslgnF I I e {OasignF i IS}

r--

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

t--

Ac ti v ~S e ttin~s { Sett i ngs}

ApplyRotation Property
ApplyS aveaViewE!ement Methoc

t--

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

AsAu~ilialyCoOidinateSystemElell

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~

t--

r-t--

Corrmar.,j 5tate

t--

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)

t--

Has A ctl v eMod~ I Refe r'e nG" {Bcal)

t--

He ight {LonQ )

t--

[s Acaderr,i eV er- s i on (800 I )

t--

IsCel IL i br-or" y Attoch"d (Bao l

t--

IsRegis tered ( Boo l

t--

I s Ser i (] I i

r--

Il ey inArguments {String)

Z6<j

,,'
S.:

{8 00 I }

t-- LaftFcs i t i on {Long)

t--

,<

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.

192

I Chapter 11: The MicroStation Object Model - Objects I


ADDING WATCHES
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

Ed~ion"

DesignFileiOesignFile
ModelReferenceiModelRef, I
Settings,Settings
WorkspaceNVorkspace
CeliLibrary
;;
8spline,Bspline
I'
CadlnputQueue/CadlnputQ' I'
string
I:
CommandStatelCommandS
Long
I;
CursorlnformationlCursorlr \'
ObjectNBProject
string
800lean
800lean
Long

I:.

CurreniGraphicGroup

HasActiveDesignFile
HasActiveModelReference
Height
IsAcademicVersion
IsCeliLibraryAttached
IsRegistered
Is Serialized
KeyinArgumenis

"C: 'Program FileslBeniley'Microstation'J.Jstation .exe"


True
True
1208
False
False
True
True

LeftPo s~ion

-4

MdlLib
MessageCenier
Name
Path
ProcesslD
RasterManager
standardsCheckerConirolier

"ustation"
"C:'Program FileslBeniley'MicroStation"
3160

TopPos~ion

User Name
V8E
Version
Visible
W eith

Boolean

800lean
800lean
800lean
string
Long
MdlLibrarylMdlLibrary
MessageCenieriMessageC
string
r.
string
I:
Long
RasterManager lRasterMan I
standard sCheckerConirolh I
Long
I.
string
ObjectN BE
string
800lean
Long

I;'
1\

-4
"Administrator"
"Version 08 .09 .00.92 W ndows x86"
True
1608

THE MICROSTATION OBJECT MODEL


Let's begin looking at the I\1icroStation Object Model by examining the
Application Object.

IThe MicroStation Object Modell

193

Application Object
The Application Object points to the MicroStation Application.

Accessors
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

194

I Chapter 11: The MicroStation Object Model - Objects I


basis in MicroStation. Some read-only properties are marked with
"{read-only}".

Application
~ 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
ISaveAsEvents)
~ 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


~

~
~
~
~
~
~
~
~
~

~
~

195

Function Atn2(Y As Doub l e , X As Double) As


Double
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
(read-only)
Property Caption As String
Property CommandState As CommandState (readonl y)
Function
ConstructCirclesTangentToThreeElements(Element
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
ElementEnumerator
Sub CopyDesignFile(ExistingDesignFileName As
String, New Des i gn FileName As String,
[Overwrite As BooleanJ)
Function
CreateApp l icationE l ement(ApplicationID As
Long, ApplicationData As DataBlock) As
ApplicationElement
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

196

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
AreaPattern
Function CreateBsplineCurveElementl(Template
As Element. Curve As BsplineCurve) As
BsplineCurveElement
Function CreateBsplineCurveE l ement2(Template
As Element. Curve As InterpolationCurve) As
BsplineCurveElement
Function CreateBsplineSurfaceElementl(Template
As Element. Surface As BsplineSurface) As
BsplineSurfaceElement
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
CellElement
Function CreateCellEle ment3(CellName As
String. Orig i n As Poi nt3d. TrueScale As
Boolean) As Cell Element
Function
CreateCo mplexShapeElementl(ChainableElements()
As ChainableE l ement. [FillMode As MsdFi ll Mode =
msdFillMode UseAct i ve]) As ComplexShapeElement
Function
CreateComplexShapeElement2(ChainableElements()
As ChainableElement. [FillMode As MsdFillMode =

I The MicroStation Object Modell

197

msdFillModeUseActiveJ, [GapTolerance As Double


= -lJ) As Comp l exShapeElement
Function
CreateComplexStringElement1(ChainableElements(
) As ChainableElement) As ComplexStringElement
Function
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
ConeElement
Function CreateConeElement2(Template As
Element, Radius As Double, BaseCenterPoint As
Point3d, TopCenterPoint As Point3d) As
ConeElement
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
DatabaseLink
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

198

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
Element
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
ShapeElement
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
SharedCellElement
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


~

~
~
~

~
~
~
~

~
~
~
~

~
~
~
~

199

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
TextNodeElement
Property CurrentGraphicGroup As Long (readonly)
Property CursorInformation As
CursorInformation (read-on l y)
Function
DataEntryRegionFromCriteria(StartPosition As
Long, Length As Long, Justification As
MsdDataEntryRegionJustification) As
DataEntryRegion
Fun ction Deg rees(Ra dian s As Double ) As Double
Sub Delete XDa t um(X Data() As XDatum, Index As
Long)
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
DLong
Function DLongFromHexString(Value As String)
As DLong
Function DLongFromInt64(Value As Empty) As
DLong
Funct ion DLongFromLong(Value As Long) As DLong
Function DLongFromString(Value As String) As
DLong
Function DLongMod(Numerator As DLong,
Denominator As DLong) As DLong

200

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
String
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
DLong
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

~
~
~

~
~
~

~
~

~
~
~

~
~

~
~
~
~

201

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
{read-only}
Property Ha sAc tiveModelReferen ce As Boolean
{read-only}
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
{read-only}
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
Function
Matr ix3dFromMatrix3dTimes Matrix3d TimesMatrix3d

202

I Chapter 11: The MicroStation Object Model - Objects I

[B

[B

[B

[B

[B
[B

[B
[B

[B

[B

[B
[B
~
~

(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
Function
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
Matrix3d
Function Matrix3dFromSca l eFactors(Xscale As
Doub l e, Yscale As Double, Zscale As Double) As
Matrix3d
Function Matrix3dFromTransform3d(Transform As
Transform3d) As Matrix3d
Function
Matrix3dFromVectorAndRotationAngle(Axis As
Point3d, Radians As Double) As Matrix3d
Function
Matrix3dFromXYRotationSkewAndScale(XAxisAngle
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


~
~
~

~
~

~
~

~
~

~
~

203

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
Function
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
Boolean
Function
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
Double
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

204

I Chapter 11: The MicroStation Object Model - Objects I


~
~

~
~

~
~
~

~
~

~
~

Fun ction Ma t ri x3dZ ero () As Matri x3 d


Functi on
MdlCreateElementFromElementDescrP(ElementDescr
P As Long) As El ement
Function
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
String)
Function OpenDes i gnFile(DesignFileName As
String, [ReadOnly As Boolean], [V7Action As
MsdV7Action = msdV7ActionAskUser]) As
DesignFile
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
Function
Pl ane3dlntersectsP l ane3d(Inter s ectionRay As
Ray3d , Pl aneO As Pla ne3d , Planel As Pl ane3d) As
Boo l ean
Function
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

o
o

o
o
o
~

o
o
o
o
o

o
o
o
o

205

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

206

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
Double
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
Function
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
Point3d
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


~

~
~

~
~

~
~

~
~
~
~
~

~
~

207

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
Function
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

208

I Chapter 11: The MicroStation Object Model - Objects I


[8 Fun ct io n Point 3d DotPr oduct 3Points XY( Or i gin As

[8
[8
[8

[8

[8
[8

[8

[8
[8

[8

[8

[8

[8
[8

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
Function
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


~

~
~

~
~
~

~
~

209

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
Function
Point3dFromTransform3dTimesPoint3d(Transform
As Transform3d, Point As Point3d) As Point3d
Function
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
Function
Po i nt3dIsVectorInSmallerSector(TestVector As

210

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
Double
Function Point3dNegate(Vector As Point3d) As
Point3d
unction Point3dNormalize(Vector As Point3d) As
Point3d
Function Point3dOne() As Point3d
Function
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
Function
POint3dSmallerAngleBetweenUnorientedVectors(Ve
ctorl As Point3d, Vector2 As Point3d) As Double

I The MicroStation Object Modell


[B

[B
[B
[B

[B

[B
[B
[B
[B
[B

[B
[B

[B

[B

[B

[B

[B
[B

[B

211

Function
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
Double
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
{read-only}
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
Boolean
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

212

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
Function
Range3dlsContainedlnRange3d(InnerRange As
Range 3d, OuterRange As Range3 d ) As Boolean
Function Range3dlsNull (Range As Range3d) As
Boolean
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
(read-only)
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


~

~
~
~
~

~
~
~

~
~
~

213

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
Function
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
Double
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)
Sub
RemoveChangeTrackEventsHandler(EventHandler As
IChangeTrackEvents)
Sub
RemoveLevelChangeEventsHandler(EventHandler As
ILevelChangeEvents)

214

I Chapter 11: The MicroStation Object Model- Objects I


[B Sub

[B

[B

[B
[B
~

[B
[B

[B

[B

[B

[B
[B

~
~

[B

RemoveModal Di alogEve ntsHandler(EventHandler As


IModalDialogEvents)
Sub
RemoveModelActivateEventsHandler(EventHandler
As IModelActivateEvents)
Sub
RemoveModelChangeEventsHandler(EventHandler As
IModelChangeEvents)
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
Function
Segment3dFromPoint3dStartTangent(PointO As
Poi nt3d, Tangent As Po i nt3d) As Segment3d
Functio n Segment3d FromRay3d(Ray As Ray3d) As
Segment3d
Fu nction
Segment3dFromTransform3dTimesSegment3d(Transfo
rm As Transform3d, Segment As Segment3d) As
Segment3d
Function Segment3dFromXYZXYZStartEnd(XO As
Double, YO As Double, ZO As Double, Xl As

I The MicroStation Object Modell

~
~
~

~
~
~
~

~
~
~
~

215

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
Function
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
Function
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
String])
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
(read-only}
Sub StartBusyCursor()
Sub StopBusyCursor()
Property TopPosition As Long
Function Transform3dEqual (Transforml As
Transform3d, Transform2 As Transform3d) As
Boo l ean

216

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
Function
Transform3dFromMatrix3dAndFixedPoint3d(Matrix
As Matrix3d, Origin As Point3d) As Transform3d
Function Transform3dFromMatr i x3dPoint3d(Matrix
As Matrix3d, Translation As Point3d) As
Transform3d
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
Function
Transf orm3dFromSqu a redTran s form3d(Tran sf orm As
Transform3d, Pr i maryAxis As Long,
Secunda ['yAx is As Long) As Trans form3d
Function
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

217

[B Function

[B

[B
[B
[B

[B

[B
[B
[B
[B
[B

[B
[B
[B

Tra nsform3dFro mTransfor m3d TimesT ransform3d(Tra


nsforml As Transform3d, Transform2 As
Transform3d) As Transform3d
Function
Transform3dFromTransform3dTimesTransform3dTime
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
Function
Transform3dGetMatrixComponentByRowAndCo l umn(Tr
ansform As Transform3d, Row As Long, Col As
Long) As Double
Function
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
Function
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
Function
Transform3dIsRotateAroundLine (Transform As
Transform3d, Fi xedPoint As Point3d,
Directi onVector As Point 3d, Rad i ans As Double )
As Boolean

218

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
Function
Transform3dlsTranslateRotateScaleRotateCTransf
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
Sub
Transform3dSetMatrixComponentByRowAndColumnCTr
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
{read-only}
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
Vector3d
Funct i on Vector3dAddScaledCOr i gin As Vector3d,
Vector As Vector3d, Scale As Do uble) As
Vector3d

IThe MicroStation Object Modell


~
~
~
~

~
~

~
~
~

~
~
~

~
~

~
~

~
~

219

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

220

I Chapter 11: The MicroStation Object Model - Objects I


[B Function

[B

[B

[B

[B
[B

[B
[B

[B

[B

[B

[B

[B

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
Vector3d
Function
Vector3dFromMatrix3dTransposeTimesXYZ(Matrix
As Matrix3d, X As Double, Y As Double, Z As
Double) As Vector3d
Function Vector3dFromPoint3d(Point As Point3d)
As Vector3d
Function
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
Function
Vector3dFromTransform3dTimesVector3d (T ransform
As Transform3d, Vector As Vector3d) As Ve cto r3d
Function
Vector3dFromTransform3dTimesXY Z( Transform As
Transform3d, X As Double, Y As Double , Z As
Double) As Vector3d
Function
Vector3dFromTransform3dTrans1ation(Transform
As Transform3d) As Vector3d
Function
Vector3dFromTransform3dTransposeTimesVector3d(
Transform As Transform3d, Vector As Vector3d)
As Vector3d
Function
Vector3dFromTransform3dTransposeTimesXYZ(Trans
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


~
~
~
~

~
~
~
~

~
~

~
~
~

221

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
Function
Vector3d I sVector InCCWSector(TestVector As
Vector3d, VectorO As Vector3d, Vectorl As
Vector3d, UpVector As Vector3d) As Boolean
Function
Vect or 3d IsVectorInCCW XYS ector(T es tVec t or As
Vector3d, VectorO As Vector3d, Vectorl As
Vector3d) As Boolean
Function
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
Double
Funct i on Vector3dMaxAbsDifference(Vectorl As
Vector3d, Vector2 As Vecto r 3d) As Double
Funct i on Vector3dNegate(Vector As Vector3d) As
Vector3d
Function Vector3dNorma l ize(Vector As Vector3d)
As Vector3d
Function Vector3dOne() As Vector3d
Function
Vector3dPlanarAngleBetweenVectors(Vectorl As

222

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
Function
Vector3dSignedAngleBetweenVectors(Vectorl As
Vector3d, Vector2 As Vector3d,
OrientationVector As Vector3 d) As Double
Function
Vector3dSma l lerAngleBetweenUnorientedVectors(V
ectorl As Vector3d, Vector2 As Vector3d) As
Double
Function
Vector3dSmallerAngleBetweenUnorientedVectorsXY
(Vectorl As Vector3d, Vector2 As Vector3d) As
Double
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

ApplicationObjectConnector
~

Pro per t y Application As Application {read on1y}

Attachment
~

Sub Act iv at e ()

I The MicroStation Object Modell


~
~
~

~
~

~
~
~

~
~
~
~
~

~
~

~
~
~

~
~
~
~

~
~
~

223

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
(read-only)
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
(read-only)
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
(read-only)
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
Function
ElementCacheContainingFilePosition(FilePositio
n As Long, [CacheIndex As Long] ) As
ElementCache

224

I Chapter 11: The MicroStation Object Model - Objects I


[B
[B
[B
[B
[B
[B
[B
[B
[B
[B
[B
[B
[B
[B
[B
[B
[B
[B
[B
[B
[B
~

[B
[B

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
Element
Function GetElementBy1D64(Element1D64 As
Empty) As Element
Function GetLastValidGraphicalElement() As
Element
Function GetMasterToReferenceTransform() As
Transform3d
Function GetNamedGroup(GroupName As String) As
NamedGroupElement
Function GetReferenceToMasterTransform() As
Transform3d
Function GetSelectedElements() As
ElementEnumerator
Function GetSheetDefinition() As
SheetDefinition
Function GetUserAttributeData(Attribute1D As
Long) As DataBlock()
Function GetXData(ApplicationName As String)
As XOatum()
Function GetXOataApplicationNames() As
String()()
Property GlobalLineStyleScale As
MsdGl oba 1 Li neStyl eSca 1e
Property GlobalOrigin As Point3d
(read-only)
Property Graphi cal El ementCache As El ementCache
(read-only)
Function HasAnyXOata() As Boolean
Function HasXOata(ApplicationName As String)
As Boolean
Property 1s30 As Boolean
(read-only)
Property IsActive As Boolean
(read-only)
Property IsAttachment As Boolean
(read-only)
Property IsElementSelected As Boolean
{readonly)

I The MicroStation Object Mod ell


~
~
~
~
~
~
~
~
~
~

~
~
~
~

~
~
~
~

~
~
~
~
~
~
~
~
~

~
~
~

225

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
{read-only}
Property IsTrueScale As Boo l ean
{read-only}
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
{read-only}
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
ModelReference
{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)

226

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
Boolean)
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
String)
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
{read-only}
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

227

Attachments
~ 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
{read-only}
~ 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}

228

I Chapter 11: The MicroStation Object Model - Objects I


[B CadlnputQu eue
[B Function Get l np ut ( [Type l As MsdCa dln putType =

[B
[B

[B

[B

[B
[B
[B
[B
[B

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
LongJ)
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)

OesignFile
[B Fu nc ti on Ad dN ew Leve l CLevelN ame As Str in g) As
Level
[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


~

~
~
~

~
~
~

~
~

~
~

~
~
~
~
~
~
~
~

~
~

~
~

~
~

229

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
SavedViewElement
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
Variant
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
(read-only)
Property Keywords As String
Property LastSavedBy As String
Property Levels As Levels
(read-only)
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 )

230

I Chapter 11: The MicroStation Object Model - Objects I


~
~
~
~
~

~
~

~
~

~
~
~

Property NonM ode lEl ementCache As Elemen tCac he


(read-only)
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 =
msdDesignFileFormatCurrentJ)
Sub SetCustomProperty(Name As String. Value As
Variant)
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)

Element
~ Sub AddDatabaseLink(LinkToAdd As DatabaseLink)
~ Function AddTag(TagDefinition As
TagDefinition) As TagElement
~ Function Add Tags(TagSet As TagSet) As
TagElement()
~ 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
Long
~ Property AsApplicationElement As
ApplicationElement (read-only)
~ Property AsArcE l ement As ArcElement (readonly)
~ Property AsAuxiliaryCoordinateSystemElement As
AuxiliaryCoordinateSystemElement (read-only)

I The MicroStation Object Modell


~
~

~
~
~
~
~
~
~
~

~
~
~
~
~

~
~
~

~
~

231

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
{read-only}
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
{read-only}
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
{read-only}
Property AsPointStr i ngElement As
PointStringElement {read-only }

232

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
{read-only}
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
{read-only}
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


~
~

~
~

~
~

~
~

~
~

~
~
~
~

~
~

~
~

233

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
String()()
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
{read-only}
Property 1sArcElement As Boolean (rea d -only)
Property 1sAuxiliaryCoordinateSystemElement As
Boolean (read-only)
Property 1sBsp l ineCurveElemen t As Boolean
(read-only)

234

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
{read-only}
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


~
~
~
~

~
~
~

~
~
~
~
~

~
~
~
~

~
~
~
~
~

235

Property Is PointStringElement As Boolean


(read-only)
Propert y IsPo ss ibl yP l anarElement As Boo l ean
(read-only)
Property IsSavedViewElement As Boolean (readonly)
Property IsShapeElement As Boolean (read-only)
Property IsSharedCellDefinitionElement As
Boolean
(read-only)
Property IsSharedCellElement As Boolean
(read-o nl y)
Property IsSnappable As Boolean
Property IsTagElement As Boolean
(read-only)
Property IsTextElement As Boo le an
(read-only)
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
(read-only)
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
Point3d)
Sub Mirror3d(PlanePointl As Point3d,
PlanePoint2 As Point3d, PlanePoint3 As Po i nt3d)
Property ModelReference As ModelReference
(read-only)
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 =
msdDrawingModeNormal])

236

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
Double)
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 }

ElementEnumerator
~ 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()
ElementScanCriteria
~ 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


~
~
~
~
~
~

~
~
~
~

~
~
~

~
~
~

~
~

~
~
~
~

~
~

~
~
~
~

237

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()

level
~

Sub AddUserAttributeData(Attr i buteID As Long,


AttributeData As DataBlock)

238

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
{read-only)
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

ModelReference
~ Sub Activate()
~ Sub AddElement(Element As Element)

IThe MicroStation Object Modell


~
~

~
~

~
~
~
~

~
~
~
~

~
~
~
~
~

~
~
~

~
~

239

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
{read-only}
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
{read-only}
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
Element
Function GetE l eme ntByID64(Element I D64 As
Empty) As El ement
Func t ion Ge t LastValidGraph i calElement() As
Element
Function GetName dGroup(GroupName As String) As
NamedGroupElement
Function Ge t SelectedE le ments() As
ElementEnumerator

240

I Chapter 11: The MicroStation Object Model- Objects I


~

~
~
~
~
~
~

~
~

~
~

~
~
~

~
~
~
~
~

~
~
~
~
~

Function GetSheetDefinition() As
SheetDefinition
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
{read-only}
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
ModelReference
{read-only}
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
~
~
~
~

~
~
~
~
~
~
~
~

241

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

REVI EW
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.

242

I Chapter 11: The MicroStation Object Model- Objects I

12

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:

MSDDESIGNFILEFoRMAT
msdDesign FileFormatCurrent
msdDesignFileFormatDWG = 3
msdDesignFileFormatDXF = 4
msdDesignFileFormatUnknown
msdDesignFileFormatV7
1
msdDesignFileFormatV8
2

-1

The enumeration name is "MsdDesignFileFormat". It has six members


with values ranging from -1 to 4. Each member in an enumeration has a
member
name
and
a
value.
The
enumeration
"msdDesignFileFormatCurrent" has a value of O. As we saw in the
previous chapter, some properties and methods make use of these
enumerations. For example,

243

244

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
name:
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

245

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.

THE ENUMERATION LIST


MsdACSType
msdACSTypeCyl i nd r ical
2
msdACS TypeNone = 0
msdACSTypeRectangu l ar
1
msdACSTypeSpherica l = 3
MsdAddAttachmentFlags
msdAddAttachmentElementsVisible = 4
msdAddAttachmentFlagCoincidentWorld
msdAddAttachmentFlagNone = 0
msdAddAttachmentFlagTrueScale
1
MsdAngleAccuracy
msdAngleAccuracyO
msdAngleAccuracyl
msdAngleAccuracy2
msdAngleAccurac y3
msdAngleAccuracy4
msdAng l eAccuracy5
msdAngleAccuracy6
msdAngleAccuracy7
msdAngleAccuracy8

0
1

2
3
4
5
6
7

MsdAngleFormat
msdFormatDD_DDDD = 0
msdFormatDD MM SS = 1
msdFormatGradians = 2
msdFormatRadians = 3

246

I Chapter 12: The MicroStation Object Model - Enums I


MsdAngleMode
msdAngleModeAzimuth = 1
msdAngleModeBearing = 2
msdAngleModeConventional
MsdAttachMode
msdAttachNone = 1
msdAttachReference

MsdBsplineCurveOffsetCuspType
msdBsp l ineCurveOffsetCuspArc = 4
msdBsplineCurveOffsetCuspChamfer = 1
msdB s pl i neC urveO f fsetCus pJ ump = 0
msdBsp li neCurveOffsetCuspParabo la = 3
msdBsplineCurveOffsetCuspPoint = 2
MsdBsplineCurveType
msdBsplineCurveCircle = 3
msdBsplineCurveC i rcu l arArc
2
msdBsplineCurveEllipse = 5
msdBsplineCurveEllipticalArc
4
msdBsplineCurveGeneral = 0
msdBsplineCurveHyperbolicArc = 7
msdB s pline CurveLin e = 1
msdBsplineCurveParabolicArc = 6
MsdBsplineParametrizationType
msdBsp l ineParame t r i zat i onCentripeta l
2
msdBsplineParametrizatio nChordLength
1
msdBsplineParametrizationInher i ted
3
msdBsplineParametrizatio nUniform
0
MsdBsplineSurfaceDirection
msdBsp l ineSurfaceU
0
msdBsplineSurfaceV = 1
Msd BsplineSurfaceType
msdBsplineSurfaceCone = 3
msdBsplineSurfaceGeneral = 0

I The Enumeration List I

247

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
MsdCadlnputType
msdCadInputTypeAny = 5
msdCadInputTypeCommand
1
msdCadInputTypeDataPoint
3
msdCadInputTypeKeyin = 4
msdCadInputTypeReset = 2
msdCadInputTypeUnassignedCB

MsdCeliType
msdCellTypeGraphic
0
msdCe l lTypeMenu = 1
msdCel l TypePoint = 7
MsdChangePropagation
msdChangePropagationA l ways = 2
msdChangePropagationGroupL ock
0
msdChangePropagationNever
1
MsdChangeTrackAction
msdChangeTrackActionAdd
2
msdCha ngeTrackActionAppData = 8
msdChangeTrackActionDelete
1
msdChangeTrackAct i onDrop = 6
msdChangeTrackActionMark = 7
msdChangeTrackActionModelAdd = 9
10
msdChangeTrackActionModelDelete
msdChangeTrackActionModify = 3
msdChangeTrackActionModifyFence = 5
msdChangeTrackActionNewF il ePositionAndMod ify

248

I Chapter 12: The MicroStation Object Model - Enums I


MsdCommandResult
msdCommandResult3dL i brary2dF il e
50
msdCommandResult3dOnly = 39
msdCo mmandResultAcceptOuery
68
msdCo mm andResultBadCellName
47
msd Comm an dRe s ult Ce ll Del et ed
59
msdCom ma ndRe s ult CellE xi sts = 55
msdCo mmandResultCe ll Li braryNot Found
12
msdCommandResultCellNestingError = 43
msdCommandResu l tCe llN ot Found = 44
msdCommandResultElementNotFound = 21
msdCommandResultEmptyFence = 27
msdCommandResultFileReadOnly = 287
msdCommandResultIllegalDefinition = 23
msdCommandResultlnvalidReferenceOperation
msdCommandResultNeedCharacters = 27
msdCommandResultNoActiveCell = 19
msdCommandResultNoCellLibrary
54
msdCommandResultNoFenceActive = 15
msdCommandResultNoOrigin = 56
msdCommandResultOffDesignPlane = 22
msdCommandResultReferenceNotFound = 7
ms dCommandResultSuccess = 0
msdCommandResultUnknownCommand = 16
msdCommandResultViewNotFound
18
MsdConversionMode
msdConversionModeAlways = 1
msdConversionModeNever = 0
msdConversionModePrompt = 2
MsdCoordinateAccuracy
msdAccuracyO = 1
msdAccuracyl = 2
msdAccuracy16th = 56
msdAccuracy2
3
msdAccuracy3 = 4

481

I The Enumeration List I

249

msdAccuracy32nd = 120
msdAccuracy4 = 5
msdAccuracy4th
8
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
MsdCoordinateFormat
msdMasterUnits = 1
msdSubUnits = 0
msdWorkingUnits = 2
MsdCopyContextLevelOption
msdCopyContextLevelAlreadyRemapped = 4
msdCopyContextLevelByUserPreference = 0
msdCopyContextLevelCopyAlways = 3
msdCopyContextLevelCopylfD i fferent = 2
msdCopyContextLevelCopylfNotFound = 1
MsdCopyViewPort

msdCopyViewPortApplyAspectRatio = 2
msdCopyV iewPortApp lySize = 3
msdCopyViewPortApplySizeAndPosition
msdCopyViewPortKeepCurrent = 0

MsdDatabaseLinkage
msdDatabaseLinkagelnformix = 1
msdDatabase Linkagelngres = 32
msdDatabaseLinkageOdbc = 128
msdDatabaseLinkageOleDb = 256
msdDatabaseL i nkageOracle = 8
msdDatabaseLinkageXBase = 4
MsdDataEntryRegionJustification
msdDataEntryRegionJustificat i onCenter = 0
msdDataEntryRegionJustificationLeft = -1
msdDataEntryRegionJusti f icationRight = 1

250

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
1
2
msdD es i gn Fi1 e Forma t V8

-1

MsdDevelopableElementOutputType
msdDeve1opab1eCones = 4
msdDeve1opab1eConesP1anar = 5
msdDeve1opab1eRu 1eLines = 0
msdDeve1opab1eRu 1eLine s P1anar
1
msdDeve1opab1eShapes = 2
msdDeve1opab1eShap esPlanar
3
Msd Dialog BoxResult
msdD i alogBoxResu1tApply = 1
msdD i a1ogBoxResu1tCance1 = 4
msdD i a1ogBoxResu1tDefau 1t = 5
msdO i alogBoxResu 1tHe l p
10
msdD i a1ogBoxResu l tNo = 7
msdOia1ogBoxResultOK = 3
msdDialogBoxResu1tReset = 2
msdDia1ogBoxResultRetry = 8
msdDia1ogBoxResu1tStop = 9
msdDia1ogBoxResu1tYes = 6
msdDi a 1ogBoxResu1 tYesToA 11
11
MsdDimAccuracy
msdDimAccuracyO = 0
msdDimAccuracy1 = 129
msdDimAccuracy16th = 8
msdDimAccuracy2 = 130
msdDimAccuracy3 = 132
msdDimAccuracy32nd = 16
msdDimAccuracy4 = 136
msdDimAccuracy4th = 2

IThe Enumeration List I

251

msdDimAccuracy5 = 144
msdDimAccuracy6 = 160
msdD i mAccuracy64th = 32
ms dDim Accur acy7 = 192
msdDimAccuracy8 = 128
msdDimAccuracy8th = 4
msdDimAccuracyHalf
1
msdDimAccuracyScil
64
msdDimAccuracySci2
65
msdDimAccuracySci3
66
msdDimAccuracySci4
67
msdDimAccuracySci5
68
ms dDim Acc uracySc i 6
69
msdDim Accura cySci7
70
msdDim Acc ur acySc i 8
71
MsdOimAlignment
msdDim Alig nme nt Ar bitr a ry
3
msdD i mAl i gnm ent Dr awin g = 1
msdDi mA lignm ent Tru e
2
msdDimAlignmentView = 0
MsdOimAlternateThresholdComparison
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
MsdDimAngleMeasureArcLength

MsdOimBaliAndChainAlignment
msdDimBallAndChainAlignmentAuto
msdDimBallAndChainAlignmentLeft
msdDimBallAndChainAlignmentRight

0
1
= 2

252

I Chapter 12: The MicroStation Object Model - Enums I


MsdDimBallAndChainChainType
msdD i mBal l AndC hai nC hain TypeA r c = 2
msdDimBallAndChainChainTypeBSpline
msdDimBallAndChainChainTypeLine
1
0
msdDimBallAndChainChainTypeNone

MsdDimCustomSymbol
msdDimCustomSymbolCharacter
1
msdDimCusto mSym bolDefault
0
MsdDimDMSPrecisionMode
MsdDimDMSPrecisionModeFixed = 0
Msd Di mDM SPrecisionModeFloa t ing

MsdDimLabelLineFormat
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
6
Ms dDi mLabe lLin eFor mat Lengt hAngl eBe l ow
7
MsdD im Labe lLin eFor mat LengthBelow = 4
MsdD i mLabe lLi neFor matS t andard = 0
MsdDimMLNoteFrameType
msdDimM LNoteFrameTypeBox = 2
msdDimMLNoteFrameTypeLine
1
msdDimM LNoteFrameTypeNone = 0
MsdDimMLNoteJustification
msdDimMLNoteJustificationCenter = 3
msdDimMLNoteJustificationDynamic = 2
msdDimMLNoteJustificationLeft = 0
msdDimMLNoteJustificationRight = 1
MsdDimNoteHorizontalAttachment
msdDimNoteHorizontalAttachmentAuto
msdDimNoteHorizontalAttachmentLeft

0
1

IThe Enumeration List I


msdDimNoteHor iz ontalAttachmentRight

253

MsdDimNoteLeaderType
MsdDimNoteLeaderTypeCurve = 1
MsdDimNoteLeaderTypeLine = 0
MsdDimNoteTextRotation
msdDimNoteTextRotationHorizontal
0
msdDimNoteTextRotationlnline = 2
msdDimNoteTextRotationVertical
1
MsdDimNoteVerticalAttachment
msdDimNoteVerticalAttachmentBottom = 4
msdD i mNoteVert i calAttachmentBottom Lin e = 3
msdDimNoteVerticalAttachmentDynamicCorner
6
msdDimNoteVerticalAttachmentDynamic Line = 5
msdDimNoteVerticalAttachmentMidd le = 2
msdDimNoteVerticalAttachmentTop = 0
msd DimNoteVerticalAttachmentTopLine = 1
msdDimNoteVerticalAttachmentUnderline
7
MsdDimNoteVerticalJustification
msdDimNoteVerticalJustificationBottom
msdDimNoteVerticalJustificationCenter
msdD im NoteVert i calJust i f i cat ionDynam i c
msdDimNoteVerticalJustification Top = 0

2
1
= 3

MsdDimPlacementTextPosition
msdDimPlacementTextPosit i onAuto = 2
msdDimPlacementTextPositionManual = 0
msdD imPl acementTextPositionSemiAuto
1
MsdDimRadialMode
msdDimRadialModeCenterMark = 0
msdDimRadialModeDiame te r = 3
msdDimRadialModeDiameterExtended
4
msdD i mRadialModeRadius = 1
msdDimRadialModeRadiusExtended
2

254

I Chapter 12: The MicroStation Object Model - Enums I


MsdDimStackedFractionAlignment
Ms dDi mSt ac kedFractionAlignmentBottom
MsdOimStackedFractionAlignmentCenter
MsdO i mStacked FractionAl i gnment Top = 0

2
1

MsdDimStackedFractionType
MsdOimStackedFractionTypeOiagon al
2
Msd Oim St ac kedFra ct i on Type Fr omF ont
0
MsdOimStackedFractionTypeHorizontal
1
MsdDimStyleProp
msdO im Sty l ePropBallAndChai nAl ignment = 101
msdOimStylePropBallAndChainChainTerminator
102
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
msdOimStylePropGeneralCenterMarkSize
302
msdDimStylePropGeneralColor = 303
msdOimStylePropGeneralOimensionScale = 304
msdOimSty l ePropGeneralOimStyleOescription
305
msdOimStylePropGeneralOimStyleName = 306
msdO i mStylePropGeneralFont = 30 7

IThe Enumeration List I

255

ms dDimSt yle Pr op Gene r alI gnoreLeve lS ym bolog y


308
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
msdDimStylePropPlacementUseReferenceScale
505
msdDimSty l ePro pSymbolDiameterChar
601
msdDimSty l ePropSymbo l DiameterFont
602
msdDimStylePropSymbo l Di ameterType
603
msdDimStylePropSymbolLowerPrefixChar
604
msdDimStylePropSymbolLowerSuffixChar
605

256

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
msdDimStylePropSymbolPlusMinusChar
608
msdDimStylePropSymbolPlusMinusType = 609
msdDimStylePropSymbolPrefix = 610
ms dDimStylePr opSy mbolP ref ix Ce l lNa me = 611
msdDimStylePropSymbolPrefixChar
612
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
617
msdDimStylePropSymbolSuffixFont = 618
msdDimStylePropSymbolSuffixType = 619
msdDimStylePropSymbolTolPrefixChar = 620
msdDimStylePropSymbolTolSuffixChar = 621
msdDimStylePropSymbolUpperPrefixChar
622
msdDimStylePropSymbolUpperSuffixChar = 623
msdDimStylePropTerminatorArrowCellName = 701
msdDimStylePropTerminatorArrowChar
702
msdDimStylePropTerminatorArrowFont
703
msdDimStylePropTerminatorArrowhead
729
msd Dim Sty l e Pr opT e rm i nator Arr owType
704
msdDimStylePropTerminatorColor = 705
msdDimStylePropTerminatorDotCellName
706
msd Di mSty l e PropTe r mi natorDo t Char
707
msdDimStylePropTerminatorDotFont
708
msdDimStylePropTerminatorDotType
709
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
msdDimStylePropTerminatorNoLineThruArrow
717

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
719
msdDimStylePropTerminatorNoLineThruStroke = 720
msdDimStylePropTerminatorNote = 736
msdDimStylePropTerminatorNoteCellName = 738
msdDimStyleProp TerminatorNoteChar
739
msdDimStylePropTerminatorNoteFont = 740
msdDimStylePropTerminatorNoteType = 737
msdDimStylePropTerminatorOriginCellName
721
msdDimSty l eP r opTerm i natorOrig i nC har
722
msdD i mSty l ePropTerm i natorOr i ginFont
723
msdDimStylePropTerm i natorOriginType
724
msdDimStylePropTerminatorOverrideColor = 725
msdDimStylePropTerminatorOverrideLineStyle = 726
msdDimStylePropTerminatorOverrideWeight
72 7
msdDimStylePropTerminatorRight = 728
msdDimStylePropTerminatorStrokeCellName
730
msdDimStyleProp TerminatorStrokeChar
731
msdDimStylePropTermina t orStrokeFont
732
msdDimSty l eProp TerminatorStrokeType
733
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

257

258

I Chapter 12: The MicroStation Object Model - Enums I


msdDimStylePropTextOmitLeadingDelimiter
815
msdDimSt yle PropTextOverrideColor = 816
msdDimStylePropTextOverrideHeight = 817
msdDimStylePropTextOverrideStackedFractions
833
msdDimStylePropTextOverrideUnderline = 834
msdDimStylePropTextOverrideWeight = 818
msdDimStylePropTextOverrideWidth = 819
msdD imSt yl ePropTextSec Le ad ingZ ero = 820
msdDimStylePropTextShowSecondary = 821
msdDimStylePropTextStackedFractionAlignment
829
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
msdDimStylePropToleranceAccuracy
910
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

259

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

260

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
1054
MsdDimSuperscriptMode
MsdD i mSupe r Scr i ptModeFrom Font = 0
MsdDimSup e rScr i pt ModeGenerated = 1
MsdDimSymbolType
msdD i mSymbo lTypeCel l = 2
msdDimSymbolTypeCharacter
1
msdD im Symbo l Type Def aul t = 0
MsdDimTerminatorArrowhead
msdDimTerminatorArrowheadClosed
1
msdDimTerminatorArrowheadFilled
2
0
msdDimTerminatorArrowheadOpen
MsdDimTerminatorMode
msdDimTerminatorModeAuto = 0
msdDimTerm i natorModelnside = 2
msdDimTerminatorModeOutside = 3
msdDimTerminatorModeReversed = 1
MsdDimTerminatorType
msdDimTerminatorTypeArrow

IThe Enumeration List I

261

msdOimTerminatorTypeCirc l e
3
msdOimTerminator TypeOot = 4
msdOimTerminatorTypeNone = 0
msdOimTerminatorTypeNote = 5
msd OimT erminatorTypeOrig in
3
msdOimTe rm inatorTypeStroke
2
MsdDimTextField
msdOimTextFieldLowerLimit
msdOimTextFieldMain = 0
msdOimTextFieldMinus = 2
msdOimTextFieldPlus = 1
msdOimTextFieldUpperLimit

MsdDimTextFormat
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
MsdDimTextFrameType
MsdOimTextFrameTypeBox = 1
MsdOimTextFrameTypeCapsule
MsdOimTextFrameTypeNone = 0

MsdDimTexUustification
msdOimTextJustificationCenter = 2
msdOimTextJustificationLeft = 1
msdOimTextJustificationRight = 3
Msd Di mTextLocation
MsdOimTextLocationAbove = 1
MsdOimTextLocationInline = 0
MsdOimTextLocationOutside
2
MsdOimTextLocationTopLeft = 3

262

I Chapter 12: The MicroStation Object Model - Enums I


Msd Oi mTextOrientation
MsdDimTextOrientationA l igned = 0
MsdD i mTextOrientationHorizontal
MsdOimThousandsOpts
MsdDimThousandsOptsComma = 2
MsdDimThousandsOptsNone = 0
MsdDimThousandsOptsSpace = 1
MsdOimToleranceType
MsdDimToleranceTypeLimit = 1
MsdDimToleranceTypePlusMinus
MsdOimType
msd DimTypeA ng leAx i s = 10
msdD imTypeAngleAx i sX
50
msdDimTypeAngleAxisY = 51
msdDimTypeAngleLines = 9
msdDimTypeAngleLocation
7
msdDimTypeAngleSize = 5
msdDimTypeArcLocation
8
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
msdDimTypeRadiusExtended
17
msdDimTypeSizeArrow = 1

IThe Enumeration List I

263

msdDimTypeSizeStroke = 2
msdDimTypeUseActive = -1
MsdDimValueAngleFormat
msdDimValueAngleFormatCentesimal = 2
msdDimValueAngleFormatDegMinSec
1
msdD im ValueA ngleF orma tDe grees
0
msdDi mVal ueA ngl e For matRadi ans
3
MsdDimValueAnglePrecision
msdDimValueAnglePrecision1Place
1
msdDimVa l ueAnglePrecision2Place
2
msdDimValueAnglePrecision3Place
3
msdDimValueAnglePrecision4Place
4
msdDimVa l ueAnglePrecision5Place
5
msdDimValueAnglePrecision6Place
6
msdDimValueAnglePrecisionWhole = 0
MsdDimVerticalTextOptions
MsdDimVerticalTextOptionsAlways = 1
MsdDimVerticalTextOptionsNever
0
MsdDimVerticalTextOptionsNoFit = 2
MsdDrawingMode
msdDrawingModeErase = 1
msdDrawing ModeHi lit e = 2
msdD r awingModeNormal = 0
msdDrawingModeTemporary = 3
msdDrawingModeTemporaryErase
msdD r awingModeXor = 6

MsdElementCachePurpose
msdElementCachePurposeControl
2
msdElementCachePurposeGraph i cal
4
msdElementCachePurposeNonModel = 1
MsdElementClass
msdElementClassConstruction

264

I Chapter 12: The MicroStation Object Model - Enums I


ms dEl ement Cl assCon tru cti onRul e
6
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
MsdElementSubtype
msdElementSubtypeApplicationElement = 20
ms dEl ement SubtypeA uxil iaryCoo r dinateSyste m
msdElementS ubtypeNone = - 1
msd ElementSubtype UpdateSeque nceE l ement
33
Msd ElementType
msdElementType44 = 44
msdE l ementTypeArc = 16
msdElementTypeBsplineBoundary
25
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
msdElementTypeGroupData
5
msdElementTypeLevelMask = 99

IThe Enumeration List I


msdE l ementTy pe LevelSymbo l ogy
10
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
msdElementTypeRasterReferenceComponent
msdElementTypeReferenceAt tac hment = 100
msdElementTypeReferenceOverride
108
msdElementTypeShape = 6
msdElementTypeS ha redCe l l = 35
msdElementTypeSharedCellDefinition
34
msdElementTypeSolid = 19
msdE l ementTypeSurface = 18
msdElementTypeTable = 96
msd ElementTypeTableEntry
95
msdElementTypeTag = 37
msdE l ementTypeText = 17
msdElementTypeTextNode = 7
msdElementTypeView = 98
msdElementTypeViewGroup = 97
MsdError
msdAccuDrawNotEnabled = -2147218287
msdError3dReference2dMaster = -2147220767
msdErrorAccessVio la t i on = -2147218313

265

91

266

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
msdErrorCannotOpenFile
-2147218391
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

267

268

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
-2147218392
msd ErrorNameNotU nique
-2147218343
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
msdErrorNotLoaded
-2147218300
msdErrorNotLocked
-2 147218293
msdErrorNotMember
- 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

269

270

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
-2147220095
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

271

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
msdErrorWritelnhibited
-2147218394
msdErrorWrongElementID = -2 147218319
MsdFileAccessMode
msdFileAccessMo deRead = 1
msdFileAccessModeReadWr i te

MsdFiIIMode
msdFil lMod eFi lled = 1
msdF illModeNot Fi lled = 0
msdFi l lModeOutl in ed = 2
msdFillModeU s eActive = -1
MsdFontType
msdFontTypeMicroStation
0
msd Fo nt TypeS HX = 1
msdFontTypeUnknown = 3
msdFont TypeWindow sTru eType

MsdGeoReferenceSisterFileType
msdGeoReferenceS i sterFileTypeHgr = 1
msdGeoReferenceSisterFileTypeNone = 0
msdGeoReferenceS i ster FileTypeTwf = 2
MsdGloba lLineStyleScale
msdGloba lL ineStyleScaleBoth
3
msdGloba l LineStyleScaleMaster
0
msdGloba l LineStyleScaleNone = 1
msdG l obalL i neSty l eScaleReference
MsdLevelChangeType
msdLevelChangeAfterChangeActive

2
9

272

I Chapter 12: The MicroStation Object Model - En ums I


msd LevelChangeA f terCreate = 2
msdLeve lChange Aft er Delet e = 3
msdLevelChangeBeforeChangeActive
msdLevelChangeBeforeDelete = 18
msdLevelChangeChangeAttribute
8
ms dLevelChangeChangeCode = 5
msdLevelChangeChangeDisplay = 7
msdLevelChangeChangeName = 4
msdLevelChangeChangeParent = 6
msdLevelChangeTableRedo
15
msdLevelChangeTableUndo = 14
MsdLevelElementAccess
msdLevelElementAccessAll = 0
msdLevelEl ementAccess Loc ked = 1
msdLevelElementAccessReadOnly
2
msdLevelElementAccessViewOnly
3
MsdLimits
msdLimitsMaxVertices
msdLimitsMaxVi ews = 8

5000

MsdMeasurementBase
msdMeasurementBaseDegree = 2
msdMeasurementBaseMeter = 1
msdMeasurementBaseNone = 0
MsdMeasurementSystem
msdMeasurementSystemEnglish = 1
msdMeasurementSystemMetric = 2
msdMeasurementSystemUndefined
0
MsdMem berTraverse Type
msdMemberTraverseCopy = 2
msdMemberTraverseDirectMembers
msdMemberTraverseEnumerate = 3
msdMemberTraverseManipulate = 1
msdMember TraverseSimple = 0

17

I The Enumeration List I

273

MsdMessageCenterPriority
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
11
MsdModelChangeType
md lM ode l ChangeActive = 5
11
mdlModelChangeBeforeAct i ve
mdlModelChangeBeforeCreate
15
md l Mode l ChangeBeforeDelete
6
md l Mode l ChangeBeforeName = 12
mdlModelChangeBeforeProperties = 14
mdlModelChangeBeforeSettings
13
mdlModelChangeBeforeUnCreate
9
mdlModelChangeBeforeUnDelete
16
mdlModelChangeCreate = 1
mdlModelChangeDelete = 2
mdlModelChangeName = 10
mdlModelChangePropagateAnnotationScale
mdlModelChangeProperties
3
mdlModelChangeSettings
4
md lM odelC hange Un Crea t e
7
mdlModelChangeU nDelete
8
MsdModelType
msdMode lTypeDefa ul t = -1
msdModelTypeExtraction
2
msd Mode lTypeNormal = 0
msdModelTypeSheet = 1
MsdN estOverrides
msdNestOverridesA l ways = 1
msdNestOverridesAsRequired
msdNestOverridesNever = 2

17

274

I Chapter 12: The MicroStation Object Model - Enums I


Msd NewLevel Display
msdNewLevelDisplayAlways = 1
msdNewLevelDisplayFromConfig
msdNewLevelD i sp la yNever = 2

Msd RasterBlockType
msdRasterBlockTyp e lmage = 4
msdRast e rBl ockTypeLin e = 1
msdRasterBlockTypeStr i p = 3
msdRasterBlockTypeTile = 2
MsdRasterDisplayOrder(ommand
msdRasterDisplayOrderCommandBackward = 3
msdRasterDisplayOrderCommandForward = 2
msdRasterDisplayOrderCommandToBack = 1
msdRasterDisplayOrderCommandToFront = 0
MsdRasterDisplayPriorityPlane
msdRasterD isp layPriorityPlaneBack = 1
msdRasterDisp l ayPriorityPlaneFront = 3
msdRasterDisplayPriorityPlaneVector = 2
MsdRasterModificationType
msdRasterModificationType_ClipBoundary = 5
msdRasterModificationType_ClipMask = 4
msdRasterModificationType_Extendedlnformation = 0
msdRasterModificationType_GeoReferenceInformation = 1
msdRasterModificationType_Raster ln formation = 3
msdRasterModificationType_Re l oad = 6
msdRasterModificationType_Renderinglnformation
2
Msd RasterWorld File
msdRasterWorldFileHgr = 1
msdRasterWorldFileNone = 0
msdRasterWor l dFi l eWorldFile
MsdReferenceSystem
msdReferenceSystemDgn

IThe En umeration List I


msdReferenceSys t emRaster

275

MsdRenderingMode
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
9
msdRenderingModeSmoothShade
6
msdRender i ngModeSo li dFil l = 4
msdRenderingModeWireFrame = 0
msdRenderingModeWireMesh = 2
MsdStandardsCheckerReplaceChoice
msdStandard s CheckerReplaceCh oiceAbo rt = 4
msd Stan dard s Ch eckerReplaceChoiceFi x = 1
msdStandardsCheckerRepla ceChoiceMarkIgn ored = 2
msdStandardsCheckerReplaceChoiceMarkNotIgnored
msdStandardsCheckerRep l ac eChoiceSkip = 0
MsdStandardsCheckerReplaceOptions
msdStandardsCheckerReplaceOptionCanFix = 2
msdStandardsCheckerReplaceOptionCanIgnore
MsdStatusBarArea
msdStatusBarAreaLeft
16
msdStatusBarAreaMiddle = 15
MsdTagType
msdTagTypeBinary = 5
msdTagTypeCharacter = 1
msdTagTypeDouble = 4
msdTagTypeLonglnteger
3
msdTagTypeShortlnteger = 2

276

I Chapter 12: The MicroStation Object Model - Enums I


MsdTangentElementOutputType
msdTangen t Arcs = 1
msdTangentCircles = 0
msdTange ntTr i angles
2
MsdTangentlnterpolationType
msdTangentFromCircleFit = 1
msdTangent Fr om Cu bi c Fit = 2
msd TangentFromCurve = 0
MsdTextDirection
msdTextDirect ionHori zontal = 0
msdTextDirectionRightToLeft = 8
msdTextDirectionVertical = 4
msdTextDirectionVerticalMultiLineRightToLeft

MsdTextJustification
msdTextJustificationCenterBottom
8
msdTextJustificationCenterCenter
7
msdTextJustificationCenterTop = 6
msdTextJustificationLeftBottom = 2
msdTextJustificationLeftCenter = 1
msdTextJustificationLeftTop = 0
msdTextJustificationRightBottom = 14
msdTextJustificationRightCenter = 13
msdTextJustificationRightTop = 12
MsdTextNodelineSpacingType
msdTextNodeLineSpacingTypeAtLeast
3
msdTextNodeLineSpacingTypeAutomatic
1
msdTextNodeLineSpacingTypeExact = 0
msdTextNodeLineSpacingTy peExactFr omLineTop
MsdV7 Action
msdV7ActionAskUser = 0
msdV7ActionUpgradeToV8
msdV7ActionWorkmode = 3

I Review I

277

MsdViews
msd Vi ew1
1
msdView2
2
msdView3
4
msdView4
8
msdView5
16
msdView6
32
msdView7
64
msdView8
128
ms dView All = 25 5
msdV i ew None = 0
MsdXDatumType
msd XDatumTypeBinaryData = 1004
msdXDatumTypeControlString = 100 2
msdX Datu mTypeData ba se Ha nd l e = 1005
msdX DatumTypeD i stance = 1041
msdXDatumTypelnt16
1070
msdXDatumTypelnt32
10 71
msdXDatumTypeLevel
1003
msdXDatumTypePoint
1010
msdXDatumTypeReal = 1040
msdXDatumTypeScaleFactor
1042
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

REVIEW
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.

278

I Chapter 12: The MicroStation Object Model - Enums I

13

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

279

280

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

281

282

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

283

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.

REVI EW
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.

284

I Chapter 13: The MicroStation Object Model- Types I

14

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

285

286

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.

ONDESIGNFILEOPENED AND ONDESIGNFILECLOSED


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

287

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

Application

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).

288

I Chapter 14: The MicroStation Object Model - Events I


REVIEW
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.

15

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

GRAPHICAL ELEMENTS
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
method.

Lines
"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?

289

290

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
LineElement

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
0
EnPt.X = 0: EnPt.Y
0: EnPt . Z 0

I Graphical Elements I

291

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
LinePoints(O).X
0: LinePoints(O).Y
0
LinePoints(l) . X 4 : LinePoin ts(l).Y
0
LinePoints(2).X
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
0
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

292

I Chapter 15: Adding To Documents I


End I f
If UBound(PointElems) + 1 < 5 Th en
MsgBox "A mirimum of 2 X,

y,

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.
1

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

293

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
ParamArray.
7

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
Create3dLines
Create3d Li nes
Create3dLines
Create3dLines
End Sub

0,
0,
0,
0,
0,

0,
0,
4,
4,
4,

0,
0,
0,
0,

4 , 0 , 0 , 4 , 4, 0 , 0 , 4, 0 , 0, 0,
4, 4 ,
4, 0,
4,

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.

294

I Chapter 15: Adding To Documents I

,
OK
Cancel

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,
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

295

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
1
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.

296

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
0
ShapePoints(l).X = 2: ShapePoints(l) . Y 0
ShapePoints(2).X = 1: ShapePoints(2).Y
1
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)
Next
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

297

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

298

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, _
rotMatrix)
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

299

Set i nputMessage = _
inputOueue . GetInput(msdCadInput TypeDa taPoint , _
msdCadInputTypeAny)
Do
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
Loop
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, _
msdCadlnputTypeAny)
Do
Select Case inputMessage.InputType
Case msdCadInputTypeDataPoint
StPoint = inputMessage . point
Exit Do
Case msdCadlnputTypeReset
Exit Sub
End Select
Loop

300

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)

Do
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
Loop
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
/ 2
/

CPoint, _
rotMatrix)

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 , _
rotMatrix)
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

301

Dim MinorAl As Point3d


Dim myEllipse As EllipseElement
MajorAl.X
1: MajorAl.Y
1
MajorA2 . X = 5: MajorA2.Y = 5
MinorAl.X = 3: MinorAl . V = 2
Set myEllipse = CreateEllipseE"ementl(Nothing. MajorAl. MajorA2. _
tvIirorAl)
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. _
rotMatrix)
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
ArcElement

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
ArcElement

302

I Chapter 15: Add ing To Documents I


4

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
ArcElement

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

303

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

304

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

Next
End Sub

~Note

1.:

iN. ote
.2:
.
. I
iNote 3:
I
4 :
N. ote ,:
INoteS:
!
-----------'-6'-- -- - ------+-. ----ote
.i
,

" .

,
,

. . .. ..... t

"N-'---! .

..

'Note 7:::

I!

-"'-.-:-------------8'~

'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

305

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
CellElement

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,

306

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) )

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

307

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

0))

Set CellElements(6) = CreateEllipseE emert2(Nothi n g, _


OriginPoint, 1 . 25, 1.25, rotMatrix)
Set myCell = CreateCellElementl( "Box3 " ,
OriginPoint)
ActiveModelReference . AddE l e~ent

Cel~Elements,

myCell

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.
Ble

o !Jse Shared Cells


Name

Description

:.

:.

o Q.isplay All Cells In Path


, Type

Annotation

, Where

Active Cells
EJacement
Terminator

I 80x3
I NONE

Point
Pgttern

I Element
I NONE

CREATING NEW DOCUMENTS


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?
1

Function CreateDesignFile(SeedFileName As String,


NewDesignFileName As String, Open As Boolean) As DesignFile

Sub CopyDesignFile(ExistingDesignFileName As String,


NewDesignFileName As String, [Overwrite As Boolean])

308

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 )
Next
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) .

Name

h':)fll; l :dgn

til file2, dgn


l:il file3, dgn
til f ile4 ,dgn
l:il fileS,dgn
~file6,dgn
~file7,dgn

309

Size

Bentley ~licroS t ati o n Design File

34 KB

Bentley ~licroSta tion Design File

34KB

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

<'

Type

34 KB

'-',,_-'.L" '"'"'"'=c.:;C"~C'.C-'==.

>

v
-

SECURITY ISSUES WITH CREATING DATA


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.

REVIEW
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.

310

I Chapter 15: Adding To Documents I

16

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:
[B

The Basics of Searching Files

[B

Using ScanCriteria

[B

Multiple Combinations of Criteria

[B

Reviewing Three Collection Methods

[B

Scan Criteria Methods

THE BASICS OF SEARCHING FILES


Let's begin by examining each element found in a file.
Sub

TestScanAllA ()
Dim myElement As Element
Dim myEnum As ElementEnumerator

311

312

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
Wend
End Sub

This procedure prints the type


property value of each element in the
active model to the Immediate
Window.

97

66

Running the procedure TestScanA 11 A


results in a list of numbers telling us
the type of element found. This
number
references
the
msdElementType enumeration.

96
96
96

96
66
66
6
4

."' .!

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
25
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
msdElementTypeLevelSymbology
10
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
msdElementTypeRasterReferenceComponent
msdElementTypeReferenceAttachment = 100
msdElement Ty peReferenceOverride = 108
msdElementTypeShape = 6
msdElementTypeSharedCel l = 35
msdElementTypeSharedCellDefinit io n
34
msdElementTypeSolid = 19
msdElementTypeSu r face = 18
msdEleme ntTypeTab l e = 96
msdElementTypeTableEntry
95
msdElementTy peT ag = 37
msdElementTypeText = 17
msdElementTypeTe xtNode = 7
msdEleme ntTyp eView = 98
msdElementTypeViewGroup = 97

313

91

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:

314

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
Wend
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

315

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
@I URL
@' URLTitie
@j' Vertex

SetXData

@' Type
@ URL
~@' 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
properties.
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"
area.
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
Wend
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?

316

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)
myText.Rewrite
End Select
Wend
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
file.

USING SCANCRITERIA
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

317

Set myE num = ActiveModelReference.Scan(myFilter)


While myEnum . MoveNext
ElementCounter = ElementCounter + 1
Wend
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
Wend
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

318

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
Wend
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.ExcludeAllLevels
myFilter.ExcludeAllColors
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
Wend
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

319

myColor = myCo l orTable.FindClosestColor(RGB(192 . 192 . 192))


myFilter . ExcludeAl lTypes
my Fi 1t e r . Ex c 1ude All Level s
myFilter.txcludeAllColors
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
Wend
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
myFilter.ExcludeAllLevels
my Fi 1t e r . Ex c 1 ude All Color s
myFilter .E xcludeAllL i neStyles
myFilter.ExcludeAllClasses
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
Wend
MsgBox ElementCounter & " elements found. "
End Sub

320

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
myFilter.ExcludeAllLevels
For Eac h myLevel In ActiveDes ig nF il e.Levels
Select Case UCase( myLeve l. Na me)
Case "SIDEWALK "
Case Else
myFilter.IncludeLevel myLevel
End Select
Next
Set myEnum = ActiveModelReference . Scan(myFilter)
While myEnum.MoveNext
ElementCounter = ElementC ounter + 1
Wend
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"
level.

I Multiple Combinations of Criteria I

321

MULTIPLE COMBINATIONS OF CRITERIA


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
Wend
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

322

I Chapter 16: Searching In Files I


Dim myFi1ter As New E1ementScanCriteria
Dim myCo11ection As New Collecti on
myFi1ter.Exc1udeA11Types
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
Wend
myFi1ter.Reset
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
Wend
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

323

Elementcache!ElemenlCac I

Adding a watch to
t he variable
myCo llection
shows somet hing
like th is:

Cache Index

18

Long

Class

msdElemenlClassPrimary

MsdElemenlClass

Color

Long

DateLastModified

#9120120055:26:39 PM#

Date

FilePosrtion

4000017

Long

GraphicGroup

Long

HasAnyTags

False

800lean

InDisplaySet

True

800lean

IsComponentElement False

800lean

IsFromAttachment

False

800lean

IsGraphical

True

800lean

IsHidden

False

800lean

IsLinear

False

800lean

Is Locked

Fals e

800lean

IsModified

True

8 00le an

IsNew

True

8 00lean

IsSnappable

True

800lean

IsValid

True

Level
Linestyle
LineWeight

800lean
Leveillevel
LinestylellineStyle

ModelReference

Long
ModelReference!ModelRel

Subtype

<Attempting to perform a non-! MsdElementSubtype

Type

msdElementTypeSharedCe l1

MsdElementType

I
I,

URL

string

URLTrtle

string
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
group.
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
Wend

324

I Chapter 16: Searching In Files I


myFilter.Reset
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
Wend
myGroup.Rewrite
MsgBox myGroup.MembersCount & " elements found."
End Sub

REVI EWING THREE COLLECTION METHODS


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

325

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.

SCAN CRITERIA METHODS


The code we have written in this chapter has used several methods of
the ElementScanCriteria Obj ect. Here is a comprehensive listing of the
methods:
Sub
Su b
Sub
Sub
Sub
Sub
Sub
Sub
Sub
Sub
Sub
Sub
Sub
Su b
Sub
Sub
Sub
Sub
Sub
Sub
Sub
Sub
Sub
Sub
Sub
Sub
Sub
Sub

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()
IncludeOnlyLocked()
Inc l udeOnlyModif i ed()
Inc l udeOnlyModifiedRange(Min As Date . [Max As Date])
Incl udeOnlyNew()
IncludeOnlyNonPlanar()
I ncl udeOn 1yNonSnappab 1e ( )
IncludeOnlyOld()
Incl udeOnlyPl anar()
IncludeOnlySnappable()

326

I Chapter 16: Searching In Files I


Sub
Sub
Sub
Sub
Sub
Sub
Sub
Sub
Sub

IncludeOnlySolid()
IncludeOnlyUn l ocked()
IncludeOnlyUnmodified()
IncludeOnlyUserAttribute(UserAttributeID As Long)
IncludeOnlyVisible()
IncludeOnlyWithinRange(Range As Range3d)
IncludeSubtype(Long As Long)
Inc l udeType(Type As Msd El ementType)
Reset()

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

327

REVIEW
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.

328

I Chapter 16: Searching In Files I

17

Interactive Modification
User interaction can be helpful when modifying files and elements in
VBA. When our programs are designed well, they are powerful and
flexible.

In this chapter:
[8

Giving users feedback and information

[8

Working with selection sets

[8

Getting user input

[8

Using the send command

[8

Employing modeless dialog boxes

[8

Applying some real-world applications

[8

Interacting with MDL applications

GIVING USERS FEEDBACK AND INFORMATION


When we are working with MicroStation in any capacity, three distinct
areas at the bottom of the MicroStation window give us information and
feedback.

329

330

I Chapter 17: Interactive Modification I


These areas are called the command, prompt, and status areas.
Command

Prompt

Place SmartLine > Enter first verte~

Status

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

ro'"

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

~dit

Element 2.ettings 10015 !,Ltilities wor!5,space

: ~ lroe;aU-lt ------.;JI 0
1D

~ Q i~ I ~ ~

ft II<)

331

IMndow tielp

. 1_

".

:::.:::

00

ItQ i ?

.1~

11J~ 1 ~:"Ij
,

;... : CJ

2} .. :.
o

]JJ',

J.

A!- .

5',
.I. ~

.GJ~ E-

7~

~-,

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

332

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.

Ire

No Eiements Found

WORKING WITH SELECTION SETS


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( _
"A-FURN-FREE")
Wend
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

333

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 " )

myElement.Rewrite
Wend
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
design.
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
Wend
End If
End Sub

TestS el ecti onS etC

Change Selection to Color O?

changes
all
selected
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.

334

I Chapter 17: Interactive Modification I


GETTING

~SER INPUT

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

335

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, _
msdCadlnputTypeReset)
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?

msdCadlnputTypeCommand
1
msdCadlnputTypeReset = 2
msdCadlnputTypeOataPoint 3
msdCadlnputTypeKeyin = 4
msdCadlnputTypeAny = 5
msdCadlnputTypeUnassignedCB 6

336

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
immediately.
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 &
myCIM.ScreenPoint.Z
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

337

This pro cedure captures ten inputs or captures until a reset is detected.
Point
Point
Command
Command
Command
Keyin
Point
Point
Command
Point

<,

11581.8836494914
27463.2962386063
0
1
9599.68844328587
28602.2371305697
0
1
PLACE SMART LINE
PLACE BLOCK ICON
CGPLACE CIRCLE ICON
bogus keyin
10796.1239475839
31518.0353275433
0
1
10664.7076908189
30833 . 5756568922
0
1
MOL KEYIN lv l mang~ levelmanage~ dialog open
11195.8483952442
310 1 9 . 7486873093
0
1

"-

377 579 0
196 475 0

168 238 0
144 363 0
241 329 0
v

'!'

The resu lts of runn ing this procedure with a variety of inputs.

Points
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
API.

Commands
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.

Keyin
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.

338

I Chapter 17: Interactive Modification I


Reset
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.

SOME REAL-WORLD ApPLICATIONS


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
CadlnputOueue
Set myCIM = myCIO.Getlnput (msdCadlnputTyp e DataPoi nt .
msdCadlnputTypeReset)
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.
msdCadlnputTypeReset)
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
myLine.Redraw
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

339

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 . _
msdCadlnputTypeReset)
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 . _
msdCadlnputTypeReset)
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
myLine.Redraw

340

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
CadInputOueue
Set myCIM = myCIO.GetInput(msdCadlnputTypeDataPoint,
msdCadInputTypeReset)
Select Case myCIM.InputType
Case msdCadInputTypeReset
Exit Sub
Case msdCadI nputTypeDataPoint
pt3Start = myCIM.point
End Select
Set myCIM = myCIO.GetInput(msdCadInputTypeDataPoint,
msdCadInputTypeReset)
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

341

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
points.
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. _
msdCadlnputTypeReset)
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 . _
msdCadInputTypeReset)
Select Case myCIM . InputType
Case msdCadInputTypeReset

342

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
CommandState.StartDefaultCommand
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
CommandState.StartDefaultCommand
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

343

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
CadlnputOueue.SendReset
CommandState.StartDefaultCommand
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
errhnd:
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

344

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 :

Start

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
CadInputOueue
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

345

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
errhnd:
CadlnputOueue.SendReset
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

346

I Chapter 17: Inte ractive Modification I


I

,__ __ __ ___ _ __ _ __ _ __ _ __ __ _ _ ____ __ _ _ . ....


~_

;t:o.~.~

- - -'-:t: - - - - - - - - - - - _.- - - - - - _.- - - - -- - - - - - - - - - - - .


! ~'-~r~~r~'~] (j'I '~'-"""" ']~r~] Cl !",.-".-... -.. -~~

II:
;~;-\
i
!1~,4\
!
:/o tl~~~_~]G100 0 : .~~~~=~' cR0i'J ICE ~ ._~ _._,_...J G1e
:

! ':

(i

I~

C .

NOTLEY,,

: . \r---,-

i~},~,.. .~~\. >..~~...~/..~..,\.~:::~,~:::.

;;
.'

II

\ TABOR,Co

11 0

:; , '

r==r'

i;
~< ,

\ 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

347

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 &
myCellHeader.Origin.Z
Wend
Close #FFile
Exit Sub
errhnd:
CadlnputQueue.SendReset
CommandState.StartDefaultCommand
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
MicroStation:

ole

!;.dit

pffi ce.
PART
GWALL
PART
URINAL
PART
URINAL
DPART
DPART
POOORR
RAIL2
RAIL1
.~

FQ.rmat

\lJew

tielp

dgn
16233.3074154912
16233.3074154912
16233.3074154912
16233.3074154912
16233.3074154912
16233.3074154912
14517.3074154912
14517.3074154912
14517.3074154912
16233.3074154912
16233.3074154912

17003.6804376836
16427.6804376836
17819.6804376836
17411. 6804376836
18635.6804376836
18227.6804376836
16991.6774376836
15851. 6804376836
15917.6804376836
16991. 6774376836
15651. 6804376836

o
o
o
o
o
o
o
o

o
o
o

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.

348

I Chapter 17: Interactive Mod ification I


USING SENOCOMMANO
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.
1

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
shown:

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.
4

Next, stop recording the macro by clicking the "Stop Record"


button.

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

349

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
CadInputOueue . SendCommand "CGPLACE LINE CONSTRAINED
Coordinates are in master units
startpoint . X 16735 . 231975
startpo i nt.Y
33020 . 733029
sta r tp oint.Z
0#
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
point.X
startPoint.X + 1985 . 401024
point . Y startpoint . Y 610 . 892623
point.Z
startPoint . Z
CadInputOue ue . SendDataPoint point. 1
Send a reset to the current co mmand
CadInputOueue.SendReset
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

350

I Chapter 17: Interactive Modification I


CadlnputOueue . SendReset
CommandState.StartDefaultCommand
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
(rectangle).
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
startPoint.Z
0#
Send a data point to the current command
point.X
startPoin t. X
point.Y
startPoint.Y
point.Z
startPoint.Z
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.X
0
point.Y = 0

I Using SendCommand I

351

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.
CommandState.StartDefaultCommand
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)

352

I Chapter 17: Interactive Modification I


Debug . Print myCIM . CommandKeyin
Next I
End Sub

TestCadlnputN captures ten


commands.
This
is
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
name.

CGPLACE LINE CONSTRAINED


PLACE BLOCK ICON
CGPLACE CIRCLE ICON
HATCH ICON
Attach Tags
WORDPROCESSOR PLACE TEXT ICON
PLACE CELL ICON
MEASURE DISTANCE ICON
DIMCREATE ELEMENT
PLACE FENCE ICON

One additional method of determining command names should be


mentioned.
L~~_~!:~~~ _________________.___________________________-'

digilizer
dimcreale
dimension
dims lyle
displaysel

ri!~
~I

BJ

palette
parlicielrace
plol
popsel

dialog openfile
dialog drawing scale open
dialog dr awingscale
delele
erase
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

353

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.

MODELESS DIALOG BOXES


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.

frmMatchProperties.frm
The
first
form,
"frmMatchProperties.
frm", looks like this:
r :source Element:~..,..-,--,-,--,-~ ~-:-:t !:-: De,stination Element(~): ~:-:-:- t

The
form
looks
::
Seled
i: I: Change Current Selection :
simple enough. We
r I
I: : 0 Element(s) modified: ,
'
:'Level
have a few command
buttons, a couple of
frames, a handful of
ILineweight r I
,': h-,:- .' .' , : ,: ,: ,: ,:::,J.
check boxes, a label,
Close
and four text boxes.
Before we look at the
code behind
the
controls, let's discuss the program's desired functionality.

:: ,. ~ :

ll

L'

:i:

:;

::

.:.:

'.,

354

I Chapter 17: Interactive Modification I


Desired Functionality
1

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
lineweight.

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:
[E

frmMatchProperties

[E

btnSelectSource

[E

chkLevel

[E

txtLevel

[E

chkColor

[E

txtColor

[E

chkLinestyle

[E

txtLinestyle

[E

chkLineweight

[E

txtLineweight

[E

btnChange

[E

IblCount

btnClose

[E

fraSource

[E

fraDestination

I Modeless Dialog Boxes I

355

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
affecting.

Control Properties
1

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

356

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:
1

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
nn

I Modeless Dialog Boxes I

357

txtCo lor.Text = myElements(O).Color


txtColor . BackCo lo r =
myColorTable .Ge tCo l orAtlndex(myElements(O).Color)
txtL'nestyle.Text =
myEle~ents(O).LineStyle.~ame

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 ."

358

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

359

Pri vate Su b btn Cl ose_Cl i c k()


Unl oad Me
End Sub

PROVIDING USER FEEDBACK AND INFORMATION


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)
ShowPrompt
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)
ShowPrompt
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)

360

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

=0

=1

IB vbApp Windows

=2

IB vbAppTaskManager

=3

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

361

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 ....
ShowCommand
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.

362

I Chapter 17: Interactive Modification I


Here is the Match
Properties Form in use.
Notice the Command
and Prompt areas at the
bottom.

iJf: N.:ift"?'>''):~,,';'.t:'' ~;""V.... ~ I 'tf;:="'B!Z~ ..-t~ 77:. "f"'-~>'<:;

v,..,..~"""'. ".~.~ry;~'\?~

~v~~ ,~,!!ch prop~rti}~~{: ?J:~<~'\~~~'"'' ,,~,~~>'t: ~~"~>;

~ '-'''-"'

<

<i,~>t~:;;~:~~~~~

- Destination Element(s)

"' Select

Level
r-" '-'-',

"

f"l,

,I,

Change Current Selection

oElement(s) modified,

II

IClick Here to make the current selection the "Source" Element.

I:::' _~B

The Match Properties


Program is simple and
straightforward.
We
Close
allow the user to make
,
modifications
to
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.

.-

frmAlignText.frm
The next form we will import into
our
VBA
project
is
the
frmAlignTextfrm file. This form
allows the user to perform text
I~; ' I 0
alignment
and
distribution
~
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
program
involves
geometric
calculations and movmg text
elements based on those calculations.
M

<

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.

[B

Only Text elements can be used, not Text Nodes.

I Providing User Feedback and Information I

363

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
above.

Control Names
[8

fraBasePoint

[8

fraHoriAlign

[8

btnPickBasePoint

[8

txtX

[8

txtY

[8

btnAlignLeft

[8

btnAlignCenter

[8

btnAlignRight

[8

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)
Do

364

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
txtX.Text
pt3BasePoint . X
txtY.Text = pt3BasePoint.Y
Exit Do
Case msdCa d InputTypeReset
Exit Do
End Se l ect
Loop
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
0
End Se l ect
En d Sub

I Providing User Feedback and Information I

365

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
0
KeyAscii
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.

366

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
msvbaAlignModeLeft
1
msvbaA l ignModeCenter
2
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 = _
msvbaAlignModeLeft)

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

o
Pci vate Su b btnA 0

msvbaAli gnModeLeft
msvbaAlignModeR ight

11!lIIModeLeft])

(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 = _
msvbaAlignModeLeft)

I Providing User Feedback and Information I

367

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)
myTextElem.Rewrite
End Select
End Select
Wend
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':

368

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:
Low:

Pick a point, place X and Y components into TextBoxes.

Medium:

Align selected Text Elements Left, Center, or Right.

High:

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


1

369

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
bottom.
3

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
elements.

We will create distinct functions to accomplish each of the following


tasks:
[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

370

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

371

pt3BoundPt( UBound(pt3BoundPt)) = _
my Tex tEl em.Bo un dary . Hi gh
ReDim Pr eserve ln gElemID ( UBound ( lngElemID ) + 1) As
DLong
ReDim Preserve

pt3Bo~ndPt(UBound(pt3BoJndPt)

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
Wend
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
Dim
Dim
Dim
Dim
Dim

I As l ong
boolMadeChange As Boolean
lngElemID() As Dl ong
pt3BoundPt () As Poi nt3d
myTextE l em As TextEleme nt

372

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
pt3BoundPt(UBound(pt3BoundPt))
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

373

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
Wend

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
SortElementsVertically

Elemsln

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

374

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

375

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
myTextElem.Redraw
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

376

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

377

frmExportElements.frm
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 : .

Export

!:: :
f

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

378

I Chapter 17: Interactive Modification I


ReDim Preserve LevelNames (UBo und(LevelNames) + 1)
Next
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
Wend
For I = LBound(LevelNames ) To UBound( LevelNames)
lstLevels . AddItem LevelNames(I)
Next
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
Else
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

379

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
ShowStatus
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)

380

I Chapter 17: Interactive Modification I


ShowPrompt nn
ShowStatus nn
ShowCommand
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
ShowCommand
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
program.

381

Levels

Here is the interface in use. Any


number of levels can be selected
and exported to the file name
entered.

File Name

I c: \~licroStation
frmDFAV.frm

VBA\exporttest.dgn

Export

The frmDFAV.frm program is used


to display attachments of design files.

. . .. .. . .. . . . . . . . . . ... . ... ...


........
..........
..... .
.. . .. .. .. .. .. .. .. . .. .. .. .. ....
..
. . . .. ...
... . -..
.. . . . .. . . . ...
. . . .. ... .... . .-,..
. . . . ...
.. .. . . .. .,. ..
. .. . ....
. .... ...
.
.
..
..
' ,
.
. . ...
.
..
.
....
-.
.
.
.
..
.
......
.
....
. .. . . . . . .. . . . . . .. . . . ...... . . . . . .. . . . . .
Oesign F~es in Folder 0 Files Found.
....

r;;
.

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.

382

I Chapter 17: Interactive Modification I


Control Names
[B

txtFolder

[B

btnBrowse

[B

lstFiles

[B

lstAttachments

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:

!I..

My Documents

$ .S iifill "I.IIIM
. $ .....

ffie

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.

!I

IQ Administrator's Documents

8:J .. !ClJ

Guest's Documents

8:J . 1O jerryw's Documents


, 8:J.jQ jkw's Documents
I+L~

M\I

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.

Cancel

Here is what this looks like:


File

Edit

View

Favorites

d CJ

L 6

CJ

Help

VBA File Attachment Viewer


Defaults

VBA File Reference Viewer

!I CJ XM_Addln_JW
tED
Viewpoint

,?'!i !
-

~"

Name

n~('D~f~~it)1

i '] i~'~i~d~"S'~bs

-=:
'I fiii"lPath
,..y ,) CJ

; Type

Data

' REG~SZ

(value not set)

REG_SZ

True

REG_SZ

C:\Microstation VBA

~ly Computer\HKEY _CURRENT _USER\Software\VB and VBA Program Settings\VBA File Attachment Viewer\Defaults

Program Components
[B

Retrieve Settings from Registry on Form Initialize

[B

Allow User to Select Root Folder

[B

Search in Folder for .dgn files

[B

Search in Folder's Sub Folders for .dgn files

[B

When user clicks on file, display Attachments

I Provid ing User Feedback and Information I

383

[B When Program Closes, save settings to Registry

Retrieve Settings from Registry on Form


Initialize
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
in.
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

384

I Chapter 17: Interactive Modification I


Private
Private
Private
Private
Private

Cons t
Const
Const
Const
Const

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
DirName
Else
txtFolder.Text
End If
PopulateF il eL i st
End Sub

1)

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

385

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:

OK

~J Visual Basic For Applications


[l,iJ Bentley ~licroStation DGN 8,
; " OLE Automation

Cancel

a Object Libr ary

~rowse",

,_~ 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

Priority

tielp

.S 'L.........x.".~"..._......._....... _1
Microso ft Scripting Runt ime
Location:

C: \ WINDOWS\System32\scrrun ,dll

Language:

Standard

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
Next
If IncludeSubs = True Then
Dim subFolder As Folder
For Each subFolder In FolderIn . SubFolders
FilesInFolder subFolder. FileExtension. _
IncludeSubs. ListToPopulate
Next

386

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
Next
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

387

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.

INTERACTING WITH

MOL ApPLICATIONS

Let's record a macro where we import an image using the MicroStation


menu
File > Import > Image.
Browse
to
C\Program
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

388

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
startpoint.X
-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
point.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

389

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.SerdCo~nand

"MOL COMMAND

MGDSHOOK.fileList_set~ilterCmd

*.cal"

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

msdDialogBoxResultOK

' 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 _
MsdDialogBoxResult)
End Sub
Private Sub IModaIDial ogEvents_OnDialogOpened(ByVal _
DialogBoxName As String . DialogResult As MsdDialogBoxResult)
If DialogBoxName = "Select Image File " Then
CadInputOueue.SendCommand _

390

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 " &
FileName
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.

REVIEW
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.

18

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

391

392

I Chapter 18: Interface Essentials I


[B The LocateCriteria object
[B

IPrimitiveCommandEvents interface

[B Optimizing the dynamics event

INTERFACE BASICS
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.

Implements

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

393

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
module:
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.

CLASS MODULE REVIEW


An interface must be exposed using a class module. We already
discussed class modules but a quick review is in order.

394

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.

1':

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
IPAddress.Set4
End Sub

395

IPB
I Pe
IPD

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

CLASS MODULE LIFECYCLE


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

396

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
until
either
MicroStation
is
closed
down,
we
use
"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.

interfaces.

The

first

is

the

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:
[EJ

Private Sub ILocateCommandEvents_Accept(ByVal Element As


Element, Point As Point3d, ByVal View As View)

[EJ

Private Sub ILocateCommandEvents_CleanupO

I Class Module Lifecycle I


IB Private Sub ILocateCommandEvents_Dynamics(Point As
Point3d, ByVal View As View, ByVal DrawMode As
MsdDrawingMode)
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
elemText.Rewrite
ActiveModelReference . UnselectAllElements
CommandState.StartDefaultCommand
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

397

398

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
ActiveModelReference.UnselectAllElements
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

399

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
"CommandState.StartDefaultCommand".

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.

400

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
class.
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 ...

TANGENT
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

401

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
ActiveModelReference.UnselectAllElements
CommandState.StartDefaultCommand
End If
End Sub
Private Sub ILocateCommandEvents_LocateReset()
CommandState.StartDefaultCommand
End Sub
Private Sub ILocateCommandEvents_Start()
End Sub
Here is the code that initializes the Interface Object.

402

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
now.
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.
':S!

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
A

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

403

CreateTextElementl(selE l em , Round(dblDistance, _
3) , Po i nt, rotMatrix)
ActiveModelReference.AddElement txtElem
txtEl eli. Rewri te
txtElem.Redraw
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

404

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.

LocateCriteria
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
LocateCriteria.
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

405

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

406

I Chapter 18: Interface Essentials I


[B

Sub ExcludeAllTypesO

[B

Sub ExcludeClass(ElemClass As MsdElementClass)

[B Sub ExcludeLevel(Level As Level)


[B

Sub ExcludeType(Type As MsdElementType)

[B

Sub IncludeClass(Elem Class As MsdElementClass)

[B Sub IncludeLevel(Level As Level)


[B

Sub IncludeOnlyHoleO

[B

Sub IncludeOnlyLockedO

[B Sub IncludeOnlyModifiedO
[B

Sub IncludeOnlyNewO

[B

Sub IncludeOnlyNonPlanarO

[B Sub IncludeOnlyNonSnappableO
[B

Sub IncludeOnlyOldO

[B Sub IncludeOnlyPlanarO
[B

Sub IncludeOnlySnappableO

[B

Sub IncludeOnlySolidO

[B

Sub IncludeOnlyUnlockedO

[B

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.

IPrimitiveCommandEvents
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
selection.
Here are the events we have to work with:
[B Private Sub IPrimitiveCommandEvents_ CleanupO

I Class Module Lifecycle I

407

[B

Private Sub IPrimitiveCommandEvents_DataPoint(Point As _


Point3d, ByVal View As View)

[B

Private Sub IPrimitiveCommandEvents_Dynamics(Point As _


Point3d, ByVal View As View, ByVal DrawMode As
MsdDrawingMode)

[B

Private Sub IPrimitiveCommandEvents_Keyin(ByVal Keyin As


String)

[B

Private Sub IPrimitiveCommandEvents_ResetO

[B

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.

PCE_LineTest
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
Else
CommandState . StartDefaultCommand
End If
End Sub

408

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

CreateLineElement2(~othirg,

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

409

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
CommandState.StartDefaultCommand
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).X
Point.X
pt3RecPoints(1).Y = pt3BasePoint . Y

410

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

411

Private Sub IPrimit iveCommandEvents_Cleanup ()


End Sub
Private Sub IPrimitiveCommandEvents_DataPoint(Point As Point3d. _
ByVal View As View)
If boolSet = False Then
pt3BasePoint = Point
CommandState.StartDynamics
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()
CommandState.StartDefaultCommand
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 .

412

I Chapter 18: Interface Essentials I


Sub Pl aceC i rc ()
CommandState.StartPrimit i ve New PCE_CircTest
End Sub

PCE_PolyTest
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
CommandState.StartDynamics
boolSet = True
Else
CommandState.StartDefaultCommand
End If
End Sub

I Class Module Lifecycle I

413

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

414

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.

PCE_PointStringTest
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

415

pt 3B aseP oint = Poi nt


pt3Points(O) = Point
ReD i m Preserve pt3Po i nts(UBound(pt3Points) + 1)
Co mmandState.StartDyramics
boolSet = True
Else
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)
Case "PLCLOSE"
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()
CommandState.StartDefaultCommand
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",

416

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
elements.
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

417

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
chapter.
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
method.

PCE_lineTest2
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

418

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
CommandState.StartDynamics
boolSet = True
Else
Dim pt3EndPoint As Point3d
Dim dblLineAngle As Double
Dim dblSegDist As Double
Dim DivPoints() As Point3d

I Class Module Lifecycle I

419

Di m I As Long
ReDim Di vPo i nts(O To lngDivisions - 2) As Point3d
pt3EndPoint
Point
cblLineAngle = Atnpt3EndPoint.Y - pt3Base Doint.Y) /
(pt3EndPoint.X - pt3BasePoint . X))
dblSegDist = Point3dDistanceXY(pt3BasePoint. pt3ErdPoint) /

lngDivisions
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
CommandState.StartDefaultCommand
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. _

rotMatrix)
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()
CommendState.StartDefaultCommand
End Sub

420

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
class:
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
segments.

PCE_TestLine3
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

421

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
DoEvents
Wend
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
Next
End Sub

Let's break this procedure down for discussion.


1

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

12

3 We start the PrimitiveCommandEvents object.


CommandState.StartPri mitive myLineTest3

422

I Chapter 18: Interface Essentials I


4

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
DoEvents
Wend

False

5 We get the points that had been created by the selection of the two
points.
myDivPoints

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
DoEvents
Wend
myDivPoints = myLineTest3.DivPts
For I = LBound(myDivPoints) To UBound(myDivPoints) - 1

I Class Module Lifecycle I

423

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
Next
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
DoEvents
Wend
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
Next
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?

424

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

425
Point
Atn((pt3EndPoint.Y - pt3BasePoint . Y) / _
(p t 3En dPoi nt . X - pt3 Base Po i nt . X))

db'SegDist = POint3dDistanceXYCpt3BasePo'nt.

For I

pt3EncPoin~)

, _

lngDivisions
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()
CommandState.StartDefaultCommand
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

426

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
Next
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

427

shown in this chapter work well, we may need to minimize processor


overhead when working in the dynamics event.
Pr~vate

Sub

IPrimit;veCo~na~dEvents_Cynamics(Poi~t

ByVal View As View, ByVal


Di m my Li ne El em As Li ne El ement
Set myLineE l em

~rewMode

As

As Point3d, _
~sdDrawingMode)

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


following:
1

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

428

I Chapter 18: Interface Essentials I


Else
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
PCE_LineTest4.

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


pt3BasePoint

429
=

Point

Set myLineElem = Create LineElement2(Nothing. Point. Point )

CommandState.StartDynamics
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.

430

I Chapter 18: Interface Essentials I


REVIEW
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.

19

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:
[B

Declaring MicroStation user form functions

[B

The FileOpen dialog

[B

The FileCreate dialog

[B

The FileCreateFromSeed dialog

[B

The OpenAlert dialog

[B

The OpenlnfoBox dialog

DECLARING MICROSTATION USER FORM FUNCTIONS


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.

431

432

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 .

mdlDialog_openAltert

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
clicked.
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


ByVal
ByVal
By Va l
ByVal

433

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

434

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:
1fi~;'"

.<:'.} ...."""._ .. "" . "J''''

'>.;~)....~~"'",~':l "~...,.o:;:~,,"""

..

''''1~'

Ope!! File -- C:\Micro~~a!io!I.VB.A\':~~"">.:J'.<"'~ '" " , '"

Ib

Look in:

Microstation V8A

vi

.... ~ ~

,"

"

'"'1'<~

<

() 1t f"

~'

.. ",t

...
rmJ

: ~~
GJ

~ filelO.dgn
~ filea.dgn
~ fileb .dgn
~ rast erdocs.dgn

e:i.~?~t.~j
ICl cd material
Ii::l Source Code

Re cent

"""' ... ,...-.. '\;~.

, ft) pics
IC::ll docs

~ file l , dgn
~ file2.dgn
~ file3.dgn
, ~file4.dgn
~fileS.dgn
"
~file6.dgn
M~ Documents
~file7.dgn
. ~file8.dgn
~file9 . dgn
Desktop

M~ Computer

,
[
;

File name:

Files of \ype:

~~~~.ti:'~E.~~_~~~.~::9.~~L ______________ ~:l

W,~,

M~

Network
Places

h... !

o apen as !ead-onl~

~.

Open

il

Cancel

I
,0>

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
selected?"

I Declaring MicroStation User Form Functions I

435

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.
strFName

Space(255)

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
MessageBox:

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
made:
Sub TestFi 1 eOpenB ()
Dim strFName As String
Dim lngfhandle As Long
Dim lngrid As Long
Dim retVal As Long

436

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

Recent

!!Q pics

@
Desktop

ll~file1.dgn
!iCl docs

I ~file2.dgn
l ~file3.dgn

Il

My Computer

~file4.dgn
~file5,dgn

II ;',

i ~file6 . dgn

. My Documents

~file9 .dgn
~filel0.dgn
~filea.dgn
~fileb . dgn
~ rasterdocs.dgn

l~ file7.dgn

Ibij) fileS .dgn

I'
. 1________________________________________
I ~I

.. __Ivtl'._~!:~g!~ _I

Filename:
Files of lYpe:

------.-.-.--.------..-.---.----.-.---.-.---..-.--..'"':1
[.mmm! _
_
_
.v ;,

I
o

Open
Cancel

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.~' ,~

Open

;-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

437

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.

438

I Chapter 19: Using Mi croStation's Bu ilt- In User Forms I

Ib

Look in:

Mic,ostation VBA

IClFonts
;Qed material
QSouree Code

Recent

;Qpies

Fr~

o
,

~Book2.xls
~ Enums,xls

Desktop

r~'\

v:;:;

does

~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

I'"

Open

Cancel

'il
I

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)
retVal

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

439

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, _

440

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
strFName
Space(255)
mdlDialog_fileCreate(strFName , lngfhandle, lngrid, _
retVal
"", "*.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
box.

iCl MiClOstation VBA

Save jn:

r:J

Recent

@l
Desktop
"

'~
- 7'
My Documents

i '0
if , Fonts
[!Q cd material
IlrtJ Source Code
I

t~il 0

f} CO

ltl

~fileb.dgn
~ rasterdocs ,dgn

ii

i Ibpics

,Iro docs

I i ~ filel.dgn
I i~ file2 . dgn
I

l ~file3.dgn

1 ~ file4.dgn

il file5.dgn
't IM

, !~ file6 , dgn
i ~ '1 d

I l ilfl e7,gn

I[

: . ~file8,dgn
I ,a fIIe9.dgn
f ,lelO,dgn
t
My Compute,
, i filea. dgn

QI
. .

" I~

,I

li:4l
i:4l

,II -

Ii

My NetwOIk
Places

,I

Filename:

I
i

Save as Jllpe:

vi

[-MiclOStation
-------"-----"
.. _------:]
DGN Files ["dgnJ
,v ,

Save
Cancel

l
..I,

I Declaring Mi croStation User Form Fu nctions I

C:\Microstation V8A\file5,dgn already exists,


Do you want to replace it?
'ies

Ir

NOm)

441

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.
Sub

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"

442

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.
o~iC:,~*,<~~f.;!:rr~,~~"'":::~~'0~<~~"':fr",;~,x::::;:":~';~*~:"~{i~"r::1-r"'~~;:Vf:~'~~:~~<>~

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.",,~
"A

< j

IC) Microstation VBA

Save in:

,
i

EJ Ii ei~~~f~]
i
I
[

Recent

; ICl cd material
rQ Source Code
D pics

t;.lll 0

~,

B' tm) T

~ lJ

~filea.dgn
~fileb.dgn
~rasterdocs.dgn

rQ docs
I ~filel.dgn

Desktop

v!J

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

f&l

i
I

Filename:

... ~y. N ~twor~ ,. : Save as ll'pe:


Seed:

I1--- ----- - --- ---- - - -,vi]

. MicroS tation DGN Files (". d g n j v_

1~:'D?::uments a~d.. Set~in~~~II. Users~pliGat~0!l

i
(
[

Save
Cancel
Browse

l
I
I
."

I Declaring MicroStation User Form Functions I

443

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

444

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
- .

_.

I"formation

~~

<',~~

'"/1 '"

'70<

.,

~,

This is a test.

REVIEW
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.

20

Class Modules
Class modules have a variety of purposes with these primary beneficial
uses:
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

445

446

I Chapter 20: Class Modules I


ENCAPSULATING SIMILAR FUNCTIONALITY
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.
[8

Display File Open Dialog for MicroStation DGN files .

[8

Display File Open Dialog for Microsoft Excel files.

[8

Display File Open Dialog for ASCII .txt files.

[8

Display File Open Dialog for custom file extensions.

[8

Property needed for File Name.

[8

Property needed for Path Only.

[8

Property needed for Path / File Name.

[8

Property needed for Size of selected File.

[8

Registry Entries to be used to store most recent path.

[8

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

447

' 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.

448

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
properties.

I Encapsu lating Simi lar Functionality I

449

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

450

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
extension.
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

451

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.

452

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

Help

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
Err.Clear
End Select
End Pr operty

I Encapsulating Similar Functionality I

453

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 . _
1)
pFileNameSelected. Chr(O))

454

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
OpenSuccess
False
Cas e 0 ' Open
Ope nSuccess
True
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


Private
Private
Private
Private
Private
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

455

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
Open Success
True
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
Case
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))
1)
xSplit = Split(tmpFile, "\ " )
pFileName = xSpl i t(UBound(xSplit))
xSpl i t(UBound(xSplit)) =
pFilePath = Join(xSplit , "\ " )
End Select
End Sub

456

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
errhnd:
Select Case Err.Nu mber
Case 52 ' Bad fi l e name or number
Err.Clear
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

457

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 "

458

I Chapter 20: Class Modules I


MyUSD.AddFileExt "dwg "
MyUSD .A ddFile Ext "dx f "
" c:\MicroStatio~ V8A "
MyUSD.DefaultPath
MyUSD.DefaultFile = "test.dgn"
MyUSD.OpenDialog
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
box.

ib

Lookjn:

EI

MicrosiaiionV8A

C l1r fjl

IT1].

C!J

[ ,-_._._._..._--_. __. _-_._-----_. _. _---_... _. __ ... _--_ ......_---_. __ . . _. __._----_.._._--_..__...__ . __....._------'-'j


f !~ Fonts
ED file 7.dxf
i

;:Y"1
W

! ifO cd material

Recenl

HD source Code

I!g~::s
@ Ij~ file1.dgn

irj ~ file2 . dgn

Desklop

l~ file3 .dgn

'",

!~ file4 . dgn

~ file8.dgn

~ file9.dgn

'

~;: :!~~~~n
~ fileb.dgn
~aa . dgn

~ rasterdocs .dg n

i l~ file5.dgn

f/jJ

M D
~ ,~ file6 . dgn
y ocumenls j~ file 7.dgn

I!m file7.dwg
l!
Ii' - - - - - - - - - - - - - - - -- - - - ---'

My Compuler

f Filename

~
., My

~.l.e.lwork

! Files of lI'pe:
i

ItmmI

~v:1

t~~~~::5!"'~~::.d~f ___________________________~_~J

Open .

:1

Cancel

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.

In

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

459

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))
1)
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

460

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"
MyUSD.CreateDialog
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 "
MyUSD.DefaultPath
"c: \ "
MyUSD . De f aultFile = "test.dg n"
MyUSD.OpenDialog
Select Case My USD.OpenSuccess
Case True
MsgBox "Open" & MyUSD.SelectedPat h &
MyUSD.SelectedFile
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

461

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
tJide

462

I Chapter 20: Class Modules I


Select a location and save the file.

Save in:

~.!:!!croslalion V8A

eiF~~t~:

e:l~d~~terial
!Q Source Code
IO docs
!Qpics

~__I~======-=_=_-=~~~J
File name:

Save as I~pe:

clsUSlalionDialog.cls

[Clas:.~~.:rcl~l._______. _ ~
Help

Lookin: eJ Microslalion V8A


' ~ J::" rr:m~
ftiSl Font;--------- ----------- ------------- -- - -- -----.----'-----

l.'ill()

File> Import .

!t:Jlcd material
1
Source Code
IC:l docs

110

'I!Q PiCS

@J!!iBM.lnJ.!J

!
File name:
Files of I~pe:

N OW, as you create a


new VBA Project,
use the VBA menu

Open "

[ViiFi;;rh;;;~~--~-~
.'

I
[

Cancel
Help .

Select the class just


exported and it will
be imported into the
new project.

:.f

'J

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
classes.

CREATING OBJECTS WITH PROPERTIES, METHODS, AND


EVENTS
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

463

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
OK

II

Help

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

464

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)
myLE.EndPoint
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:

10
10
10

Double
Double
Double

Point 3d
Point3d

Now that the StartPoint and


EndPoint properties seem to
Double
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
tmpPoint.Y
StartPoint . Y + (EndPoint. v
tmpPoint . Z StartPo i nt . Z + (EndPo i nt . Z
~lidPoint
tmpPo i nt
End Pro pe rty

465

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.

10
10
10

Double
Double
Double
Point3d
Double
Double

Double
Point3d
Point3d
Double
Double
Double

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


properties:
Property Get LineAngleRads() As Double
LineAngleRads = Atn((pEndPoint.Y - pStartPoint.Y) / _

466

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
myLE.EndPoint
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
module.
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

467

Di m Line Ang As Doub le


Di m LineLength As Do uble
LineAng = LineAngleRads
PerpMid = MidPoint
LineLength = Point3dDistance(pStartPoint. pEndPoint)
PerpSt
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
myLE.DrawLinePerp
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
explanation:
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.

468

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
properties.
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

469

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

> ..

470

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.
1

Declare the event with "Public Event" in the class module.

2 Raise the event with "RaiseEvent" in a class module property or


method.

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.

USING CLASS MODULES WITH COLLECTIONS


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
Next
End Sub

I Using Class Modu les with Collections I

471

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.

Long
String
msdLevelElementAcce ssAIl

MsdLevelElementAccess t

Long
Linest yleilineStyle

Long
Long

IsActive

Boolean

IsDisplayed

True
True

IsFr omLe velLibrary

False

Boolean

IsFrozen

False

Boolean

IslnUse

False

IsLa eked

False
"Levell "

Boolean
Boolean

Name
Number
OverrideColor

1
0

Boolean

String
Long
Long
LinestylelLineStyle

OverrideLinestyle
OverrideLineWeight

Long

Parent Level

Nothing

Level

Plot

True

Boolean

UsingOverrideColor
USingOverrideLinestyle

True
True

Boolean
Boolean

UsingOverrideLineWeight

True

Boolean

~ em

Variant lObjectilevel

~em

V ariantlObjectilevel

Iv

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.
Sub

TestCo ll ect i onB ()


Dim myColl As New Collection
Dim my Level As Level

472

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
Next
'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
Next
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
Next
' 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
Next
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

473

Dim myCol l As New Collection


Di m my Level As Level
For Each myLevel In ActiveModelReference.Levels
myCo11 .Add myLevel
Next
'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
Next
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
Next
' 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

474

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
Next
'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

475

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
myLE.DrawLine
Next
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
collection.

476

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
Next
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

477

we draw a line between the entered X and Y values and the Start and
End Points of the myLineElem object.

Enter X Value:

OK

[ Cancel

Enter Y Value:

OK

I
I

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.

478

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
collection.

REVIEW
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
programming.

21

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

USING

VBA FOR MAINTAINING STANDARDS

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.

479

480

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)
"ROADW AY "
"SI DEW ALK "
Goo dLevel s( l )
GoodLeve l s(2)
"PA INT"
Good Le ve l s(3 )
"ELECT RIC "
GoodLevels(4)
"GAS"
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
Next
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.

Unsupported
Unsupported
Unsupported
Unsuppo rted
Unsuppo rted
Unsupported
Unsupported
Unsuppo r te d
Unsuppo rted
Unsuppo rt e d

<::F I

Level
Level
Level
Level
Le vel
Level
Level
Level
Level
Level

Fo und:
Found:
Found:
Fo und:
Fo und:
Found:
Found:
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
Default

~.

~~l

XI

I Using VBA for Maintaining Standards I

481

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
next.
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
"ROADWAY "
GoodLevels(O)
Goo dLev el s(l)
"SIDEW ALK"
"PA I NT"
GoodLeve l s(2)
GoodLevels(3)
"ELE CT RI C"
GoodLevels(4)
"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

True

Next
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


Window.
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

482

I Chapter 21 : VBA for CAD Managers I


GoodLevels(O)
"ROADWAY"
GoodLevels(l)
"SI DE WALK "
"PAINT"
GoodLevels(2)
"ELECTRIC"
GoodLevels(3)
"GAS "
GoodLevels(4)
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
Next
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
GoodLevels(O)
"ROADWAY "
GoodLevels(l)
"SIDEWALK"
"PAINT"
GoodLevels(2)
"ELECTRIC "
GoodLevels(3)
"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)

LevelFound(I)
End If

0 Then

True

Next
Next
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

483

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
"ROADWAY"
"SI DEWALK"
GoodLevels(lJ
"PAINT"
GoodLevels(2)
Goo dLe vel s(3)
"ELE CT RI C"
GoodLevels(4)
"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
Next
Close #FFile
End Sub
Sub Fi ndMi ss ingL e vel sC(F i le ToOu e ry As Des ignFi le)

484

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
GoodLevels(O)
"ROADWAY"
"SIDEWALK"
GoodLevels(l)
"PAINT"
GoodLevels(2)
"ELECTRIC"
GoodLevel s(3)
"GAS"
GoodLevels(4)
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

True

Next
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

485

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\" &

"Projects\Examples\Arcnitectural\Dgn")
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
myDGNFile.Close
En d Sele ct
Next
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
VBA.
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:'

CROSS-COMPANY STANDARDS
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.

486

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"
ActiveOesignFile.Levels.Rewrite
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

487

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
Case "SIDEWALK "
X myLevel . Name = "Leve 1 2l "
ActiveDesignFile.Levels.Rewrite
Case "GUTTER"
myLevel . Name = " Level 23"
ActiveDesignFi l e.Levels.Rewrite
Case "STRIPING "
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
Case "ELE CTR IC"
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
Next
End Sub

488

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

489

Open my File For In put As #F Fil e


While EOF(FFile)

False

Line Input #FFile, txtIn


If InStr(l, txtIn, vbTab)
xSplit

>

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) +

1)

ReDi m Prese r ve New Level (UBound(NewLevel ) + 1)


End If
Wend
ReDim Preserve OldLevel (UBound(OldLevel)

1)

ReDim Pr eserve NewLevel (UBound(NewLevel) - 1)


Cl ose #F Fil e
For

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

Next
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.

490

I Chapter 21: VBA for CAD Managers I


TRACKING TIME
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.

Drafters
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:'

Managers
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.

Accountants
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
side.

ITracking Time I

491

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
problem.
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

492

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 &
XSplit(UBound(XSplit))
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

493

Cl ose #FFile
End Sub

Let's look at the events we are capturing:


Class_Initialize

Triggered when th e cl ass is fi rst


opened.

Class_Te rminate

Triggered when the class is


te rminated.

IViewUpdateEvents_AfterRedraw

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
file.

OnDesignFileOpened

A member of the MicroStation


application object. When a fi le is
opened, we are given the name of the
file.

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

494

I Chapter 21: VBA f or CAD Managers I

Here is
the
output of
t he Class:

Eile !;.dit FQ.rmat I'.iew t!elp


IN IT
RE DR
REDR
RE DR
CLOS
OPE N
RE DR
REDR
REDR
REDR
CLOS

9/11/2005
9/11/2005
9/11/2005
9/11/2005
9/11/2 0 05
9/11/200 5
9/11/ 20 05
9/11/2005
9/11/20 05
9/11/20 05
9/11/200 5

3:08 : 13
3:08:17
3:08:19
3:08 : 20
3 : 08 : 23
3 : 08 : 24
3: 08 : 24
3:08:29
3: 08 :3 2
3:0 8 : 38
3 : 08 :5 2

PM
PM
PM
PM
PM
PM
PM
PM
PM
PM
PM

Admi ni s trator
Administrator
Administrator
Admini strator
Admi ni s t r ator
Admin i str ato r
Administrator
Administrat or
Admin is trat or
Admi nis trator

cogo.dgn
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.

AUTO-LoAD AND AUTO-RuN


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.
'';'-~~

-,~~j

~~""';''''''~;7,-A

~'?

",""~

,,,,::>;\~',(''''i~'';'r,'~.,~-

r~:

~'y~~I?~pj~ct,"Manager ?:,,,i'~~, ,,/,-,:~,i<:",.. \.. . . J~

~.,-"

"'-~>_P",;;;-

',.,

r""l'<~'"

.~.,

,.'

v,,",

.lf8J

Name

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
loaded.
Now that our project is AutoLoading, how do we automatically run a
procedure?

Sub OnProjectLoad ()
TestTimeTra ck
End Sub

I Auto-Load and Auto-Run I

495

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
file.
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,
no.

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.

496

I Chapter 21: VBA for CAD Managers I


Let's discuss the acceptable values for this variable, define it, and set its
value.
all

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.

readonly

Opens ReadOnly VBA Proj ects in memory. ReadWri te


Project s are opened fro m fil e.

none

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
,--:;;!".":>-"\r~'"

"""'~'y.:".v:n-"t"'''' -".~-"'_.::r:,,:~v-

::?":~~

.:: ."; . .

.,~

~H."', "'~ ~,~ '~',

Cqf!figl!r!l~ton ~; lIJie,~ [!l!1titled]" " " ,,,,' " " ',' ,"
File

~"H~~~~V<"'"W""''''' ~~,.,

""~'

p,' '."

"

, , "

....

".>~~

,'.

. , '

AV

'

'~~:c;--==~'i E~~t~l:::::~~:::::~~;;--s-y~~:--;-'--':'l- --~:-~~


I~avlng

, Names of standard profect s


DirectOries to search for VBA

leis
- Development

~:~~~/l mages
~~~~i~es

IlI ing

~l' I [

II,

I f!e'~, ~

I I SelecL I
.

C:\Documents and SeUlngslA1i UserslApphcalion Data\Documents\Bentley\\

r~i

i.ii !

'1

Edit..

r,1 1Ewansion

i,dards Checker
'bOIOGY
:em Env

- - - - - - - - --- - - - - - - - - - - - - '

h'

Cancel

If

~'I:

'

:I

'ration
ling

,:~~i~:arch Paths

User
System

'!!

:1

gjl

i Directory that is used when a new project is created,

and Backup Files


,slation,CGM
,slation,I GE S
,slation,Step

t ~II
ill i

r Commands

(MS_VBANEWPROJECTD IR ECTORYJ

\_1, 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

497

Click the New button to add a new configuration variable.


_.

. . .0'

_,~_,.

~""<~"",

-_~_

~~

""

,.~

_''v~

_.

.~

,....

~.

...

'{:.

<

New Confjgu~a!!~_~ ~~ri'!!J!e.~ . __ ..... ~~=.... ~'-=,.~~-"A.<,~


Variable: [}IS_VBA OPEN IN MEMOR'!: _ _ _ _._________

=:J

"~VO~ [ .,_
y_________.

~
~

___..--1
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 .

~."

,,<~

"'

_~~v_

.,,~~._

A~rt

.~N

_~'A

,
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.

498

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.

General

ICustom Ii Summary I

~
~

rr----------------------------J
~_~"!.'_ter2 :_~~~_~ ___________________ _

---_._----------_._--------Type of file:

MVBA File

Opens with:

Unknown application

Location:

C:\Documents and Settings\AII Users\Application C

Size:

69.0 KB (70,656 bytes)

Size on disk:

72 .0 KB (73,728 bytes)

Change .. .

---~===~

Created:
Modified:
Accessed:

---------------------Attributes:

[AI;!vanced ...

OK

I[

Cancel

Ii

8Ppl/

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.

PROTECTING PROJECTS
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
1

499

In the VBA menu, select Tools>

"~-",,("'--,~-"-"'-- -,--- ~'::"''''l''f'''"'

ettings\AU Us~rs~ppl!!=d!io~~

Project Properties.

~ Add-Ins

(t.

The Properties menu item is


different from project to project.

Help

f!.ddi\:onal Controls ..
[:1acros ...

2 Click the Properties menu item to

Qptions...
Chapter2 1 Prop ~rties ... ~

display the VBA Project Properties


window.

General

Window

B.eferences...

Qigital Signature ...

[~~IfL_--_-----------

[~~----------. -.- -.------.--.------~~-1

"*"" ,,0;;0",
r~-~~:~~~--------------------------lO
r~~~itiO.~ ~I_~~~.':il_~ t~o_~~r~~~=~~s_:_.___ _ ______

Project Help

W~:~I~----l

I
t
I

. __________ ... ______ ,

!
t

_._-_ . _ -------------_.._______1
~ .

OK

. :11

Cancel

I[

Help

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.

500

I Chapter 21: VBA for CAD Managers I

~ Protection

1<--___________--,

Use the Protection tab


to set the Project's
password.

' Lock project

<'

Password to view project properties - '-

- - - - _._- - - - - - ,

~-----~--------~I I

!:assword

~onfirm password 1-1__________---'1 1


,---..-----.--.-.--.-------.-.-.-c __ ..__.-1

r'

..

OK

ij

I .Cancel II

Help

Projects set as "Locked


for viewing" can be
loaded and procedures
can be executed from
Project
the
VBA
Manager but they
cannot be opened in
the VBA environment
for
modifications
unless
the
correct
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.
",--(-~t!>~

''''1~''''':'f~-It '':~ ~~'''''''- ~';Y

5 Click on the Plus symbol to

. . ~",

Chapter214Pilli~rd ""., .,\(' .'> ""'"

"

.~
!:assword

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


DISTRIBUTING

501

VBA PROJECTS

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
;,7
lAS Helper COM Component 1.0 Type Library

El

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

502

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:

OK

Ali

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
i
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
j
D Microsoft InkEdit Control 1.0
-+ I
D Microsof t Internet Controls
...:J
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

Cancel

Browse".

i-.!J

Help

lIll!::~.:.~.~,:;.:~~~::}~J~r,::,,~"~,~,>,~~:,;..~,::J

,,~~l_i

6 11

, Microsoft Excel 11.0 Object Library


Location:
Language:

C:\Program Files\Microsoft Office\OFFICEII \EXCEL.EXE


Standard

_" _______ ...... _ ..;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.

WORKING IN HIGH SECURITY MODE


There are benefits to starting MicroStation in High Security mode. Here
are two:
1

attempting to enter the VBA area by clicking Utilities> Macro>


Visual Basic Editor results in the following error:
1

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

503

"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.

REVIEW
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.

504

I Chapter 21: VBA for CAD Managers I

22

MicroStation File-Based
Events
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:
[B

The "OnDesignFileOpened" event

[B

The "OnDesignFileClosed" event

[B

The "ISaveAsEvents_BeforeRemap" event

[B

The "ISaveAsEvents_AfterRemap" event

[B

The "ISaveAsEvents_AfterSaveAs" event

50S

506

I Chapter 22: MicroStation File-Based Events I


ONDESIGNFILEOPENED
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

507

ONDESIGNFILECLOSED
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

508

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
module.
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

509

Kil l CopyFileAs
Else
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 http://www.codeproject.com!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\FileClosed.zip". _
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

510

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.

ISAVEAsEvENTSINTERFACE
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

511

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?

512

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
else.
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:
i..,i)

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

513

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

~..

_~~

~~v

~ 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

514

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 !

515

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

516

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

517

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.

REVIEW
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.

518

I Chapter 22: MicroStation File-Based Events I

23

Responding to
MicroStation
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:
[B

The IAttachmentEvents Interface

[B

After Attach

[B

After Detach

[8 AttachmentModified
[8 BeforeAttach
[8 BeforeDetach

519

520

I Chapter 23: Responding to MicroStation Attachment Events I


THEIATTACHMENTEvENTSINTERFACE
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 .

AFTERATTACH
Private Sub IAttachmentEvents_AfterA t tach(ByVa l _
TheAttachment As Attachment)
End Sub

I AfterAttach I

521

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.
[B

Sub AddElement(Element As Element)

[B

Sub AddElements(ElementsO As _Element)

[B

Property AttachName As String {read-only}

[B

Property Description As String

[B

Property DesignFile As DesignFile {read-only}

[B

Property DisplayFlag As Boolean

[B

Property Is3D As Boolean {read-only}

[B

Property IsActive As Boolean {read-only}

[B

Property IsReadOnly As Boolean {read-only}

[B

Property IsTrueScale As Boolean {read-only}

[B

Property Level As Level

[B

Property Levels As Levels {read-only}

[B

Property LogicalDescription As String

[B

Property LogicalName As String

[B

Property MasterOrigin As Point3d {read-only}

[B

Sub Move(Offset As Point3d, ApplyToClipElement As Boolean)

[B

Property Name As String

[B

Function Reattach(FileName As String, ModelName As_


String) As Attachment

[B

Sub Redraw([DrawMode As MsdDrawingMode =_


msdDrawingModeNormall)

522

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 Point3d
Txtpt. X = a
TxtPt . Y = -100
Dim RotMatrix As Matr i x3d
Dim myText As TextElement
Set myText = CreateTextElementl(Nothing. Now & n n &
TheAttachment.DesignFile.Path. TxtPt. RotMatr i x)
ActiveModelReference.AddElement myText
End Sub

Each time an attachment takes place, we are writing the path of the
attachment's DesignFile as text to the ActiveModelReference. As the
code stands now, the text will be added to the same place whether it is

I AfterAttach I

523

the first attachment or the hundredth. We could add code to change the
position of the Text Insertion Point, etc., but we will move to another
example that uses the AfterAttach Event.
Private Sub IAttachmentEvents_AfterAttach(ByVal
TheAttachment As Attachment)
Dim FFi le As Long
FFi l e = Fr eeF il e
Open "C: \MicroStatior VBA\AttachmentLog . txt " For Append As #FFi1e

Print #FFi l e . Now & vbTab & ActiveDesignFile . Path & vbTab &
TheAttachment.DesignFile.Path
Cl ose #FFile
End Sub

If you follow the code shown directly above, you see that we are writing
the ActiveDesignFile's Path and the attachment's path to an ASCII text
file named AttachmentLog.txt. You can use this log file to track file
dependencies.
One more example demonstrates additional use of the Attachment's
properties:
Private Sub IAttachmentEven t s_AfterAttach(ByVal _
TheAttachment As Attachment )
Di m PtA As Point3d
Dim PtB As Po i nt3d
Dim my Range As Ra nge3d
Dim MyRe c As Li neEleme nt
Dim Li nePts(O To 4) As Point3d
myR ange = TheA t tachme nt.Range ( True)
PtA = myRange . Hi gh
PtB = myRange.Low
LinePts(O).X
PtA . X: LinePts ( O) .Y
Pt A. Y
LinePts(l).X
PtB . X: LinePts(l) . Y PtA . Y
LinePts(2).X
PtB.X : LinePts ( 2).Y
PtB. Y
Lin ePts (3 ) . X PtA . X: LinePts (3) . Y PtB.Y
LinePts(4) . X PtA . X: Li nePts(4) . Y PtA . Y
Se t MyRe c = Applic ation . Crea teL ineEl ementl ( Not hi ng. LinePt s)
Act iveM od elReference. AddEleme nt MyRe c
End Sub

This example draws a rectangle around the outer rectangular boundary


of the attachment.

524

I Chapte r 23 : Res pondin g to Mi croStation Attachment Events I


We looked at three examples of using the AfterAttach event. These three
examples cannot exist in the class simultaneously. Leave the example
you want to see working uncommented, comment out the remainder of
the AfterAttach examples in the code.
As with other Interfaces, you need to add a class to the event handler.
The class we are working in is named clsAttachmentEvents. Here's the
code that adds the class to the event handler:
Sub Test Attach mentsA ()
Di m myAE As New cl sA t tachment Events
AddAttachment EventsHandler myAE
End Sub

AFTER DETACH
Two events relate to detaching attachments: Before Detach and After
Detach. The After Detach event is you last opportunity to do anything
with the attachment. Let's try some code that could notify someone an
attachment has been detached.
Pr iv ate Sub IAtt achmen tE ven ts_Aft e rD eta ch( By Va l _
Th eA tta chm en t As Att ach ment )
Shell "c : \ Progr am File s\ Int e r ne t Ex pl or e r \ie xp lo r e .e xe " &
""" http : //www . trackmydgndra wi ngs.co m/log . asp ?f i le na me= " & _
Ac ti veDesignFi l e . Name & "&at t achment= " & _
Th eA t t ac hment . Des i gnF i le.Na me & """ ", vbMaximizedF ocus
End Sub

The She 11 function is a standard VBA function. In this example, we use


it to execute Internet Explorer and go to a fictitious web site with the
ActiveDesignFile.Name and the "TheAttachmentDesignFile.Name". If
the log.asp file can read these parameters in the HTTP request, we now
have a web server tracking files as they are detached.

I AttachmentModified Event I

525

ATTACHMENTMoDIFIED EVENT
The AttachmentModified Event gives us the same information as the
AfterAttach and AfterDetach events. We are given the parameter
"TheAttachment" with which to work. This event may be useful to track
changes made to an attachment but will not help us with the nature of
the modification. We need to look at additional events to get more
detailed information on that.
Pr ivate Sub IAttachmentEvents AttachmentMod i fied (
ByVal TheAttachment As At t achme nt)
Debug.Print "A t tachme ntModified"
End Sub

BEFOREATTACH EVENT
Pr i vate Sub IAttac hme ntE vents_BeforeAttach(FileName As Str i ng . _
AllowA tt ac hment As Boo l ea n )

The BeforeAttach Event is triggered before an attachment takes place.


We are supplied with the FileName and given the opportunity to cancel
the attachment by setting the AllowAttachment parameter to False.
Pr ivat e Sub IAttachm en t Events_Be f oreA t tac h( Fil e Name As Stri ng.
All owAttac hme nt As Boo l ea n)
If Ge t Att r( FileName) And vbReadOn l y Th en
MsgBox "Attaching Read-Only Files is Prohibited. "
AllowAttachme nt = Fa l se
End If
End Sub

In this example, if the file that is being attached is set as ReadOnly, we


show a MessageBox stating that attaching read-only files is prohibited.
After the MessageBox, we set the AllowAttachment parameter to False.
This means the user is not shown the attachment dialog box. If an
attempt is made to attach a file and the file is not read-only, we do
nothing and the Attach File procedure continues.

526

I Chapter 23: Responding to MicroStation Attachment Events I


BEFOREDETACH EVENT
Private Sub

IAt~achmentEvents_BeforeDetach(ByVal

TheAttachment As

Attach~ent)

The last event we need to discuss is the BeforeDetach Event. Again, an


attachment object is provided to us as a parameter of the event. This
event is triggered just prior to the AfterDetach event.
We do not have the ability to keep a detachment from occurring. So, one
thing we can do is log the' fact that the detachment took place.
Private Sub IAttachmentEvents_BeforeDetach(ByVal
TheAttachment As Attachmen t )
Dim FF ile As Long
FF ile = Fr ee File
Open "C: \M i croStat i on VBA\Detac hmen tL og . txt " For Append As #F File

Pr i nt #FFile , Now & vb Tab & App l icat i on .U serNa me & _


vb Tab & TheAttac hment.DesignFile.FullName
Cl ose #FFi l e
End Sub

In this example, we write to a log file. We capture the date and time, the
user who is detaching the file, and the full name of the file being
detached.

REVI EW
The ability to attach files to an existing design file is powerful. It allows
us to design more quickly and with fewer errors. Accuracy is improved
because we can look at an entire design at one time. Do the walls line up
with the foundation? Using the IAttachmentEvents Interface allows us
to intercept events so we can track which files are being attached,
detached, and modified.

24

Model Events
The last chapter dealt with Attachment Events; this one deals with
model events. Two separate interfaces expose Model-related events:
IModelActivateEvents and IModelChangeEvents. We will implement
both interfaces in the same class module, clsModelEvents.
In this chapter:
[B The AfterActivate event
[B The BeforeActivate event
[B The ModelChange event

Implementing each of the interfaces in one class module makes it easy to


identify the order in which these events are triggered. Here is the code in
our class module clsModelEvents:
Implements IModelActivateEvents
Implements IModelChange Events
Private Sub IModelActivateEvents_AfterActivate(ByVa l TheModel
As ModelReference)
Debug.Print "AfterActivate: " & vbTab & vbTab & _
TheModel.DesignFile.Name & vbTab & TheModel.Name
End Sub

527

528

I Chapter 24: Model Events I


Pri vate Sub IM ode lAc ti vat eE vent s_Be fo re Ac tivate
( ByV al TheM odel As Model Reference )
Debug . Pr i nt "BeforeActivate : " & vbT ab & _
TheMode l. DesignFi l e . Name & vbTab & Th eModel.Name
End Sub
Priva t e Sub IModelChangeEvents_ModelChange(ByVal TheModel As _
ModelReference. ByVal Change As MsdModelChangeType)
Debug.Print "Change: " & vbTab & vbTab & vbTab &
TheModel . Des i gnFi 1e . Nam e & vbTab & TheModel . Name & " - " &
Mode lC hange(Change)
End Sub
Funct i on ModelChange(C hangeln As MsdModelChangeType) As String
Se l ect Case Changeln
' Active
Case Ms dMode lChangeType.mdlModelChangeBeforeActive
ModelChange = "BeforeActive "
Case MsdModelChangeType.mdlModelChangeActive
ModelChange = "Active"
' Create
Case MsdModelChangeType.mdlModelChangeBeforeCreate
ModelChange = "BeforeCreate "
Case MsdModelChangeType.mdlModelChangeCreate
ModelChange = "Create "
' Delete
Case MsdModelChangeTy pe.mdlModelCha ngeBeforeDelete
Mode lCh ang e = "B efore Del ete "
Case MsdMode l Change Type . md l Mode l Change Dele t e
ModelC hange = "Delete "
' Name
Case MsdMode l ChangeType.mdlModelChangeBeforeName
ModelChange = "BeforeName "
Case MsdMode l ChangeType . mdl ModelChangeName
ModelChange = "Name "
' Properties
Case MsdModelChangeType . mdlModelChangeBef orePr operties
ModelChang e = "Bef oreProperties "

I Model Events I

529

Case Ms dMode lChange Type.m dlMo de lCh angeP roperties


Mode lC hange = "Properties "
'Settings
Case MsdModelChangeType.mdlMode l ChangeBeforeSettings
ModelChange = "BeforeSet t ings "
Case MsdMod el ChangeType . md lM odelChangeSett i ngs
Mode lC hange = "Settings"
' UnCreate
Case MsdModelChangeType.mdlModelChangeBeforeUnCreate
ModelChange = "Be foreUnCreate "
Case MsdModelChangeType.mdlModelChangeUnCreate
ModelChange = "UnCreate "
' UnDe l ete
Case MsdModelChangeType.mdlModelChangeBeforeUnDelete
ModelChange = "8ef oreUnDelete "
Case MsdModelChangeType.mdlModelChangeUnDelete
ModelChange = "UnDelete"
' PropagateAnnotationScale
Case
MsdModelChangeType.mdlModelChangePropagateAnnotationScale
ModelChange = "P ropagateAnnotationSca l e "
End Select
End Function

The function "ModelChange" receives a constant value and converts it


to text. This makes it easier to see what is taking place. As with other
interfaces, the class module must be instantiated in a code module:
Pr i vate myME As cl sModelEvents
Sub AddEvents ( )
RemoveEvents
Set myME = New cl sModelEvents
AddMode l ActivateEventsHandler myME
AddModelChangeEventsHandler myME
End Sub

530

I Chapter 24: Model Events I


Sub RemoveEvents( )
If myME I s Not hing = False The n
RemoveModelActivateEventsHandler myME
RemoveModelChangeEventsHandler myME
Set myME = Nothing
End If
End Su b

When a model is switched in a design file, we see the followi ng results in


the Immediate window:

Af te rAc ti va te :
Change :
Be fo reActiva t e:
Change:
AfterActivate:
Change:

f l angedval ve .dgn
fl angedval ve.dgn
f l ange dvalve . dgn
f l angedvalve.dgn
fl angedvalve .dgn
flangedvalve.dgn
flangedvalve.dgn
flangedvalve.dgn

Untitled Sheet - BeforeActive


Untitled Sheet
Untitl ed Sheet - Activel
Model -l
Model-l - BeforeActive
Model-l
Model-l - Active

The order of events are:


IMode l ActivateEvents_BeforeActivate
IModelChangeEve nts_ModelChange (With Be f oreAc t ive Eve nt )
IModelActivateEvents_AfterActivate
IModelChangeEvents_ModelChange (W i th Active Event)

Each event supplies a ModelReference so we know which model is about


to be activated. The ModelChange event is triggered twice when the
active model is being changed. First, we get a Mo delChange event with
the "BeforeActivate" constant and then a ModelChange event with an
''Active'' constant.

REVIEW
Like other events, model events are implemented through an interface.
Something as simple as adding Debug.Print statements can help us
understand how, when, and why events are triggered. A little creativity
and some VBA know-how can result in the creation of powerful
solutions.

25

Level Events
Levels are so important to the organization of a design file that an
interface is provided just for Level events.
The ILevelChangeEvents interface exposes only one event which
handles these twelve types of changes:
[B

AfterChangeActive

[B

AfterCreate

[B

After Delete

[B

BeforeChangeActive

[B

BeforeDelete

[B

ChangeAttribute

[B

ChangeCode

[B

ChangeDisplay

[B

ChangeName

[B

ChangeName

[B

Change Parent

[B

TableRedo

[B

TableUndo
531

532

I Chapter 25: Level Events I


Although the change type names are self-explanatory, we had better
look at the actual event before continuing.
Private Sub ILevelChangeEvents_LevelChanged( _
ByVal ChangeType As MsdLevelChangeType, _
ByVal The Level As Level , _
ByVa l TheModel As Model Reference)
End Sub

Three parameters are passed to us in the LevelChanged event. The first


gives us the type of change. This is provided in the form of an
enumeration constant. Values for the ChangeType parameter are:

MsdLevelChangeType.msdLevelChangeAfterChangeActive = 9
MsdLevelCh ange Typ e. msd Lev elCh ange Af t erCreate = 2
Msd LevelChangeType.msdLevelChan ge AfterDelet e = 3
Msd Level Ch ang eType .msdLe ve l Change Befo re ChangeAct i ve 17
MsdLeve lC hangeType .ms dLevelC hangeBef oreDelet e = 18
MsdLevelCh angeType.msd LevelChange Cha nge At tr i but e 8
MsdLevelC ha ngeType .msdLeve l Chan geChan geCod e = 5
MsdLevelCha ngeType .msdLev elCha ngeChan ge Displ ay = 7
MsdLeve l ChangeType .msdL eve lChangeCh ange Na me = 4
MsdLeve lC hangeType.msdL evelChangeChang ePar ent = 6
Msd LevelC hang eType. msdL eve l Chan geTab l eRe do 15
Msd Level Chan geType .msdLevelCh angeTabl eUndo
14
Let's begin by using a function to convert the constant provided to a
string and a simple Debug.Print statement. This allows us to implement
the interface and experiment with it to see when and how Level events
are handled.
I mp l ements ILevelC hangeEvents
'be fore change act i ve shows the ol d level name
Pr i vate Sub ILeve l Change Eve nts _L eve l Changed( _
ByVal Chan ge Type As Msd LevelChangeType , _
By Val TheLeve l As Level , _
ByVal TheModel As Mode l Ref erence)

I Level Events I

533

Oebug .P r int TheLevel.Name & vbTab & _


TheMode l. OesignFi le. Name &
vbTab & TheModel.Name & vbTab & GetChan ge Typ e (Chang eType)
End Sub
GetChangeType(ChangeIn As Msd LevelChangeType) As String
Select Case Change I n
Case Ms dLe ve lCha ngeTy pe. msdL evelC hang eAft erCh angeAc ti ve
GetChangeType = "Afte rChangeActive"
Case M sdLevelC h angeType.~sdL e ve l ChangeAfterCreate
GetChangeType = "AfterCreate"
Case MsdLevelChangeType.msdLevelChangeAfterOelete
GetChangeType = "AfterOelete"
Case MsdLeve l ChangeType . msdLeve l ChangeBeforeChangeActive
GetChangeType = "BeforeChangeActive"
Case MsdLevelChangeType.msdLevelC hangeBeforeOelete
GetChangeType = "BeforeOe l ete "
Case MsdLevelChangeType.msdLevelChangeChangeAttribute
GetChange Type = "ChangeAttribute "
Case Msd LevelChangeType.msdLeve l ChangeChangeCode
GetChange Type = "ChangeCode"
Case MsdLevelChange Type.msdLevelChangeChangeOisplay
GetC hangeTy pe = "ChangeO i sp l ay "
Case MsdLevelChangeType.msdLeve l ChangeChangeName
GetChangeType = "ChangeName"
Case MsdLeve l ChangeType.msd Leve l ChangeChangeParent
GetChangeType = "ChangeParent "
Case MsdLevelChangeType.msdLevelChangeTab l eRedo
GetChangeType = "ChangeTableRedo "
Case MsdLevelChangeType.msdLeve l ChangeTableUndo
GetChangeType = "Chang eTableUndo "
End Select
End Function
Functio~

The following code is placed in a code module. It is used to call up the


class "clsLevelEvents':
Private myLC As clsLevelEvents
Sub Add Events ( )
Set myLC = New clsLevelEvents
AddLevelChangeEventsHa ndler myLC
End Sub

534

I Chapter 25: Level Events I


Sub Remov eEvent s()
If myLC Is Notring = False T~en
RemoveLevelChangeEventsHandler myLC
Set myLC = No t hi ng
End I f
End Sub

Running AddEvents adds the class clsLevelEvents to the


LevelChangeEventsHandler. Now, if we make modifications to levels in
MicroStation design files, we see lines added to the Immediate window
reflecting the events.
Since we can only have one LevelChanged event in a single class
module, the best way to implement this event is by using a Select Case
statement.
Pr i vate Sub I LevelChangeEvents_LevelChanged( _
ByVal ChangeType As MsdLevelChangeType, _
ByVa l TheLeve l As Level, _
ByVal TheMode l As Model Reference)
Select Case ChangeType
Case MsdLevelChangeType .msdLeve l ChangeAfterChangeActive

Case MsdLevelChangeType.msdLevelChangeAfterCreate
Case MsdLevelChangeType.msdLevelChangeAfterDelete
Case Ms dL evel Chan ge Type .m sd Level Cha nge Bef oreCha ngeA ct i ve

Case Msd LevelChangeType.msdLevelChangeBeforeDelete


Case MsdLeve l ChangeType . msdLevelChangeChangeAttribute
Case MsdLevelChangeType.msdLevelChangeChangeCode
Case Msd Leve l Cha ng eType . msdLeve l ChangeC hangeDisplay
Case MsdLeve l ChangeType . msdLevelChangeChangeName
Case Msd Leve l ChangeType . msdLevelChangeChangeParent
Case MsdLevelChange Type . msd LevelChangeTab l eRedo
Case MsdLevelChangeType . msdLevelChangeTableUndo
End Select
End Sub

Now we are ready to populate our Select Case sLaLement to deal with the
Level changes. We cannot prohibit changes in this interface; we can only
react to the events.
Let's look at some useful events in this interface.

IThe Active Event I

535

THE ACTIVE EVENT


Case

~sdLevelC~angeType.msdLevelChcngeAfterC~angeActive

Dim FFile As Long


FFile = FreeF i le
Open "c : \levelact i vated.txt " For Append As #FFile
Pr i nt #FFil e . Now & vbT ab & _
The Lev el . Name & vbTa b &
TheM odel . Na me & vb Tab &
TheModel.DesignFile . FullName
Cl ose #FF i l e

This keeps a simple log of when the Active Level is changed recording
date/time, name of the level, name of the model, and the design file's full
name.

THE AFTERCREATE EVENT


Case MsdLeve l Change Type . msd Leve l ChangeAfterCreate
Dim FFile2 As Long
FFi l e2 = FreeF i le
Open "c : \levelcreated.txt " For Append As #FFile2
Print #FFile2. Now & vbTab & _
Appl i cation . UserName & vbTab &
The Level . Name & vbTab &
TheModel . Name & vbTab &
TheM odel . Des i gnF i 1e. Full Na me
Close #FF i le2

Knowing that a level was created is helpful. Knowing who created it can
be critical. This event is triggered after a level is created. A log file is
created/appended showing a Date/Time stamp, the user who created the
level and other miscellaneous information.

THE AFTERDELETE EVENT


Case MsdLeve l ChangeTy pe . msd Lev elChangeAfterDelete
MsgBox "Level " & The Level . Name & " has been de l eted ."

By the time this event is triggered, very little can be done with the level,
however we can still get its name.

536

I Chapter 25: Level Events I


THE BEFORECHANGEACTIVE EVENT
Case MsdLevelChangeType.msdLevelChangeBeforeChangeActive
Debug.Print " Level """ & TheLevel.Name & _
""" i s about to be deact i vated ."

The name of this event suggests that we are being told which level is
about to become active. Not so - we are being told which level is about
to become deactivated. We do not know which level is about to become
activated until the AfterChangeActive Event.

THE BEFOREDELETE EVENT


Case MsdLevelChangeType.msdLevelChangeBeforeDelete
Debu g . Pri nt

" Bef o re Dele t e:

" & TheLevel.N ame & v bCr &

vbTab & TheLeve l .Descri ptio n

Before a level is deleted, we can gather a little more information. After it


is deleted, the only thing we can get is the Name property. In this
example, we are pulling the Description and the Name.

THE CHANGEATTRIBUTE EVENT


Case MsdLevelC hangeType.msd Leve l ChangeChangeAttribute
Debug. Pr i nt

" Change Attri bu te:

" & The Level . Na me & v bCr & _

vbTab & TheLeve l .Descr i pti on & vbCr & _


vbTab & The Leve l .IsAct i ve & vbC r &
vbTab & TheLevel.E l ementColor

Levels have a large number of attributes. In this example, we are only


looking at four of them.

REVIEW
Every model uses levels as they are critical to organizing our design files
and models. Knowing when levels are modified can be helpful especially
when we are dealing with standards.

26

Change Track Events


The last interface exposing MicroStation events that we will look at in
this book is the "Change Track Events" interface which exposes four
events: BeginUndoRedo, ElementChanged, FinishUndoRedo, and
Mark. We will look at two of those.

In this chapter:
[B

The BeginUndoRedo event

[B

The ElementChanged event

BEGINUNDOREDO EVENT
Private Sub IChange TrackEvents_BeginUndoRedo( _
ByVal AfterUndoRedo As El ement. _
ByVal BeforeUndoRedo As Element. _
ByVal Act i on As MsdChangeTrackAction.
ByVal IsUndo As Boo l ean)
End Sub

The Begin Undo Redo event is triggered before any undo or redo.
'AfterUndoRedo' points to the element with all of its properties after the
Undo or Redo action. The 'BeforeUndoRedo' parameter points to the
element with all of its properties before those actions. The 'Action'

537

538

I Chapter 26: Change Track Events I


parameter tells which type of action has taken place. 'IsUndo' helps us
know whether the action was an Undo or a Redo. Let's look at some
code in this event:
Private Sub IChange TrackEvents _Beg in UndoRedo( _
By Val Af t e rUndoR edo As El ement. _
ByVal BeforeUndoRed o As Element. _
ByVal Actio n As MsdChang eTrackAction.
By Val I sUndo As Boolean)
Debug.Print AfterUndoRedo .L evel . Na me & vb Tab &
BeforeUndoRedo . Level . Name & vbTab &
Action & vb Tab & Is Undo
End Sub

In this example we write the Level Names of the elements that are
modified, Action, and IsUndo parameters to the Immediate window.
For example, if we change the level of an element, and then issue an
Undo and then a Redo, we see the following lines in the Immediate
window:
Ner.J Level (0)
Default 3
Default NelJ Level (0)
3

True
False

l:'.'~
i~

,Vii
....
{ ."
\,;1

We can see the After and Before Level names, the Type (3), and the fact
that the first action was an Undo and the next one was a Redo (based on
True and False values). The Level names and Undo/Redo values make
perfect sense. But what about the number 3? It tells us the Type of event.
Right? The value points to an item in the MsdChangeTrackAction
enumeration.

msdC hangeT ra ckAc ti onAdd = 2


msdChangeTrackActionAppData = 8
msdC hangeTrackAc ti on Delete 1
msrlC hange TrackAc ti on Drop = 6
ms dChangeTrackAct i on Mark = 7
msdChangeT rackAc ti onModelAdd = 9
msdChangeTrackActionModelDelete 10
msdChangeTrackActio nModify = 3

I Element Chang ed Event I

539

msdCh angeTrackAct i onModifyFence = 5


msdChangeTrackActionNewFile PositionAndModify = 4
We see Undo/Redo actions on a modification to an elem ent. Keep in
mind that the constants in this enumeration are used in other areas of
the Change Track Events interface as well as the Begin UndoRedo event.

ELEMENT CHANGED EVENT


Private SUD ICha ngeTrack Events_ElementChangedC
ByVal AfterChange As Element . _
ByVa l BeforeChange As Element. _
ByVal Action As MsdChangeTrackAction.
CantBeUndone As Boolean)
End Sub

Whenever an element changes, the ElementChanged event is triggered.


The parameters provided in this event are very similar to the
BeginUndoRedo event. We are given a reference to the element before
and after the change is made, the type of action, and whether the action
can be undone.
Let's look at several implementations of the ElementChanged event:
Pri vate Sub IChange Track Eve nts _E l eme nt ChangedC _
ByVa l AfterCh ange As Element. _
ByVal BeforeChange As Element. _
ByVal Action As MsdChangeTrackAction.
CantBeUndone As Boolean)
Debug.Print BeforeChange.Level.Name & vbTab &
AfterChange. Level.Name & vbTab &
Action & vbTab & CantBeUndone
End Sub

540

I Chapter 26: Change Track Events I


The code is simple and straightforward. When an element is modified,
we are given the element before and after it is modified, the type of
action, and whether or not the action can be undone.

Default NeTJ Level


NeTJ Level
NeTJ Level

(0)
(1)

False

NeTJ Level
Default 3

(0)

(1)
3
False

False

.'' '1
-

Here an element's level was changed from "Default" to "New Level (0)"
to "New Level (1)" and back to "Default". Each action type is 3 and the
actions can be undone because the "CantBe Undone" parameter is
passed to us with a value of False.
The Change Type is 3. A review of the constants in the
MsdChangeTrackAction enumerator reveals that we are dealing with a
"msdChangeTrackActionModify" action.
Let's build on the event by adding more code:
Pr i vat e Sub ICh angeT r ac kEv en t s_ El ement Changed(
By Val Af terC hange As El ement . _
ByVa l Be f or eC hange As Ele ment . _
ByVal Act i on As MsdChange Tr ackAct i on . _
CantB eUnd one As Boolean)
Selec t Case Appl i cation.CommandState.CommandName
Case "Delete Element "
Debug.Print "De l ete El ement "
Debug . Pri nt vbTab & BeforeChange. Le vel . Name & vbTab &
Act i on & vb Tab & CantBeUndone
Case Else
Debug.Print App l i cation.CommandState.CommandName
Debug. Pri nt vbTab & BeforeChange. Level. Name & vbTab &
AfterChange . Leve l . Name & vbTab &
Action & vbTab & CantBeUndone
End Select
End Su b

I Element Changed Event I

541

Peeking at the CommandState.CommandName property can give us a


hint as to what was taking place just prior to the element's change.

o
NelJ Level (0 )
Drag Select. i on
NelJ Level (0)
NelJ Level (0)
Element. Select.ion
Defau lt. Default 3
Fa l se
Delete Element
NelJ Level (0)
Fa l se

'I

Fa l se
False

<,

>

We see we encountered a "msdChangeTrackActionNewFilePosition_


AndModify" (4), a "msdChangeTrackActionModify" (3), and a
"msdChangeTrackActionDelete" (1), In the above example, we based
our actions on the CommandName property of the CommandState
object. When we saw a "Delete Element" CommandName, we only
showed the level of the 'BeforeChange' object because after an item is
deleted, the 'AfterChange' object is set to "Nothing" and the object has
been deleted.
Basing our reactions to events on a CommandName may work in some
circumstances but basing our reactions on the ''Action'' produces more
reliable results.
Priv ate Sub IChangeTrackEvents_ElementChanged(
ByVal AfterChange As Element, _
ByVal Befo reChange As Element, _
ByVal Act i on As MsdChangeTra c kAction,
CantBeUndone As Boolean)
Se l ect Case Action
Case msdChangeTrackAct i onAdd
Case msdC hange Tr ackActio nAppData
Case msdChangeTrackAct i on Delete
Cas e ms dChange Tr ac kAc tion Dr op
Case msdC hangeT r ackAc ti onM ark
Case msdChangeTrackAct i onMode l Add
Case msdC hange TrackActionModelDe l ete
Case msdChangeTrackAct i onModify
Case msdChangeTrackActionModifyFence
Case msdChange TrackActionNewFilePositionAndMod i fy
End Select
End Sub

542

I Chapter 26: Change Track Events I


We can now build on this framework. Let's look at a few examples.

Example 1
Private Sub IChangeTrackEvents_ElementChanged(
ByVal AfterChange As El ement. _
ByVal BeforeChange As Element. _
ByVal Action As MsdChangeTrackAction.
CantBeUndone As Boolean)
Select Case Act i on
Case msdChangeTrackActionAdd
Case msdChangeTrackActionAppData
Case msdChangeTrackActionDelete
Case msdChangeTrackActionDrop
Case msdChangeTrackActionMark
Case msdChangeTrackActionMo de lAdd
Case msdChangeTrackActionModelDelete
Case msdChangeTrackActionModify
Select Case AfterChange.Type
Case MsdElementType.msdEle mentType TextNode. _
Ms dElementType.msdElementTypeText
If AfterChange.Level . Name <> "TEXT" Then
AfterChange . Level ~ _
Ac t i veDe s i 9 n Fi 1e . Le vel s ( .. TE XT" )

AfterChange.Rewr i te
End If
End Select
Case msdChangeTrackActionModify Fen ce
Case msdChangeTrackActionNewFilePositio nA ndModify
End Select
End Sub

When an element is modified, we check to see if it is a TextNode or Text


element. If it is one of these types of elements, we check what level the
element is on. If it is not on the "TEXT" level, we place it on the "TEXT"
level and Rewrite it so the change is saved to the file.

I Element Changed Event I

543

Example 2
Private Sub IChangeTrackEvents_E lementCha ng ed (
ByVa 1 AfterChar.ge As El ement. _
ByVal BeforeChange As Element. _
ByVal Action As MsdChangeTrackAction.
CantBeUndone As Boolean)
Select Case Action
Case msdChangeTrackActionAdd
Debug . Print "Add : " & AfterChange.Type & vbTab &
AfterC hange . ID. High & vb Tab &
AfterChange . ID.Low
Case msdChangeTrackActionAppData
Cas e msdC hange Tra ckActi onDe le t e
Case msdChangeTrackActionDrop
Case msdChangeTrackActionMark
Case msdChangeTrackActionModelAdd
Case msdChangeTrackActionModelDelete
Case msdChangeTrackActionModify
Case msdChangeTrackActionModifyFence
Case msdChangeTrackActionNewFilePositionAndModify
End Select
End Sub

Add :
Ad d:
Ad d:

15 0
6 0
'I
0

13 6
13 7
138

(: J

Knowing what type of element is added can come in handy. Let's add a
function so we can see the type of element as a description instead of a
number. We will modify the event to use this new function.
Private Sub IChangeTrackEvents_E l ementChanged (
ByVal AfterChange As El ement . _
ByVal BeforeChange As El ement . _
ByVal Action As MsdChangeTrackAction .
CantBeUndone As Boolean)
Select Case Action
Case msdChangeTrackActionAdd

544

I Chapter 26: Change Track Events I


Debug.Print "Add: " & GetType(AfterChange.Type) _
& vbTab &
AfterCrange.ID.High & vbTab &
AfterChange . ID.Low
Case msdChangeTrackAct i onAppData
Case msdChangeTrackAct i onDele t e
Case msdChangeTrack Ac tionDr op
Case msdChangeTra ck ActionMark
Case msdC han geTrack Act ion Model Add
Case msd ChangeTr ackActionModelDelet e
Case msdChangeTrackActionModify
Case msdChangeTrackActionModifyFence
Case msdChangeTrackActionNewFilePositionAndModify
End Select
End Sub
Function GetType (Typeln As MsdElementType) As String
Select Case Typeln
Case msdElementTypeArc
GetType = "Arc "
Case msdElementTypeBsplineBoundary
GetType = "BSplineBoundary "
Case msdElementTypeBsplineCurve
GetType = "BSplineCurve"
Case msdElementTypeBsplineKnot
GetType = "BSplineKnot"
Case msdElementTypeBsp l inePole
GetType = "BSp li nePole"
Case msdElement TypeBsplineSurface
GetType = "BSplineSurface "
Case msdElementTypeBsplineWeight
GetType = "BSplineWeight"
Case msdElementTypeCellHeader
GetType = "CellHeader "
Case msdElementTypeCellLibraryHeader
GetType = "CellLibraryHeader"
Case msdElementTypeComplexShape
GetType = "ComplexShape "
Case msdEle mentTypeComplexString
GetType = "ComplexString"
Case msdElementTypeCone
GetType = "Cone"

I Element Changed Event I


Case msd Eleme nt Typ eConic
GetType = "Conic "
Case msdElementTypeCurve
GetType = "Curve"
Case msdElementTypeDesignFileHeader
GetType = "Desig nF ileHea der "
Case msdEle ment TypeDgnStoreC om ponent
GetType = "D gnStoreComponent "
Case msdElementTypeDgnStoreHeader
GetType = "D gnStoreHeader"
Case msdElementTypeDigSetData
GetType = "Di gSetData "
Case msdE l ementTypeDimension
GetType = "Dimension"
Case msdElementTypeEllipse
GetType = "Ellipse"
Case msdElementTypeGroupData
GetType = "GroupData"
Case msdElementTypeleve lM ask
GetType = "levelMask "
Case msdElementTypelevelSymbolog y
GetType = "level Symbology "
Case msdElementTypel i ne
GetType = "li ne "
Case msdE l ementTypel i neString
GetType = "li neString "
Case msdElement TypeMatr i xDoubleData
GetType = "MatrixDoubleData"
Case msdElementTypeMatrixHeader
GetType = "Matr i xHeader"
Case msdElementTypeMatrixlntegerData
GetType = "Matr i xl ntegerData "
Case msdElementTypeMeshHeader
GetType = "MeshHeader "
Case msdElementTypeMicroStation
GetType = "Mi croStation "
Case msdElementTypeMult i line
GetType = "Multiline"
Case msdElementTypeNamedGroupComponent
GetType = "N amedGroupComponent "
Case msdElementTypeNamedGroupHeader

545

546

I Chapter 26: Change Track Events I


GetType = "NamedGroupHeader "
Case msdElementTypePointString
GetType = "PointString"
Case msdElementTypeKasterComponent
GetType = "RasterComponent"
Case msdElementTypeRasterFrame
GetType = "Ra sterFrame "
Case msdElementTypeRasterHeader
GetType = "RasterHeader "
Case msdElementTypeRasterReference
GetType = "RasterReference"
Case msdElementTypeRasterReferenceComponent
GetType = "RasterReferenceComponent "
Case msdElementTypeReferenceAttachment
GetType = "ReferenceAttachment "
Case msdElement TypeReferenceOverr i de
GetType = "ReferenceOverride"
Case msdElementTypeShape
GetType = "Shape "
Case msdElementTypeS haredCel l
GetType = "SharedCe l l "
Case msd El ement TypeSharedCe l lDef i ni t i on
GetType = "SharedCel l Defin i tion "
Case msdElementTypeSo lid
GetType = "So li d"
Case msdE l ement TypeSurface
GetType = "Surface"
Case msdElementTypeTable
GetType = "Table "
Case msdElementTypeTableEntry
GetType = "T ableEntry "
Case msdElementTypeTag
GetType = "Tag "
Case msdElementTypeText
GetType = "T ext "
Case msdElementTypeTextNode
GetType = "T extNode "
Case msdElementTypeView
GetType = "View "
Case msdElementTypeViewGroup
GetType = "ViewGroup "

I Element Changed Event I

547

End Sele ct
End Func ti on

Add:
Add:
Add:
Add :
Add :
Add:
Add:
Add :
Add:
Add:

<

LineString
Line
0
Line
0
Line
0
Line
0
Curve 0
Shape 0
Ellipse
Ellipse
Text
0

0
139
140
141
142
1 43
144
145
0
146
0
147
148

-[

>:

Example 3
When
an
element
is
deleted,
'msdChangeTrackActionDelete'.

the

action

recorded

Private Sub ICha ngeTrackEvents_ElementChanged( _


ByVal AfterChange As El eme nt . _
ByVal BeforeChange As Element . _
ByVal Act i on As MsdChangeTrackAction.
CantBeUndo ne As Boolean)
Select Case Action
Case msdChangeTrackActionAdd
Case msdC hangeTrackA ctionAp pD ata
Case msdChangeTrackActionDe l ete
Debug .P rint GetType(BeforeChange . Type) & " Deleted "
Case msdC hangeTrackAct i onDrop
Case msdChan geTrackActio nMark
Case msdChangeTrackAct i onModelAdd
Case msdChange TrackActionModelDelete
Case msdChange Trac kAct i on Modify
Case msdChangeTrackActionModifyFence
Case msdChangeTrackAct i onNewFilePositionAndModify
End Selec t
End Sub

is

548

I Chapter 26: Change Track Events I

Text Deleted
Shape Deleted
Ellipse Deleted
Line Deleted
Line Deleted

<1: J

When an element is deleted, we only have access to the 'BeforeChange'


element.

Example 4
We had several examples using the 'Action' parameter instead of
counting on the 'CommandName: Our next example uses both.
Private Sub IChangeTrackEvents_ElementChanged(
ByVal AfterChange As Element. _
ByVal BeforeChange As Element. _
ByVal Action As MsdChangeTrackAction.
CantBeUndone As Boolean)
Dim my Level As Level
Dim LevelCount As Long
Select Case Action
Case ms dChange TrackAct i onAdd
Case msdChangeTrackActionAppData
Case msdChangeTrackActionDelete
Case msdChangeTrackActionDrop
Case msdChangeTrackActionMark
Case msdChange TrackActionModelAdd
Case msdChangeTrackAct i onModelDelete
Case msdChangeTrackActionModify
Case msdChangeTrackActionModifyFence
Case msdChangeTrackActionNewFilePositionAndModify
Select Case GetType(AfterChange . Type)
Case "Table"
Select Case CommandState.CommandName
Case "New Level "
LevelCount

ActiveDesignFile.Levels.Count

I Activating the ChangeTrackEvents Interface I

549

Se t myLev el = _
Ac t iv eDesign File .L ev els( Leve l Coun t - 1)

Debug.Pr int myL eve l .Na me & " Added . "


End Select
End Select
End Selec t
End Sub

NelJ
NelJ
NelJ
NelJ
NelJ
NelJ
NelJ
Ne lJ
NelJ
NelJ

Level
Lev e l
Leve l
Level
Level
Level
Level
Level
Level
Level

Added.
Add ed.
Added .
Added.
Added.
Added.
Added.
(34) Added.
( 35) Added.
(35 ) Added.

(27)
(28 )
(29)
( 3 0)
(31 )
(32 )
(3 3 )

~ IY J

The 'NewFilePositionAndModify' change type tracks modifications to


the design file and non-element objects such as levels. In this example,
we use the CommandName property of the CommandState object in
addition to the 'Action' parameter so we know when a level is added or
deleted from the file. For more information on level modiHcations,
review the ILevelChangeEvents interface.

ACTIVATING THE CHANGETRACKEvENTS INTERFACE


As with other interfaces, we implement each of the interface's events in a
class module and then add the event handler. Run the code that adds the
Event Handler from within a code module as follows:
Pr i vate myC TE As clsChange Track Events
Sub Add Events ( )
RemoveEvents
Set myCTE = New clsChangeTrack Events
AddChangeTrackEventsHandle r myC TE
End Sub
Sub RemoveEvents ()
If myCTE Is Noth i ng

False Then

550

I Chapter 26: Change Track Events I


RemoveChangeTrackEventsHandler myCTE
Set myCTE = Nothing
End If
:::nd Sub

REVIEW
You can use the ChangeTrackEvents interface for a thousand different
purposes. Fortunately, it usually takes only a little experimentation
before you can implement it and get the results you need.

27

Non-Graphical Info -

Databases
We can store data about design file elements in a database instead of in
design files. This makes the design file smaller and the file element
information available outside of the MicroStation environment.
This chapter focuses on MicroStation's Database Link functionality. A
later chapter discusses additional ways to work with databases.
In this chapter:
[8

Linking MicroStation elements to databases

[8

Creating a Microsoft Access database from scratch to link to a


design file

[8

Using UDL (Universal Data Link) files

[8

Linking elements in MicroStation to a database using VBA

[8

Creating database records using SQL

[8

Creating a user interface to view database information

551

552

I Chapter 27: Non-Graphical Info - Databases I

How MICROSTATION 'UNK' ELEMENTS TO DATABASES


MicroStation allows us to link an element to a database record by
attaching a DatabaseLink" object to the element. Databases contain
tables, each table contain rows (or records), and each row contains
columns. The DatabaseLink object contains the information necessary
for MicroStation to find the table and row that is linked to the element.
The DatabaseLink properties that store these pieces of information are
the EntityNumber" and the Mslink" properties.

EntnyNumber
[sin formation
Mslink

.. -

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

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

.1-

At the top of this image is a DatabaseLink object that is attached to an


element in a design file. Let's look at the DatabaseLink's EntityNumber
property.
We use the EntityNumber of a DatabaseLink to help determine to which
table the DatabaseLink is pointing. The EntityNumber value is numeric,
so it does not tell us which table to look in, only how to find the table to
look in. Each database that is used with DatabaseLinks has an
mscatalog" table, which matches the DatabaseLink.EntityNumber
property with the database table we need. When we find a matching
entitynum" value in the mscatalog" table, the tablename" field tells
the table of the record.
When we know the table, we look there for a matching mslink" value.

I How MicroStation 'link' elements to Databases I

553

In the above example, we found a DatabaseLink obj ect attached to an


element in MicroStation. The EntityNumber of the DatabaseLink object
is 18. In the "mscatalog" table, the EntityNumber 18 points to the table
named "parcel". In the "parcel" table, we found a value of 531 in the
"mslink" field which provides the match between the element in
MicroStation to a record (or row) in the database.
This is how MicroStation links elements to database records. If m ore
than one record is linked to an element, multiple DatabaseLink objects
will be attached to the element in MicroStation.
We discussed the theory behind linking elements in MicroStation to
records in databases. Let's look at an example.

Open the file ... \Examp /es\Civi/\Ogn \cogo.dgn. This file is installed
with MicroStation.

2 Zoom in to the upper-left corner of the file.

In the file and the illustration above, we looked at Parcel Number 13


on Hayden Drive.

3 Now, let's open the database file


.. . \Examp/es\Oatabase\O/edb\Examp/es\Access\gis.mdb in

554

I Chapter 27: Non-Graphical Info - Databases I


Microsoft Access. This is the database that is attached to this file. A
review of the "mscatalog" table shows the database tables in this
Access database with which MicroStation can work. The "parcel"
table shows the records we just looked at.
We looked at how MicroStation links elements in design files to records
in databases. You can get more information in MicroStation's help file by
searching for "Database". Many of the topics relate to using
MicroStation's database tools, but we will concentrate on VBA
programming tools in this chapter.

CREATING A DATABASE FROM SCRATCH


MicroStation supports Oracle, ODBC, OLEDB, and SQL Server
(through the BUDBC Database Server Selection). We use an OLEDB
connection with a Microsoft Access database. OLEDB drivers are
installed as part of the Microsoft Windows operating system, so we will
use these drivers to communicate with the database and to create a new
database.
Let's create a new Microsoft Access database. Since this book is about
programming, I will demonstrate how you can do this even if Microsoft
Access is not installed on your computer. First the code and then an
explanation.
Sub CreateDB()
Dim myCat As New ADOX.Catalog
Dim dbLocation As String
Dim ConnString As String
dbLoca t ion = "C: \MicroS t at ion VBA\DatabaseLinkTest . mdb "
ConnS t r i ng = "Provider=M i croso f t . Jet.OLEDB.4.0 ; Data Source= " &

dbLocat i on
myCat . Create ConnStr i ng
End Sub

This code uses the "Microsoft ADO Ext. 2.8 for DDL and Security"
Reference. Before executing the procedure, you need to add this
Reference in VBA. This code creates a new database. The database is
empty; there are no tables in it. Our next step is creating tables.
Although there are many ways to add tables to an existing database, we
will discuss three of them.

I Creating a Database from Scratch I

555

One way to add tables to an existing database is to use the same ADOX
reference we just used to create the database.
Sub CreateDB2()
Dim myCat As New ADOX.Catalog
Dim dbLocation As String
Dim ConnString As String
dbLocation = "C:\MicroStation VBA\DatabaseLinkTest . mdb "
ConnString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" &
dbLocation
myCat . ActiveConnection = ConnString
Dim myTable As New Table
Dim myColumn As ADOX . Column
myTable.Name = "Lots "
'mslink Colu mn
myTable . Columns.Append "mslink " , adlnteger
' Owner Co lu mn
Set myCo lumn = New ADOX . Column
myColumn.Name = "Owner"
myColumn.Type = adVarWChar
myColumn.Attr i butes = adColNu l lable
myColumn . DefinedSize = 50
myTable.Columns . Append myColumn
' Sold Column
Set myCo lumn = New ADOX . Column
myColumn.Name = "S old "
myColumn.Type = adBoolean
myTable.Columns . Append myColumn
'DateSold Column
Set myColumn = New ADOX . Column
myColumn.Name = "DateSold "
myColumn.Type = adDate
myColumn . Attributes = adColNullable
myTable.Col umns.Append myColumn
' Acres Column
Set myColumn = New ADOX . Column
myColumn.Name = "Acres "

556

I Chapter 27: Non-Graph ica l Info - Databases I


myColumn.Type = adDouble
myColumn.Attributes = adColNullable
myTaJ l e.Columns.Append myColu~n
'Sa l eAmount Column
Set myColumn = New ADOX.Column
myColumn . Name = "SaleAmount "
myColumn . Type = adCurrency
myColumn.Attributes = adColNullable
myTable.Columns.Append myColumn
myCat.Tables.Append myTable
myCat.ActiveConnection = Nothing
End Sub

When we create the "mslink" column, we append the columns collection


in-line. When we create a column in-line, we do not have as much
control over the properties of the column (field). For example, you may
or may not want a field (column) to be required. If you do not want a
column to be required, you specify the Field's Attributes property as
<adColNullable'. This means the column can have a value of <null'.
The second way you can create a table is to use the "ActiveX Data
Objects Library" reference. You need to add a new reference before
running the procedure Cr eateDB3. When you open the references dialog
box to add "ActiveX Data Objects" as a reference, you may see several
libraries to choose from. You should select the highest version of the
library available.
In this example, we will execute an SQL Statement on the database to

create a new table named "Expenses". Here is the example:


Sub CreateDB3()
Dim myDB As New ADODB.Connection
Dim dbLocation As String
Dim ConnString As String
dbL oc ation = "C: \ MicroStati on VBA\DatabaseLinkTest.mdb "
ConnString = "P rov ider=Microsoft.Jet.O LEDB .4.0;Data Source=" &
dbLocation
myDB.Open ConnString
myDB.Execute "Create Table Expenses (mslink Coun ter, " & _
"LotLink Long, Description Char(lOO) , Amount Double) "
End Sub

I Creating a Database from Scratch I

557

When I created the mslink field, I gave it a 'Counter' type. This causes
the field to be an auto-number field that automatically assigns a numeric
value to this field each time a new record is created. The value for this
field begins with one (1) and increases by one each time a new record is
created. The auto-number field type is not available in all databases but
it is available in Microsoft Access databases.
The last method uses the MicroStation Visual SQL Query Builder. Use
the Query Builder to get information out of databases that are
connected to MicroStation and to execute standard SQL statements on a
connected database.
Before using the Query Builder, connect to the database. Before
MicroStation can connect to a database, it needs to know the type of
database and where the database is located. One of the best ways to
provide this information is through a UDL file.
In MicroStation, select the menu Settings> Database> Connect.

Q.esign File",
level

loc!>s
Cam!lt'a

B.endering

2.naps
'/jew Attributes

Q.uery Builder

Verify l,inkages

Ctrl+B

558

I Chapter 27: Non-Graphical Info - Databases I


The Database Connection dialog box opens:
~l'-:'

(,','" , ~

}~"

~"~'

. '"

,~,,~

!S;~~!IJ;5!, !!'~~~!~~~.s,e"

... ~~

':;V'..;,",

"'.,
'. , ..," J"

After selecting OLEDB from the


database server, select New from
the Database Source combo box.

,.~;'J?t7: tY1f~~;0?:''','~~-'r.,:r_~ Sij,,~ 'f;~~f ,<,~"n:;"" w~

Cancel

:!JP~ w[l!~P~T,~/ '~'!Xi;,~~~,<~ '~v)f;' h~~Ni~t~.i1'~~'ti~: ~y;

I~O~L~ED~8;====='~1

Browse

i@:c.I;J~~:~~~::j I 0 atabaseLink Test

"

~I

.Q.atabase Server: [ OLEDB

BUDBe Provider:

When you select New, a new UDL file is created and you are asked to
enter the UDL file parameters.

i
I
!

ProY-ider

ll~nnec~Advanced 11.6:11

Select the

~~~~~~ to_~~~:~~~.:. .

. .~E DB

want

I __._ ______ _

'"O;ml

________ ______

t
'll
!
f.. !

pro~lder~"~"~"~,.,,~~._,,._ _ _ _ l.~!

Connectivity Service Provider


MediaCatalogDB OLE DB Provider
MediaCatalogMergedDB OLE DB Provider
MediaCatalogWebDB OLE DB Provider
Microsoft ISAM 1.1 OLE DB Provider
Microsoft Jet3.51 OLE DB Provider
MicrosoftJei4.0 81: DBProvider ,', ' ': "
Microsoft OLE DB Provider For Data Mining Services
Microsoft OLE DB Provider for Indexing Service
Microsoft OLE DB Provider for Internet Publishing
Microsoft OLE DB Provider for ODBe Drivers
Microsoft OLE DB Provider for OLAP Services
Microsoft OLE DB Provider for OLAP Services 8.0
Microsoft OLE DB Provider for-Oracle
Microsoft OLE DB Provider for Outlook Search
Microsoft OLE DB Provider for SQL Server
Microsoft OLE DB Simple Provider

i~

:'.<l

r.21

OK

il l

Cancel

I[

q
:,

.1

,~

!::lext

i'

I.

Help

\Vhat tvv'O critical pieces of information do we need to provide to


MicroStation? First, the type of Database. You specify the type of
database by selecting the OLE DB Provider (database driver) . When
working with a Microsoft Access database, select "Microsoft Jet 4.0 OLE
DB Provider" from the list. Click the Next button to continue.

I Creating a Database from Scratch I

Specify the following to connect to Access data:


1. Select or enter a database name:

L---------..JIct1
2. Enter information to log on to the database:
User name:

P.o:o.'J,:,d:

l~.?~':. ...........__._. __. _..___.._._ . . _.__.J


L___._.__. __._________.___._~

o Blank password

D Allow saving password

559
The Connection tab gives
us the ability to provide
information regarding the
location of the database to
which we want to connect.
The interface in the
Connection tab changes
based on the selection in
the Provider tab.

When working with a Jet


(Microsoft Access) database, you can type the file
name or browse for the file
by clicking the button as
[ Test Connection I
shown. If you are working
SQL
Server
with
a
OK
.11 Cancel I [ Help
database, you are asked to
select the SQL Server
Name (it can be any server on the network that has SQL Server
installed) and then select the database to connect to.
Parameters also allow us to enter a User name and Password if the
database requires these.
After you click the Browse button, you can select the Access database
you just created. After selecting the file, the file path displays in the UDL
Properties dialog box.

560

I Chapter 27: Non-Graphical Info - Databases I


Use the TestConnection button to see if you have provided the UDL file
with enough information to connect to the selected database.
~ Co~nection ~~~.!:1~~] AII _ L____"__ _ _~_
Specify the following to connect to Access data:
1. Select or enter a database name:

~;;;i~-;;-VBA\D-;ta~~-;;;"db---I O
2. Enter information to log on to the database:

User name: Admin

Pa'i','}v~!'Jd:

~===============~

L I_ _ __

o Blank password

_ _ __

_ _- - - '

Allow saving password

~ . T~~i C:~~n~qij~n

I[

OK

Test connection succeeded.

Cancel

I[

Help

If you provided enough information, you get


a "Test connection succeeded" message. This
is the MessageBox we want to see. From time
to time, however, a different MessageBox
displays.

For example, if you select the wrong provider


for the selected database, you see an error message. Or, if the database
you selected is open exclusively somewhere else, you will see an error.
After clicking OK in
the Test Connection
MessageBox, click OK
in the UDL dialog box.
If all goes well, you will
see this MessageBox:

~"--'- 1

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

tl)for~~~ion .

" ..... ;:: - ~.

~'X.

" ,"."., ':

'7"> "':

'~~.I "

. .' "

-<}~ ,,~

f."='

,..',

: P"~

No MSCATALOG table exists .


Attachments of database data to graphical elements will not be
()
)

possible.
To create an MSCATALOG table, go to
Settings- >Database->Setup.

o Do not display again.

;.

I Creating a Database from Scratch I

561

MicroStation depends on a table named "MSCatalog" to associate


DatabaseLink objects with database records. Since we have not created
an MSCatalog table in the database we just attached to MicroStation, we
are notified that this table needs to be created and are given a hint how
we can do that within MicroStation.
Let's
follow
the
directions specified
in this MessageBox
by going to the menu

~.

"_,,

_ w_
~

_"'

"'"

Alert
There is no MSCATALOG table, Create one now?

Settings > Database


> Setup where we see

this MessageBox:

Click OK to have MicroStation create the MSCatalog table.


Next, we see the Database Setup dialog box. We will deal with this in a
little while. For now, close it by clicking the OK button.
When MicroStation creates the MSCatalog table, it may need to detach
and then re-attach it to the database, so you may see the UDL file
properties again. If this happens, make sure that the correct 'Provider'
and 'Connection' settings are made prior to closing the UDL File
Properties dialog box.
OK. We just performed a number of steps to connect our
DatabaseLinkTest.mdb database to MicroStation. Now that the database
is connected, we can create a new table by using the SQL Builder
(Settings> Database> Query Builder).

___f,.<l.':!!'E-l,~~~_Q,,!-,,,!.e~ __ Settings ()utput


Sele~i I Where 1 C,wlIPf)y'l OrderBy 1

Functions:
TaQ!es

SQL 5 elect 5 tatement

i Value .9_1 _ __

_ ___ ~J

o Dataset Mode
o Execute Query on Open
o No Duplicate Rows
o Append to Dataset
Execute
Clear All
Clear Text

562

I Chapter 27: Non-Graphical Info - Databases I


You can use the Visual SQL Query Builder dialog to get information
from a connected database. When we retrieve information from a
database, we often use the "Select" SQL statement. So, the box we type in
is titled "SQL Select Statement". But you can do much more than simply
select database records in this textbox.
When you type SQL statements in the "SQL Select Statement" textbox
and click the Execute button, the statement entered is executed by the
connected database. Let's create a new table named "History" by
executing a SQL Statement. The History table will store historic
ownership data.
CREATE TABLE History (mslink Inleger. Owner Char(50). PurchaseD ale
Dale. SoldO ale Dale)

Clear 8)1
Clear Texl

CREATE TABL E History (ms l ink Integer , Owner Char(50), PurchaseDate


Date , SoldDate Date)

Here is the statement to create our new


History table. After clicking the
Execute button, the new table is created
(if the SQL statement is correct). How
do we know if it has been created? We
can click the Tables button.
If the History table was not created, it
do es not show up in the "Select Table"
list and we know that the SQL
Statement we used needs to be
modified to work properly.

:":~} ,~'JZ~f't,Y~\.7!4-:W r',

<""::. . .~"""'~t<, Y"~ri1"

oc_

~!t~ck~r\~!!7"~~' .';'~;'~!(',"f.0~'!.,,';1.j;l:;fJ~;:Z,g<' ~,
Expenses
Hislory!'-.
Lois 1'1)
mscalalog

vi

Table List: All


Qose

I Using UDL Files I

563

To display the History table in the Query Builder dialog box, doubleclicking on "History" in the list then click the Close button.
Select

1Where I '

r':

I Order8y I

History

These are the fields


created in th e
History t able.

mslink
Owner
PurchaseD ate
SoldD ate

0
~

I-

USING UDL FILES


Thus far, we discussed the mechanism behind MicroStation's linking of
design elements to database records and we created a new Access
database. While creating the Access database, we created a new UDL file
that has two primary components: a provider and a connection that
specify the database driver and database location.
One of the great things about UDL files is that if the location of the
database changes, we make one change in one file (the UDL file) and the
programs we wrote and the MicroStation links we created work
perfectly. We can even scale up the database platform from Microsoft
Access to SQL Server or Oracle and, with only a couple of simple
changes to the UDL file, our programs will continue to function.
Let's take one more look at a procedure we ran earlier:
Sub CreateDB3 ()
Di m myDB As New ADODB . Connection
Dim dbL ocati on As Str i ng
Di m ConnString As Str i ng
db Locat i on = "C: \MicroS t ation VBA\DatabaseLink Test . mdb "
Con nString = "Provider=Mi crosoft . Jet . OL EDB . 4 .0; Data Source= " &
dbLocat i on
myDB . Open ConnString
myDB.Execute "Create Table Expenses (mslink Counter. " & _
"LotLink Long . Description Char(lOO). Amount Double) "
End Sub

564

I Chapter 27: Non-Graphical Info - Databases I


In this procedure, we provide a "Location" and a "Connection String" in
the code to connect to a database at a specific location. This
demonstrates that it is possible to work with databases without a UDL
file. But what happens if the database's location or file name changes?
You need to change the code if you have hard-coded the database
connection information in your programs. If you are the only one using
the program, this is not much of a problem. But if multiple people are
using your program, it is much easier to change a UDL file than it is to
change code, especially if you have locked the code in VBA. You can
instruct even the most basic computer user to double-click on a UDL
file and browse for a different database.
We will discuss UDL file usage in greater detail in a later chapter but for
now, just keep in mind that using a UDL file provides the perfect
combination of power and flexibility.

LI NKI NG MICROSTATION ELEMENTS TO DATABASE


RECORDS
The process of linking a MicroStation Element to a Database Record is
simple. Here is the declaration for "CreateDatabaseLink":
Function CreateDatabase Link (Ms li nk As Long.
Ent i ty As Long . _
Li nkType As MsdDatabaseLinkage. _
IsInformation As Boolean. _
DisplayableAttributeType As Long) As DatabaseLink

And here is an example using CreateDatabaseLink:


Sub DatabaseLinkA()
Dim myElem As Element
Dim myLink As DatabaseLink
Set myElem = CommandState.GetLocatedElement(True)
Set myLink = CreateDatabaseLink(l . 1. msdDatabaseLinkageOleDb. _
True. 0)
myElem . ArlrlDatabaseLink myLink
myEl em. Rewrite
End Sub

After the DatabaseLink is created, add it to an element in MicroStation.

I Creating Database Records using SQL I

565

Note that you can create Database Links and add them to elements
without a database being attached. In the example above, we create a
Database Link with an MSLink of 1 (one) and an Entity of 1 (one).
Remember, the Entity property points to the table we are to look in (by
referencing the Entitynum field in the MSCatalog Table) and the Mslink
property points to the record in the table.
This code assumes that the MSCatalog table has a record with an
"Entitynum" of 1 and that the table it references has a record with an
"MSLink" of 1.
The following code looks at the selected element in MicroStation and
displays each DatabaseLink attached to it with the Database Link's
properties in a MessageBox:

256

a
1

True

Sub GetDatabaseLinksA ()
Di m myElem As Element
Dim myLinks() As DatabaseL i nk
Dim myLink As DatabaseL i nk
Dim I As Long
Set myElem = CommandState.GetLocatedE l ement(True)
myLinks = myElem.GetDatabaseLinks
For I = LBound(myLinks) To UBound(myLinks)
Set myLink = myL ink s(I)
MsgBox myLink.DatabaseType & vbCr & _
myLink.DisplayableAttributeType & vbCr &
myL i nk . Enti t yNumber & vbCr & _
myLink . Islnformat i on & vbCr &
my Lin k . Ms 1ink
Next
End Sub

CREATING DATABASE RECORDS USING

SQL

Thus far we successfully created a new database, added new tables to the
new database, and know how to attach DatabaseLinks to elements in
MicroStation. The only thing we are missing in our database is data! We
need to know how to add records to the database we created. You do this

566

I Chapter 27: Non-Graphical Info - Databases I


by using the same SQL Query Builder used to add a table to the
database. Let's add a record to the "Lots" table.
SQL Select Statement
IN SERT INTO Lots [mslink, Owner I VALUE S [1, ~ ones Family'l

Execute
Dear 811

------------------~

The SQL statement is:


INSERT INTO Lots (mslink, Owner) VALUES (1. ' Jones Family')

In this example, we create a new record in the table "Lots". The fields
"mslink" and "Owner" are given values of "I" and "Jones Family"
respectively. You can add fields and values by placing them in the
appropriate places. For more information on SQL Statements, see
''Additional Sources" at the end of this book.

CREATING A USER INTERFACE TO VIEW DATABASE


INFORMATION
<,
,=
:Qw!1er~,hip Infof~atjon ,
~W ~"':",'"

'7' -*~<

>-""-~f1~""

Owner

I Jones Family

Sold

-:~-

'"

~~

" f@

Date Sold
Acres
Sale Amount

Let's capitalize on what we now know


about Database Links by creating a user
interface to view and edit "Lot"
information.
Here is our interface. The TextBoxes and
CheckBox display associated information
from the fields in the table "Lots".

Here is the code behind the interface:


Canst UOLFile As String = "C:\Oocuments and Settings\All Users\" & _
"Application Oata \ Documents \ Bentley \ Wor kSpace \ " &
"Projects\Examp l es\Oatabase\Oledb\Udl\" &
"OatabaseLinkTest . udl "
Di m MS Li nkI D As Long

I Creating a User Interface to view Database Information I

567

Private Sub bt nCl os e_Cl i ck()


Un l oad Me
End Sub
Pri vate Sub btnUpdate_Cl i ck( )
Di m myDB As New ADODB . Co nnect i on
Dim myRS As New ADODB.Recordse t
myDB.Open "File name= " & UDLFile
myRS . Open "Select * from Lots Where MSLink = " & MSLinkID . _
myDB . adOpenForwardOn l y . adLockOp ti mist i c
If myRS . EOF = Fa l se Then
If txtOwner.Text <> "" Then
myRS( "Owner " )
txtOwner.Text
Else
myRS( "Owner " )
Null
End If
myRS("So l d") = chkSold.Va l ue
I f IsDate(txtDateSo l d.Text) Then
myRS( "DateSold " )
CDate(txtDateSold . Text)
Else
myRS( "DateSold " )
Null
End If
If Is Numer i c( t xt Ac re s.Tex t ) Th en
myRS( "Acres " )
CDbl(txtAcres . Text )
Else
my RS( "Ac res " )
Null
End If
If Is Numeric( t xtSa l eAmount . Text) The n
myRS( "Sa l eAmount " )
CDb l ( t xtSaleAmount. Text)
Else
myRS( "SaleAmount " )
Null
End I f
myRS . Update
End If

568

I Chapter 27: Non-Graphical Info - Databases I


End Sub
Private Sub UserForm_Initialize()
Dim myElem As Element
Dim my Lin ks ( ) As DatabaseLinK
Di m myL i nk As Datab ase Li nk
Dim I As Lo ng
Di m myDB As New ADODB.C onnect io n
Di m myRS As New ADODB . Recordset
myDB.Ope n "File name=" & UD LFile
Set myElem = CommandState.GetLocatedElement(True)
myLinks = myElem.GetDatabaseL i nks
For I = LBo un d(myL ink s) To UBound( myL i nks)
Se t my Lin k = myL i nks( I )
If my Li nk . Ent i ty Nu mbe r = 1 Then
myRS. Op en "Se l ect * from Lot s Wh ere ms link = " &
my Li nk .M s li nk, _
myDB , adOpe nF orwardOnly, adLockReadO nly
If myRS .E OF = Fal se Then
MSLink I D = myLink . Ms l ink
If IsNu l l( myRS( "Owner")) Then
txtOwne r. Text
El se
myRS( "Own e r" )
t xtOwner . Tex t
End If
chkSold.Value = myRS( "Sold " )
If IsNull(myRS( "DateSold " )) Then
txtDateSold.Text
El se
myRS( "DateSold " )
txtDateSold.Text
End If
If IsNul l (myRS("Acres")) Then
txtAcres.Text
Else
myRS( "Acres")
txtAcres.Text
End If

I Review I

569
If IsNulHmyRS( "SaleAmou nt " )) The n
txtSa l eAmount.Te xt
El se
txtSa'eAmount.Text
myRS("SaleAmount " )
End If

my RS.Cl os e
myDB.Close
Exit Sub
End If
myRS . Close
End I f
Next I
myDB.Close
End Sub

When the form is initialized, we look at the selected element in


MicroStation, then get the "Lot" information through the DatabaseLink
object. We display the information in the database and allow users to
change values in the TextBoxes and CheckBox. When a user clicks the
Update Database button, the database record is updated based on what
is entered in the form.

REVIEW
Databases store a large variety of information in tables. Database tables
have fields (or columns) defined in them and each record in a table can
have values in these fields.
DatabaseLink objects associate MicroStation elements with database
records. For DatabaseLinks to work with databases, the database must
have a table named "mscatalog". The DatabaseLink Objects are
associated with the specified table through the MSLink property.
Databases are discussed in more detail in a later chapter.

570

I Chapter 27: Non-Graphical Info - Databases I

28

Tags
Tags store and display information associated with elements in a design
file and to display the same type of information from file to file. The
data stored in each tag is different. For example, "Drawn By" is a useful
piece of information for every file but the actual value may vary from
file to file.
Use the macros in this chapter with the "Building" project that is
installed with MicroStation.

In this chapter:
lB Getting information from tags based on a selection
lB Getting all tags in a file
lB Working with tagsets
lB Getting all tags of all files in a folder
lB Changing a tag's value
lB Changing multiple tags in multiple files
lB Exporting tag information to a file

571

572

I Chapter 28: Tags I


GETTING INFORMATION FROM TAGS BASED ON A
SELECTION
Let's begin our look into the wonderful world of tags by having the user
(in this case it is probably us) select a tag and get some basic information
from it.
Sub GetSelectedTagA( )
Dim myTag As TagElement
Dim myE l emEnum As El ementEnumerator
Set myE le mEnum = _
Application . Act i veModelReference . GetSelectedElements
While myElemEnum.MoveNext
Select Case myE lem Enu m.C ur re nt.Type
Case Ms dEl ementType. msd El ement TypeTag
Set myTag = myEl emEnu m. Cur re nt
If myTag Is Nothin g = Fal se The n
MsgBox my Tag . Val ue
End If
End Se l ect
Wend
End Su b

In this example, we create an ElementEnumerator to look at each


element selected in MicroStation. We look at each selected element to
see if it is a tag. If it is, we display the tag's value in a MessageBox.
The tag's value is of great importance to us but there is more to a tag
than its value. Let's expand the macro to include the tag's name.
Sub GetSelectedTagB ()
Dim myTag As TagE l ement
Dim myElemEnum As ElementEnumerator
Set myElemEnum = _
Application.ActiveModelReference.GetSelectedElements
Whi l e myE l emEnum.MoveNext
Select Case myElemEnum . Current.Type
Case MsdElementType . msdElementTypeTag
Set myTag = myE l emEnum . Current
If myTag Is Nothing = False Then
Ms gB ox myTag . TagOefinitionName & vbTa b & myTag.Valu e
End If
End Select

I Getting Information from Tags based on a Selection I

573

Wend
En d Sub

You can use the tag's name to know if the tag's value tells us who created
the drawing, who checked the drawing, or who printed the drawing.
Get Se1ecte dTagB gives more information than the previous procedure
but can be improved upon. Let's get the tag's TagSetName.
Sub Get Selec t edT agC()
Dim myTag As TagElemen t
Di m myEl emEnum As Elemen t Enumerator
Set myEl emEnu m = _
App l icat i on . ActiveMo del Reference . GetSelectedEle ments
While myElemEnu m. MoveNext
Se l ect Case myE l emEnum . Current . Type
Case MsdElementType.msdElementTypeTag
Set myTag = myElemEnum . Cu rr ent
If myTag Is Noth in g = Fa l se Then
MsgBox myTag . TagSetName & vbTab &
myTag . TagDef i nit i onName & vb Tab & myTag . Value
End If
End Se l ect
Wend
En d Sub

The TagSet tells us to which group a tag belongs. For example, does the
tag belong to a title block? Does it belong to a door schedule?
At this point we are getting some very useful information. If four tags
are selected, we see four MessageBoxes. If one tag is selected, we see one
MessageBox. Let's expand our code to include all tags that belong to the
same TagSet.
In the next example, we display tag information and use the selected tag,
but we display all tags that are siblings to the selected tag. We do this by
getting the all tags belonging to the selected tag's BaseElement.
Sub GetSelectedTagD ()
Di m myTag As TagElement
Dim s i bTags() As TagElement
Dim myElemEnum As Element Enumerator
Dim I As Long
Set my ElemEnum = _
Application.ActiveModelReference . GetSelectedElements

574

I Chapter 28: Tags I


Wh il e myE l emEnum.MoveNext
Select Case myElemEnum.Current.Type
Case MsdElementType.msdElementTypeTag
Set myTag = myElemEnum.Current
sibTags = myTag.BaseElement.GetTags
For I = LBound(sibTags) To UBound(sibTags)
MsgBox s i bTags (I) . TagSetName _
& vbTab & sibTags(I) . TagDefinitionName
& vbTab & sibTags(I).Value
Next
Exit Sub
End Select
Wend
End Sub

We use GetTags on the selected Tag's BaseElement. GetTags fills an


array composed of tags belonging to an element. We use a For ... Next
loop to look at each tag's TagSetName, TagDefinitionName, and Value.

GETTING ALL TAGS IN A FILE


You saw how to extract tag information based on a selection in
MicroStation. Let's move from the selection method to scanning an
entire file for tag elements and displaying each tag's information in a
MessageBox.
Sub GetTagsA( )
Dim myTag As TagElement
Dim myElemEnum As ElementEnumerator
Dim myFilter As New ElementScanCriteria
myFilter.ExcludeAllTypes
myFilter.lncludeType msdElementTypeTag
Set myElemEnum = _
Application.ActiveModelReference.Scan(myFilter)
While myElemEnum . MoveNext
Set myTag = myElemEnum.Curren t
MsgBox myTag.TagSetName & vbTab & myTag.TagDefinitionName _
& vbTab & myTag.Value
Wend
End Sub

I Working with Tagsets I

575

After we create a new ElementScanCriteria object, we exclude all types


from the filter and then include the tag element type. The entire file is
scanned and only tag elements are enumerated. There are times when
you want the user to select a tag. Other times you want to process an
entire file without user intervention - both scenarios are presented
here.
I will discuss how to extract tag information into Microsoft Excel in a
later chapter.

WORKING WITH TAGSETS


Tags are grouped into TagSets. We have been showing each tag's
TagSetN arne property in the previous examples. Let's look at the active
design file's TagSets.
Sub GetTagsSetsA()
Dim myTagSet As TagSet
For Each myTagSet In Application.ActiveDesignFile.TagSets
MsgBox myTagS et.Name
Next
End Sub

Now that you know how to identify each TagSet in a file we can look at
each TagDefinition in each TagSet in a file.
Sub GetTagsSetsB()
Dim myTagSet As TagSet
Dim myTagDef As TagDefinition
For Each myTagSet In Application.ActiveDesignFile.TagSets
For Each myTagDe f In myTagSet.TagDefinitions
MsgBox myTagSet.Name & vbTab & myTagDef .Na me
Next
Nex t
End Sub

And now we will perform the same basic function, but we will display
more tag information:
Sub GetTagsSetsC()
Dim myTagSet As TagSet
Dim myTagDef As TagDefinition

576

I Chapter 28: Tags I


For Each myTagSet In App l ication.Act i veDesignFile.TagSets
For Each myTagDef In myTagSet . TagDefinitions
MsgBox "SetName: " & myTagSet.Name & vbCr & _
"TagName: " & myTagDef.Name & vbCr & _
"Prompt: " & myTagDef.Prompt & vbCr &
"Defa ult: " & myTagDef .DefaultVal ue
Next
Next
End Sub

GETTING ALL TAGS OF ALL FILES IN A FOLDER


You can get all tags in a file with only a few lines of code, but it can take
time to open each file in a specific folder and run a macro, such as
GetTagsSetsC. Especially if there are hundreds or even thousands of files.
Let's look at some code that opens each design file in a folder and
exports its tag information by printing it to the Immediate window in
VBA. Of course, writing the information to a file is much more useful,
so you will see an example of that later.
Before proceeding, you need to add a reference to the "Microsoft
Scripting Runtime". The file system object helps you traverse all files in a
folder.
Sub GetFolderTags()
Di m my DGN As Des i gnFi l e
Dim myFSO As New Scripting.FileSystemObject
Dim myFolder As Scripting. Folder
Dim myFile As Scripting.File
Dim myTagSet As TagSet
Dim myTagDef As TagDefinition
Dim TargetTagset As String
Dim myTag As TagElement
Dim myElemEnum As ElementEnumerator
Dim myFilter As New ElementScanCriteria
TargetTagset = "TitleBlock "
Set myFolder = myFSO.GetFolderC"C:\Documents and Settings\" & _

"All Users\Application Data \ " &


"Bentley\WorkSpace\Projects\ " &
"Examples\Building \Dgn ")
For Each myFile In myFolder.Files

I Getting All Tags of All Files in a Folder I

577

Select Case my File.Type


Case "Bent l ey MicroSta ti on Des i gn Fi l e "
Set myDGN = Application.OpenDesignFileForProgram( _
my Fi 1e . Pat h. rue)
For Each myTagSet In myDGN.TagSets
Select Case UCase(myTagSet.Name)
Case UCase(TargetTagset)
myFilter.ExcludeAllTypes
myFilter.IncludeType msdElementTypeTag
Set myElemEnum = _
myDGN . Models(l).Scan(myFilter)
While myE l emEnum.MoveNext
Set myTag = myElemEnum.Current
Debug.Print myFil e. Name & vbTab &
myTag.TagSetName & vb Tab & _
myTag.TagDefinitionName; vb Tab &
myTag.Val ue & vbTab & _
vbTa b & myTag.I D. Hi gh &_
vbTa b & myT ag. I D. Low
We nd
En d Select
Next
myD GN. Cl ose
End Se l ect
T

Next
End Sub

This example opens each design file in the specified folder "for
program". This means the file is opened in memory but is not displayed
in MicroStation. You can open and manipulate files very quickly when
they are not rendered to the screen.
We are extracting one piece of information in this example that we have
not extracted before: the High and Low elements of the ID property.
Th is ID property is very important because it provides a unique
identifier for an element in MicroStation that persists from session to
session. So, storing the ID property of an element in a database, for
example, allows us to quickly and easily identify the element in
MicroStation hours, days, weeks, or months after we first worked with it.

578

I Chapter 28: Tags I


CHANGING A TAG'S VALUE
Now we are going to create and use a procedure named ChangeTag. It
uses the High and Low elements of the ID property to get and set a tag's
value. Here's the procedure:
Sub Ch a ng eTag (IDHigh As Long. IDLow As Long. NewVa lue As String)
Dim TagID As DLo ng
Dim myTag As TagElement
TagID . High = IDHigh
TagI D. Low = ID Low
Se t my Tag = App l ication . Active DesignFile . GetElementByID(TagID)
myTag. Value = NewVa l ue
myTag.Rewrite
End Sub

When you have an ID, you can get its element by using the
GetEl ementBy I 0 procedure. After I set the tag's value based on the ID, I
rewrite the tag element. This procedure cannot be executed by itself. It
needs something else to call it. Let's look at a procedure that does that:
Sub TestChange TagA()
Dim myTag As Ta gEl ement
Di m myEnum As Ele me nt En umer ator
Set myEnum = ActiveModelReference.GetSelectedElements
While my Enum.M oveNext
Se l ect Case my Enum . Current.Type
Ca se Msd ElementType. msdE l ementType Tag
Change Tag myEn um. Cur rent .I D.High . _
my Enu m.Cur r en t. I D. Low . "ABC "
End Sel ect
Wend
En d Sub

In the real world, we store the High and Low elements of the ID
property in a database or in some other storage mechanism. In our
example, we get the ID property from selected elements, then use our
new procedure Change Tag to change the tag's value.

I Changing mUltiple Tags in Multiple Files I

579

CHANGING MULTIPLE TAGS IN MULTIPLE FILES


As you learn more about VBA programming, you discover that you can
make significant changes on a large scale in a short period of time. So,
you can do a lot of good or you can do a lot of damage with your code.
When you begin working with multiple files using VBA, use extreme
caution to make sure your changes are good.
Changing a tag in a single file is not nearly as exciting as changing
multiple tags in multiple files, nor is it as dangerous. The procedure you
will create, ChangeTag2 , allows you to specify fo ur parameters.
Sub ChangeTag2 (F i leName As St r ing . TagSet As Str i ng. _
TagName As String . NewValue As Str i ng)
Dim myDGN As DesignFi l e
Dim myFilter As New ElementScanCriteria
Dim myElemEnum As ElementEnumerator
Dim myTag As TagElement
Dim myModelRef As ModelReference
Set myDGN

Applicat i on.OpenDesignFileForProgramCFi l eName. Fa l se)

myFilter.ExcludeAllTypes
myFilter.IncludeType msdElementTypeTag
For Each myModelRef In myDGN.Models
Set myElemEnum = myModelRef . Scan(myFilter)
While myElemEnum.MoveNext
Set myTag = myElemEnum.Current
If StrC omp (myTa g.TagSe tN ame. TagSet) = 0 Then
If StrComp(myTag . TagDefinitionName . _
TagName) = 0 Then
myTag . Va l ue = NewVa l ue
myTag.Rewrite
End If
End If
Wend
Next
myDGN . Save
myDGN.Close
End Sub

In this procedure, I open the specified file "For Program", scan the file for
tags with a specific TagSet and TagName and set its value. Here's a
procedure that uses ChangeTag2 .

580

I Chapter 28: Tags I


Sub Tes tCh ange TagB()
Dim myFSO As New Scripting.FileSystemObject
Dim myFolder As Scripting.Folder
Dim myFile As Scripting.File
Set myFolder = myFSO.GetFolderC"C:\Oocuments and Settings\" & _
"Al l Users\App l ication Data\ " &
"Bent l ey\ Wor kSpace\Projects\ " &
"Exa mp les\Bui l di ng\Dgn " )
For Each myFile In myFolder.Files
Select Case myFile.Type
Case "Bentley MicroStation Desig n File"
ChangeTag2 myFile.Path, "T i tleBlock", _
"Checked By", "J Winters"
En d Se l ec t
Next
En d Sub

The above procedure changes each design file in the specified folder,
saves it, and gives all tags named "Checked By" in the TitleBlock TagSet
a value of "J Winters". Powerful? Yes. Dangerous? Potentially. Be careful
so that the programming you do benefits your employer instead of
derails your career.

EXPORTING TAG INFORMATION TO A FILE


All of the MessageBoxes and Debug.Print statements will not do us any
good because the data extracted and displayed is not in a format that can
be saved. We will begin with a simple ASCII Text file.

e Tags.lxt . Notepad . ,' ,


File

Edit

Format

.' .

. :

" , ' :, : .. ~

"

', " " ,, '

,.'

rJ['QJ~

View Help

bs; 3 Qoae2 01-e 1 evat; cns . dgn


bs; J OOae2 Ol-e 1 evat; cns . dgn
bsi JOOae201-el evat; cns. dgn
bsi JOOae201-el evat; ons. dgn
bs i JOOae201-el evat; cns. dgn
bs i 300ae201-e l evat; on5. dgn

bsi 3 aoae201-el evat; cns. dgn


bsi )OOae201-el evat; cns. dgn

bsi 300ae201-el evat; ons. dgn


bsi JooaeZ01-el evat; cns . dgn
bs i 3 DOae) 01-5 ect; cns . dgn
bsi JOOae301-secti ons. dgn

Ti t1 eel oek
Ti tl eBl oek
Ti t1 ee l oek
Ti tl eel ock
Ti tl eBl oek
T; tl eBl oek
Ti tl eel ock
Ti tl eel ock
Ti tl eBl ock
Ti tl eBl ock
Ti tl eBl ock
TitleBlock

(,!

Design File Ref


project File Ref
Date
Drawn By
Bent l ey
DRG. NO.

Rev.
Job No
scal e
T; tl e

AE201

1.0
BS1300
1: 100

South El evat i on

~~:~~~d F~\'e

Ref
project File Ref

11568
0
11574
0
0
11569
11566
11567
0
0
0

11573

11576
11570

11572
11575
4] 12
0

~I

'th,!
;;"1
:2

4316

~l
) .

...

Here is the output we want to create. It is the same information that we


printed to the Immediate window:
Sub ExportFolderTags ()

I Exporting Tag Information to a File I

581

Di m myD GN As Desig nFi l e


Dim myFSO As New Scripting.FileSystemObject
Dim myFolder As Scripting. Fol der
Dim my File As Scripting . File
Dim myTagS et As TagS et
Dim myTagDef As TagDefinition
Dim TargetTagset As String
Dim myTag As TagElement
Dim myElemEnum As ElementEnumerator
Dim myFilter As New ElementScanCriteria
Dim FFile As Long
FF il e = FreeFile
Open "C:\MicroStation VBA\Tags . txt " For Output As #FFile
TargetTagset = "Titl eBlock "
Set myFolder = myFSO . GetFolder("C:\Oo(uments and Settings\" & _
"Al l Users\Applicat i on Data\ " &
"Bent l ey\ WorkSpace\P r ojects\ " &
"Examp l es\Building\Dgn " )
For Ea c h my Fi 1e I n my Fold e r . Fi 1e s
Se l ect Case myF il e . Type
Case "Bentley Mi croStation Design Fi l e "
Set myDGN = App l icat i on . OpenDesignFi l e ForProgram( _
myFile.Path. True)
For Each my TagSe t In myDGN . TagSets
Select Case UCase(myTagSet.Name)
Case UCase(TargetTagset)
myFilter.ExcludeAllTypes
myFilter.IncludeType msdElementTypeTag
Set myElemEnum = _
myDGN. Models(l).Scan(myFilter)
While myElemEnum.MoveNext
Set myTag = myElemEnum.Current
Print #FF i le. myFile.Name & vbTab &
myTag.TagSetName & vbTab & _
myTag. TagDef initionName; vbTab & _
myTag.Value & vbTab &
vbTab & myTag.ID . High &_
vbTab & myTag . ID . Low
Wend
End Select
Next
my DG~I . Clos e

582

I Chapter 28: Tags I


End Se l ect
Next
Close #FF i l e
End Sub

Writing to a text file is simple. Of course, an ASCII .txt file is useful for
reviewing in Notepad but isn't formatted. Let's modify the above
example and, instead of creating a .txt file, create an .htm file.
Sub ExportFolderTagsToHTML ()
Dim myDGN As DesignFile
Di m myFSO As New Scripting.Fi l eSystemObject
Dim myFo l der As Scripting. Folder
Dim myFile As Script i ng.Fi l e
Di m myTagSet As TagSet
Dim myTagDef As TagDefinit i on
Dim TargetTagset As String
Dim myTag As TagElement
Dim my ElemEnum As ElementEnumerator
Dim myFilter As New ElementScanCriteria
Dim FFile As Long
FFi l e = Free Fi l e
Open "C:\MicroStation VBA\Tags.htm" For Output As #FFile
Pr i nt #FFile. "<table width=660 border=I> " & vbCr
Pr i nt #FFile. vbTab & "<tr><td></td></tr> " & vbCr
TargetTagset = "TitleBlock"
Set myFol der - myFSO .Ge t Folde r("C: \Documents and Set ti ngs\ " &
"All Users\Application Data\" &
"Bentley\WorkSpace\Projects\ " &
"Examples\Building\Dgn " )
For Each myFile In myFo l der.Fi l es
Select Case myFi l e.Type
Case "Bentley MicroStation Design File"
Print #FFile. "<tr><td colspan=5>" &
myFile.Path & "</td></tr>" & vbCr
Print #FFile. vbTab & "<tr><td>Tag Set Name(/td>" &
"<td>Tag Name</td>" & _
"<td>Value<!td>" &
"<td>ID Hi gh</td>" & _
"<td>ID Low(/td></tr> "
Set myDGN = Application.OpenDes i gnFile ForProgram (

I Exporting Tag Information to a File I

583

myFile.Path. True)
For Each myTagSet In myDGN.TagSets
Se l ect Case UCase(myTagSet.Na me )
Case UCase(TargetTagset)
my Fi 1t e r . Exc 1ude All Ty pes
my Fi lt er.I nc ludeType ms dElementT ype Tag
Set myEle mEnum = _
myDGN .M odels( l ).Scan (myF i lter)
Wh il e my El em En um . Mo veNex t
Set myTag = myElemEnum . Current
Print #FFi l e . vbT ab & "<t r ><td> " &
myTag.TagSetName & "< l td>" &
"( td)" & myTag .T agDefinitionName & _
"(ltd)" &
"(td)" & myTag.Val ue & "(l td ) " & _
"(td) " & myTag . IO.High & "(ltd) " & _
"(td)" & myTag . ID.Low & "(/td)(/tr) " & vbCr
Wend
End Select
Next
myDGN . Close
End Select
Next
Print #FFile. "</table>"
Close #FFile
End Sub

File

Edit

View

Favorites

-~

Tools

Help

~ t~ ;

search ' t r Favorites

e ,~ ' ,y~ ~

El

: Add",s l ~ C:\Microstation VBA\Tags,h~._____._

,C:\Documents and SettingsWl Users\AppJication


!Data\Bentley\W orkSpace\Projects\Examples\Building\Dgn\bsi300ae 10 1-plan. dgn
iTag Set Name

iTag Name

iValue

Ic
rn"--High
='- - jJD Low

iC:\Documents and SettingsWl Users\AppJication


:Data\B entley\W orkSpace\Projects\Examples\Building\Dgn\bsi300ae20 1 elevations. dgn

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

--- .--.------.... ----. . r--..----:---------.. .. r----------------.. --.... - !

[Tag Set Name


!Tag Name
!Value
'T;il;Bl~ ~k----- ----- .iDesig;:;F;i~Ref-----'-

10------- ii1-568-------!

iTitleBlock
ITitl~Bi~~k

iProject File Ref

. . .. . .. .. . . .!D~t~

iID High

iID Low

10

i11573

- ;O-- - - j 11570

[0

:Titl~Blo~k--- ---------- [I;;:;;;-~-By------------- rB~-;;tl~y ---------- -- --- ~ ----

TitleBlock
'~:;_~:Ol-=-=l-: ------------

;DRG NO.

'AE201

. "1i574.: I
. .----.. -.. ;1-1576'------- I

.....- ft>=:"
:-------..----.---.----.---~1-(;--------- ..--------1::;------------f;-~~.~.------- [

ifD~---------------

~ My Computer

v .

..

584

I Chapter 28: Tags I


A little HTML code is all it takes to display the data we are exporting
into a more visually pleasing and better organized format. And since
web browsers can be found on most computers independent of the
operating system, HTML is a standard format that can be read by nearly
everyone.

REVIEW
Tags contain useful information. The ability to access tags through VBA
gives us control over tags in the active design file but also in every file in
a specific folder and so forth. It is easy to read change values, as you have
just seen. Exporting tag data into ASCII files allows us to work with the
data or view it in other programs such as Notepad and a web browser.
I provide an example of extracting tag information into Microsoft Excel
in a later chapter. You could also extract tag information to a database or
use it as the body of an e-mail. You are only limited by your imagination.

29

XML
Imagine for a moment that you are tasked with the responsibility of
developing a new method of describing and housing data. This method
must be flexible enough to handle a great variety of data models, data
types, etc. It must be able to describe a car, a family of people, and a
farm. What would you come up with? Hopefully you would come up
with something like XML, because XML is designed to handle a variety
of data structures and types.

In this chapter:
[B

What is XML?

[B

XML file structure

[B

Reading XML files with VBA

WHAT IS

XML?

XML is an abbreviation for Extensible Markup Language. XML


documents often have a file extension of "xmI:' Even though XML files
conform to a common specification, the data they contain can vary
greatly from file to file and from structure to structure.
Companies and organizations create their own XML document
definitions to store their own type of data with their own data

585

586

I Chapter 29: XML I


structures. For example, there are XML documents to store financial
transactions, documents to store GIS information, and documents to
store architectural bills of material. XML formats have been devised to
store genealogy, sports statistics, and cooking recipes. Programs such as
Microsoft Excel and Microsoft Access can export their data to XML
files.

XML FILE STRUCTURE


The XML file structure allows it to be so flexible and powerful. Here is a
small snippet of an XML file:
<?xml version= "I .O " encoding= "UTF-8 "?>
<data root xmlns:od= "urn:schemas-microsoft-com:officedata"
generated= "2005 -12-28T08:57:09 ">
<parce l >
<mslink>I</mslink>
<old_map_no>119-L<lold_map_no>
<group_no>A</group_no>
<parce l _no>9</parcel_no>
9<lc l t no>
<clt_no> 11 9-LA
<owner>CANTRELL WILLIAM B &amp; EVELYN W<lowner>
<parc_value>9000</parc_va lu e>
<house_num>220</house_num>
<str_name>BR IDGEW ATER RD</str name>
<city>KNOXVILLE<lcity>
<state>TENNESSEE</state>
<zip_code>379 19<lz i p_code>
<county>K NOX<lcounty>
<district>NW -COUNTY</district>
<zone_class>RA<lzone_class>
<block_num>O</block_num>
<lot_num>O</lot_num>
<subd_name>CRESTWOOD HILLS UNIT l</subd_name>
<parc_area>16036</parc_area>
<perimeter>51 1</per i meter>
<mapid)6</mapid)
<currdate>1986-09-19TOO:OO:OO<lcurrdate>
<txt>This is a memo field. used for really l ong text entries</txt>

</parcel>

I Reading XML Files I

587

This XML file is an export from a Microsoft Access database. To be


more precise, it is an export from the gis.mdb file installed with
MicroStation. The "Parcel" table was exported to an XML file.
XML files are being used more and more in place of traditional
databases and, looking at the structure, it is easy to see why. You can see
nodes for City, State, Zip_Code, County, District, Pare-Area, Perimeter,
and so forth .

READING

XML FILES

There are several ways to read an XML file. You could use standard VBA
file Input/Output calls, reading each line of the XML file and parsing it,
but there is a better way.
Microsoft has given us some tools to work with XML files. The first step
is adding a reference to a Microsoft XML DLL file.

Available References:

OK

~I,'I

i [J ~licrosoft XML,

v2.6
v3.0
v4 .0
I[J Microsoft X~lL , vS.O
[J ~licrosoft X~lL, v6.0
Ilicrosoft X~lL, version 2.0
I' !cd ~licrosoft. Vsa.dll
; U ~licrosoft_JScript
.....,): Priority
![J ~licrosoft Vsa
I[J ~licrosoft - Vsa vb CodeDOMProcessor
U ~limeDir I~O Tipe Library
I[J mmAEPlugIn 1.0 Type Library
i
i [J ~lMC Internal Web Browser event sink 1.0 Type Librc .!

I[J Microsoft X~lL,


![J Microsoft X~lL,

IW

Cancel
Browse ...

Help

I 1"" 1, ~'11MFlltil
! .~/

1 ..~. Tvnp. I .ihr~rv


... '''' .. .'. '... 1

..;":!.!1
.>..1

w3ext 1.0 Type Library


Location :

C:\WINDOWS\5ystem32\inetsrv\w3ext.dll

Language:

Standard

Each computer has different versions of the Microsoft XML libraries. In


the example above, v6.0 is the highest version so I will use it in the
examples here.
Sub ReadXMLFi 1 e ()
Di m myX ML As New MSXML2.DOMDocument
Di m myXElem As MSXML2 . IXMLDOMElement
Di m myXRecord As MSXML2 . IXMLDOME l ement
Dim myXF i el d As MSXML2. IX MLDO MElemen t

588

I Chapter 29: XML I


myXML.async = False
myXML.va li dateOnParse = False
myXML.Load "c:\MicroStation VBA\parcel.xml "
Set myXElem = myXML.documentElement
For Each myXRecord In myXElem.childNodes
Debug.Print "****NEW RECORD****"
For Each myXField In myXRecord.childNodes
Debug.Print myXField.baseName &" "& myXField.Text
Next
Next
End Sub

This example loads the XML file and then prints each record with its
children (the fields) in the Immediate window.
~~~*NEW RECORD~***

mslink .. 1
old_map_no .. 119-L
gJ:oup_no . A
paJ:cel_no . 9
clt_no .. 119-LA
9
owneJ:. . CANTRELL hlILLIAM B & EVELYN hl
paJ:c_value .. 9000
house_mUll . 220
stJ:_name .. BRIDGEhlATER RD
city . KNOXVILLE
state .. TENNESSEE
zip_code .. 37919
county KNOX
distJ:ict . . NW-COUNTY
zone_class .. RA
block_num .. 0
lot_num . . 0
subd_name .. CRESThlOOD HILLS UNIT 1
paJ:c_aJ:ea . 16036
peJ:imeteJ: .. 511
mapid .. 6
cUJ:J:date .. 1986-09-19TOO:OO:OO
txt . This is a memo field, used fOJ: J:eally long text entJ:ies

The procedure ReadXMLFi 1e starts at the beginning of the file and looks
at each item until it reaches the end.
If you only want to display the record with a mslink of 531, you could
use the following code:
Sub ReadXMLFi l eB ()
Dim myXML As New MSXMLZ.DOMD oc ument
Dim myXElem As MSXMLZ.IXMLDOMElement
Dim myXRecord As MSXMLZ .IXMLDOMElem ent
Dim myXField As MSXMLZ.IXMLDOMElement
myXML . async = False

I Read ing XML Files I

589

myXM L.valida t eOnPar se = False


my Xtv! L. Loa d "c: \ Mi c r 0 St at i on VBA\ par c e 1 . xm1 "
Set myXElem = myXML.documentEle~ent
For Eacr myXRecord In nyXElem.chi1dNodes
If myXRecord.childNodes(O) . Text = "531" Then
For Each myXFie'd In myXRecord.childNodes
" & myXField.Text
Debug.Print myXField.baseName &
Next
End If
Next
End Sub

In this example, I look at each record in the XML file and when I find
one with an mslink of 531, I print the record's fields to the Immediate
window. The code works to be sure, but it is not very efficient.
Let's look at this next example. Instead of looking at each and every
record in the XML file, I get only the mslink properties, look at them,
and then print each field in the Immediate window.
Sub ReadXMLFileC ()
Dim myXML As New MSXML2.DOMDocument
Dim myX Elem As MSXML2 .I XMLDOMElement
Dim myXList As MSXML2.IXMLDOMNodeList
Dim myXRecord As MSXML2 . IXMLDOMElement
Dim myXField As MSXML2 .I XMLDOME leme nt
myXML . async = False
myXML.va l idat eOnPar s e = False
myXML . Load "c : \MicroStat i on VBA\parcel.xml "
Set myX Elem = myXML . documentElement
Set myXList = myXML . getElementsByTagName( "mslink " )
For Each myXReco rd In myX Lis t
I f myXRecord.Text = "53 1" Then
For Each myX Fi el d In myXReco rd . parent Node . ch il dNodes
Debug.Print myXFie l d. baseName & " .. " & myXFie l d.Text
Next
End I f
Nex t
End Sub

590

I Chapter 29: XML I


The code runs faster than the previous example but can be improved by
looking for the first node that has an mslink value of 531.
Sub ReadXMLFileD ()
Di m myX ML As New MSXML2 .D OMD ocument
Dim myXFie l d As MSXML 2.IXMLDOMElement
Dim myXNode As MSXML2.IXMLDOMN ode
myXML.asyn c = False
myX ML.validateOnPa r se = False
myXML.Load "c: \MicroStation VBA\parcel.xml"
Set myX Ele m = myXML .d ocume ntElement
Set myXNode = myXML.selectSingleNode("//parcel[mslink=531] " )
For Each myXF ield In myXNode .c hildN odes
Deb ug . Pr i nt myXF i eld . baseName & " " & myXField . Text
Next
End Sub

Now we are getting somewhere by letting the Microsoft XML Library do


the work. It is fast, efficient, and very easy to modify. Not matter how
deep the parcel object is in the XML file, this code finds the first
instance where the mslink property of the parcel object is equal to 531.
Looking at the screen capture shown previously, you will see a good
number of fields to work with. One is "parc_value:' You could modify
the code above, looking for "parc_value" instead of "mslink" and find a
parcel valued at 250,000. But the code only works if a parcel is valued at
exactly 250,000. Let's examine the code that will return all parcels (not
only one) with a value greater than or equal to $250,000.
Sub ReadXMLFileE ()
Dim myXML As New MSXML2.DOMDocument
Di m myXList As MSXML2.IXMLDOMNodeList
Dim myXField As MSXML2.IXMLDOMElement
Dim myXNode As MSXML2.IXMLDOMNode
myXML.async = False
myXML . validateOnParse = False
myXML.Load "c: \M icr oStat i on VBA\parcel.xml"
Set myXList - myX ML. selectNodes("/ l parcel[pdrc_value>-250000] " )

Debug . Print myXList.Length & " Found ."


For Each myXNode In myXL i st
Debug . Print "*****PARCEL***** "
For Each myXF i eld In myXNode.childNode s

I Read ing XML Files I


Deb ug , Pr in t myXFiel d ,baseName & "

591
" &

myXField , Te xt

Next
Next
End Sub

81 FOWld ,
1; 1; 1; 1; 1;

PARCEL

1;1; 1; 1;

1:

ms link, , 7
old_map_no , , 119
p ar cel_no, , 34
c l t _no , , 119
34
O1J1ler. ,CGS LAND COHPAlN
parc_value , ,675000
s tr_name " P 0 BOX 10845
city, ,KNOXVILLE
state, ,TENNESSEE
zip_code,, 3791 9
COWlty, , KNOX
district " Nhl-COUNTY
zone_clas s "CA
block_num, , 0
lo t_num, , 0
parc_area , ,4822 06
perimeter , , 3170
mapid, ,6
currdate,,1 99 8-09-11TOO:OO: OO
txt, ,This is a memo field, used for re al l y long text entries

The code is beginning to look a little like an SQL statement but it really
is an XPath statement.
A careful comparison between the screen capture shown here and one a
few pages ago shows that this one is missing the "group_no" field and
the "house_num" field, In a previous example, I used childNodes(O) to
get the "mslink" field. I addressed the "mslink" field by an index number
instead of its name. You can see here that some records do not have all
fields represented, so you should be extremely careful when addressing
fields (or nodes) by their index - it is far better to address a field by its
name.
This next example displays information about each record where the
parcel area is greater than or equal to 1 acre.
Sub ReadXMLFileF ()
Dim myXML As New MSXML2 , DOMDocument
Dim myXList As MSXM L2, IXMLDO MNodeL i st
Dim myXField As MSXML2 . IXMLDOME l ement
Dim myXNode As MSXML2 . IXMLDOMNode
myXML , async = False
myXML , validateOnParse = False
my XML. Loa d "c: \ Mi c r 0 Stat ion VBA\ par eel , xm1 "

592

I Chapter 29: XML I


Set myX Li st = myXML .s elect Nodes( "/lp a rcel [p arc _a rea) =43560 ]" 1
Debug . Pr i nt myX List . Length & " Parcels Found )= 1 Acre ."
For Each myXNode In myXList
Debug.Print myXNode.selectSingleNodeC".llmslink"l.Text & vbTab &_
myXNode . selectSingleNodeC " .llowner " l.Text & vbTab & _
myXNode . selectSing l eNode(".llparc_area " ).Text
Next
End Sub

In the above example, I use selectNodes to select multiple nodes


meeting the criteria specified. I then look at each node that is found and
get its "mslink;' "owner;' and "parc_area" values. Note that I use ",/1"
with a period (.) instead of "I/". When I use a period, I state that I want
to look relative to the current context. If I did not use the period in the
above example, it would find the first "mslink", "owner", and "parc_area"
nodes in the XML document.
Let's look at one more example of reading this Access-exported XML
document.
Sub ReadXMLFileG ()
Dim myXML As New MSXML2.DOMDocument
Dim myX Lis t As MSXML2.IXMLDOMNodeList
Dim myXField As MSXML2.IXMLDOMElement
Dim myXNode As MSXML2.IXMLDOMNode
myXML.async = Fal se
myXML.validateOnParse = False
myXML. Load "c: \M i croSta t ion VBA \pa rce 1 . xm l "
Set myXL i st - myXML.sele ctNodes( "ll parce l [parc_a rea ) -43 560 " &

"and parc_area<=87 120] " )


Debug.P ri nt myXL i st . Lengt h & " Parce l s Fou nd Be t ween 1 and 2 Acres. "

Fo r Eac h myX Node In myXList


Debug . Pr i nt myXNode . se l ectSing l eNode( " . ll msl i nk " ).Text &_
vbTab &
myXNode . selectS i ng l eNode ( ". l lowner " ) . Text &_
vbTab &
myXNode . selectS i ng l eNode( ". llparc_area " l.Text
Next
End Sub

I Reading XML Files I

593

Now I expanded the filter to only return nodes with "parc_area" values
between 1 and 2 acres. I again export the "mslink", "owner': and
"parc_area" node values to the Immediate window.
60 Pa~ce13 Found Bet~een 1 and 2 Ac~es.
10 CRESTlJOOD DEV HlC
86657
36 lJHlSTOH HAROLD J & JERRY H 46475
68 RODGERS CADILLAC IHC
61295
71 SAHFORO GEORGE B
58874
76 VISSER REAL ESTATE IHVESTIIEHTS 53599
87 J B F COHPANY & lJILBURS 63518
90 STEPHENS lJILLIA}! J & KATIlERINE NULLA}IE 44372
94 DA}IIEL REALTY CORP % EASLEY- HCCALEB &
5900 3
100 lJAL-fIART PROPERTIES INC 65364
107 KINGTON CURTIS H & PATRICIA 52386
110 lIURRAY TED E & HCKINNEY JA}IE S HI CHAEL
56333
111 ADKINS BOYD L & HARRHlGTml GLEHARO F
50360
113 HUGLEY DAVID B & PEGGY C % UNIOH SECURITY f!ORTGAGE
114 ADKDIS BOYD L & HARRIHGTON GLEHARO F
47912
139 CAIH LILLIE HAE LSD OHEGA E~rrpRS DIC
74120
144 CADI LILLIE l1AE 46608
1 49 CADI LILLIE lIAE LEASED lJE STS IDE IHC 50262
156 SCHUBERT f! A TR 69869
164 FIRST AlIERICA}1 NATIOHAL BANK
82497
184 CAHI LILLIE flAE LEASED OlIEGA ENTPRS HlC 5 9782
187 ROBERTS LARRY S & SUZAHNE
75045

44071

As mentioned previously, XML documents have a specific structure.


The XML file I have been working with is an export from Microsoft
Access. Let's look now at an XML file exported from Microsoft Excel.
t:l~ Ella' ~t ' Il!elii ~ .!ns';,r( FQ,I'~t
r~J;);id"\~$.;(t.:s.:.:.~~<m *_

I~

0 lC3

rU~ ~~

"'""-<""

G; t91 (}

,~..

~I

4.~ 4~J"

..

,~,,,,,

""',

lo~ls ~ Qat~
~k~

,:" ~ I ~

l'lindow'

tlelp ,Ado!le PDF "

. ::/ I "1

.I.@,

... ~, . <.U( ~

~ ,lJ!b

,,",

,,~. $, _

('1
~
'"

.ZO

1: ,~~~J L~ ~l} , 100%


!

"

"rm'?

02

1
-- -,,'table Tension!
1200
2
l,7ico,lor
3 "r
Y":Eij ghtjft; ,
2
13 towers
4
TtN'
~OO ,_ '
5
1e""x+-IC""a:::-::b:-::
6
"I-I--.-::c=-:'::x:,,I-::-::-=:Yc'-I_ _ _~zJ..::1O""A'T7-=-:A:":SE,=,T,==-=,,I_ _Co..;ac::b-:o:
le::-:Y""p""a""bl::,:e:'7Z1
7
18325 22791
514 , xy= 1 8~25,2279
18290 ' 22929
894
8
18414 22609526xy~ l B414,??60
18379 22747
906
9
18427 222242' 47"'6~'
465 ,xy~ 1 ~42?,22~4;
18392 22582
845
10
184414}~/=lB441,?227
18406 22414
816
11
18461 22062
446:>y=1841,?206, "
18426 22200
826
12
18458 21940
420xy~ 1 8~5~,2 1 94
184?} 22078
800
13
18492 21852
343 X'>,=18492.2185
, 1~1??.. } 1~90
(23
14
18446 21835
335 ' xy~184;;ilf2 ; 83 -1841 1 21,973
715
15
18653 21454
4~6~/=18653.21~5
1861~ 21592 ,,, 816
18610 21374
361 xy=18610.2137
18575 2151 2
741
16
18495 21363
259 xy~184 95,2136"
18460 21501
639
17
18
18485 21198
224 xy=18485.2119
18450 21336
604

594

I Chapter 29: XM L I
The "towerdat.xls" spreadsheet is installed with MicroStation. When
exported as a "Spreadsheet XML" document, you can traverse the data
using the Microsoft XML Reference.
Sub ReadXMLFilel()
Dim myXML As New MSXML2 . DOMDocument
Dim myWSheets As MSXML2.IXMLDOMNodeList
Dim myWSheet As MSXML2.IXMLDOMNode
Dim myRows As MSXML2.IXMLDOMNodeList
Dim myRow As MSXML2 . IXMLDOM Node
Dim myCe l ls As MSXML2 .I XMLDOM NodeL i st
Di m myCell As MSXM L2 . I XMLDOMNode
my XML. async = Fals e
myXML . validateOnParse = False
myXM L. Load "c : \MicroS t at i on VBA\towe r dat . xml "
Set myWSheets = myXM L.getE l ementsBy TagName( "Worksheet " )
For Each my WSh ee t In my WSheets
Set myRows = myWSheet . se l ectNodes ( " . II Row " )
Fo r Each myRow I n myRows
Set my Ce l l s = myRo w. se l ect Nodes( " .IICell " )
For Eac h myCel l In myC e l l s
Debug. Pr i nt myCe ll . Tex t
Next
Next
Next
End Sub

The structure for a Microsoft XML file is different than that of a


Microsoft Access Database XML export. After declaring the variables to
use and loading the XML file, I get all worksheets in the file. Then I look
at each worksheet and get all rows for the worksheet. Then I get all cells
in each row and look at each cell. The result of this first Excel XML
macro is that the values (stored in the Text property) of each cell is
shown in the Immediate window.

I Reading XML Files I

Even though
XML
provides a
standard
mechanism
for storing
and
retrieving
information,
the structures
are different
fromformat
toformat.
You can read
theXMLfile
specification
in its entirety
at http://
www.w3.org/
XML.

595

Cable Tension
1200
17
color

weight/ft
2
13
tovrers

You see the values all right but how do you know which row and column
you are looking at when you get a value? To answer this question, you
must understand a little more about the Worksheet XML structure.
Each and every cell could have an ''Address'' property. This may be
convenient for us developers, but it would cause the size of the XML file
to increase. So, index properties are used when needed to identify where
a specific row or cell is located.
The first row in the worksheet has an attribute named "Index" that tells
the row number you are looking at. The first cell in each row also has an
attribute named "Index". The cell's index tells in which column the cell is
located. So, between the index of the row and the index of the cell, you
know exactly where the first cell in the first row is located.
The second cell in the first row is a different matter. If the first cell is in
column 2 and the second cell does not have an index attribute, the
second cell listed is in column 3. The next cell in the same row is in
column 4 if it does not have an index attribute. If the next cell has an
index attribute value of 8, it is located in column 8. Only use indexes on
the first cell and whenever a cell is not directly adjacent to another cell.
The second row works in a similar manner. If the second row in the
XML file has an index attribute, the row number is specified in the index
attribute. If an index attribute is not supplied, the row is directly below
the previous row.
A slight modification of the original code allows us to track the row and
column of the cell.

Sub ReadXMLFile2 ()
Dim myXML As New MSXML2.DOMDocument

596

I Chapter 29: XML I


Dim my WSheets As MSXML2. I XML DOMNode Lis t
Dim my WSheet As MSXML2 . IXMLDOMNode
Dim myRows As MSXML2.IXM LDOMN odeList
Dim myRow As MSXML2.IXMLDOMNode
Dim myCells As MSXML2.IXMLDOMNodeList
Dim myCell As MSXML2.IXMLDOMNode
Dim myRowAtt As MSXML2. IXMLDOMAttribute
Dim myColAtt As MSXML2.IXMLDOMAttribute
Dim myAtts As MSXML2.IXMLDOMNamedNodeMap
Dim CurRow As Long
Dim CurCol As Long
myXM L.async = False
myXML . validateOnParse = Fa l se
myXML . Load "c:\MicroStation VBA\towe rdat . xml "
Set myWSheets = myXML.getElementsByTagName( "Worksheet " )
For Each myWSheet In myWSheets
CurRow = 1
CurCol = 1
Set myRows = myWShee t . selectNodes( ". II Row")
For Each myRow In myRows
Set myAtts = myRow.Attributes
Set myRowAtt = myA t ts.getNamedItem("ss:Index ")
If Not myRowAtt Is Nothing Then
CurRow
myRowAtt . Text
El se
CurRow
CurRow + 1
End If
CurCol = 1
Set myCells = myRow.selectNodes( " .IICell " )
For Ea c h my Cell I n my Cell s
Set my At t s = my Cell . Att r i bute s
Set myColAtt = myAtts.getNamedItem("ss:Index " )
If Not myColAtt Is Nothing Then
myColAtt . Text
CurCol
Else
CurCol = CurCol + 1
End If
Debug.Print " R" & CurRaw & " C" & CurCal & "=" & myCell . Text

Next

I Review I

597

Next
Next
End Sub

In the above procedure, ReadXMLFi 1e2, I am printing the row and


column of the cell as well as the text. I do this by looking for index
attributes of rows and cells. When found, I use them as the current
values for the variables "CurRow" and "CurCol': When they are not
found, I increase the "Cur Row" and "Cur Col" values by 1 (based on
whether I am looking at a row object (when I increase the "CurRow"
variable) or a cell object (when I increase the "CurCol" variable).
R2C2=
R2C3=
R2C4=Cable Tension
R2C6 =1 200
R2C7=
R2C8=17
R2C9=color
R3C3=
R3 C4=weight/ ft

Here is the output of Rea dXM LFi 1e2 . Notice that some of the cells have an
empty string ("") for their text property.

REVI EW
XML is a technology that has been talked about for years and is
becoming used more widely. The ability to read XML files is not only
useful now but will become more critical in the days to come.
Since each xml Document Definition varies, you may need a little time
to learn how to traverse a new XML file type.

598

I Chapter 29: XML I

30

Batch Processing
The ability to rapidly process 10, 100, or even 1000 files is as simple as it
is powerful.

In this chapter:
[B

Processing files listed in an ASCII file

[B

Processing all files in a folder

[B

Processing all files in a folder and subfolders

[B

Creating a user interface for file selection

[B

Logging batch file processing

PROCESSING FILES LISTED IN AN

ASCII FILE

It is common to have a list of files that need processing in an ASCII text

file. Here is an example:


File

Edit

Format

View

Help

c:\r.li erostation VBA\BatehProeessing\File A.dgn


c: \r~i eros tati on VBA\8atehProeess i ng\Fi 1 e c. dgn
c: \r~i eros tati on V8A\8atehP roeess i ng\Fi 1 e E. dgn

599

600

I Chapter 30: Batch Processin g I


This first example opens the file ProcessThese.txt, reads each line in it,
then displays each line in a MessageBox.
Sub ProcessASCII()
Dim FileToOpen As String
Dim BatchFile As String
Dim FFile As Long
FFile = FreeFile
BatchFile = "C:\MicroStation VBA\BatchProcessing\ProcessThese.txt"
Open BatchFile For Input As #FFile
While EOF( FF ile ) = Fa l se
Lin e In put #FF i l e, Fi leTo Op en
MsgBox Fi leToOpen
Wend
End Sub
,
C:\Microstation VBA\BatchProcessing\File A.dgn

'-', '

. _C:\Mitrostatio'n vBA\BatchProcessing\File C.dgri

'-'

-,'--'

'

c,;\t:'licrostati~n VBA\B9tchProcessing\Fife E,dgn

Now that the basics are in place, let's build on them.


Sub ProcessASCIIB ()
Dim FileToOpen As String
Dim Batc hFile As String
Dim FFi le As Long
FFile = FreeFile
Ba t chFi l e = "C: \M i croStat i on VBA\BatchP r ocess i ng\ Pr ocessT hese . txt"
Open Bat chFi le For Inp ut As # FFi l e
Wh i le EOF(FFi l e) = False
Li ne Inp ut #FFile, Fil eToOp en
Appli ca t i on.O pen Desig nFil e Fi l eToOp en , Tru e
Msg Box "Do Som eth i ng"
We nd
End Su b

Instead of just showing the file name in a MessageBox, we open each file
in MicroStation with the "Read Only" parameter set to True. This is
useful when you need to extract information from files but don't want to
make any changes to the file. Note that we only open the files, we do not
close them.

I Processing Fi les Listed in an ASCII File I

601

Let's change the ASCII file format. After the path and name of the file,
we have lines specifying which levels to process. Each level line in the
file should begin with a tab and then have the level name. After adding
level names to the file, we perform a SaveAs to the file with a new
filename of ProcessTheseLevels.txt.
" '-""" ~"

'.- , ....~

-~:

.,~,

"'

...

:' """ . :-4<~,~~'t' '<"

~;H'7>.3~~;"'"

''Y'''''~ f''

,'"

""'--

.""

~:Rro!=~s_sl~es,:~e~Is.~ ~,~~~~I!.!~.:..;(~;/: .:, ,,2,;'c,~:,; ~~~


File

Edit

Format

View

Help

C:\Mierostat i on VBA\Batehproeessing\File A.dgn


Level A
Level B
Level C
Level 0
Leve l E
Lev el F
C: \ Mi er os t at i on VBA\Bat ehPr oce ssi ng\ Fil e C.dg n
Lev el A
Level C
Leve l E
c : \I.1i ero s tat i on VBA\BatehProces s i ng\Fi 1 e E. dgn
Leve l B
Level 0
Level F

Here instead of opening each file and displaying the file name in a
MessageBox, we open each file and display each level name in a
MessageBox.
Sub ProcessASCIIC c)
Di m Fil eToOpen As St r in g
Dim Batch Fi l e As St ri ng
Dim st rL IN As Stri ng
Dim FF il e As Lo ng
FFi l e = Fr ee Fi le
BatchF il e =
"C: \ MicroS tation VBA\BatchProcessing\ProcessTheseLevels . txt"
Open BatchFile For Input As #FFile
While EOFC FFile) = False
Line Input #FFile. strLIN
Select Case LeftCstrLIN. 1)
Case vbTab
MsgBox "Do something to Level " &
ReplaceCstrLIN. vbTab. "H)
Case El se
FileToOpen = strLIN
Application.OpenDesignFile FileToOpen . True
End Select
Wend
End Sub

602

I Chapter 30: Batch Processing I


The procedure Pr ocessASC I IC works very well if the specified files exist.
What happens if you attempt to open a file that does not exist?
<_

" . _ .

",,-

~ Informatjon

(])

File [C:\~licrostation VBA\BatchProcessing\File EE,dgn] does


not exist

Prior to attempting to open any file, make sure the file exists.
Sub ProcessASCIIO ()
Dim Fi l eToOpen As St r in g
Dim BatchFile As String
Dim strLIN As String
Dim FFile As Long
Dim Fil e IsOpen As Boolean
FFile = FreeFile
BatchFile =
"C: \ MicroStati on VBA\BatchProcessing\ProcessTheseLevels.txt "
Open BatchFile For Input As #FFile
While EOF(FFile) = False
Line Input #FFile. strLIN
Select Case Left(strLIN. 1)
Case vbTab
If FileIsOpen = True Then
MsgBox "Do something to Level " &
Replace(strLIN. vbTab. "H )
End If
Case Else
FileToOpen = strLIN
If Di r(F il eToOpen)
"" Then
FileIsOpen
False
Else
FileIsOpen = True
ApplicQtion.OpenDesignFilc FilcToOpen. True
End If
End Select
Wend
End Sub

I Processing All Files in a Folder I

603

The 0i r Function is a standard VBA function that returns the name of a


file if it exists. If the file does not exist, the Di r Function returns an
empty string ("") .
In this example, if the file does not exist, we pass it and move to the next
file. We could display a MessageBox stating that the file doesn't exist but
that could be problematic. When we are batch processing files, we may
start processing on Friday at 4:55 PM and expect it to take place over the
weekend. If we attempt to process a file that does not exist and an error
dialog box shows up, the MessageBox would be sitting there from Friday
at 5:05 PM until we come into the office on Monday morning.
We will look at logging our batch processing later in this chapter.
Logging the fact that a requested file does not exist would be much
better than displaying a MessageBox and waiting for user input.

PROCESSING ALL FILES IN A FOLDER


When we have an ASCII file to tell us exactly which files to open,
everything is clear. We know where the files are (or where they are
supposed to be). Quite often, what we need to do, however, is open
every design file in a specific folder.
There are several ways to get all of the file names in a folder. We could
use the Visual Basic 0i r function but let's try something different. For
our example, we will use the Microsoft Scripting Runtime Reference.
After adding this reference, the following code executes:
Sub ProcesslnFolderA()
Dim myFSO As New Scripting.FileSystemObject
Dim myFolder As Scripting. Folder
Dim myFile As Scripting.File
Dim RootFolder As String
RootFolder = "C:\MicroStation VBA\BatchProcessing"
Set myFolder = myFSO.GetFolder(RootFolder)
For Each myFile In myFolder.Files
Select Case UCase(myFile.Type)
Case "BENTLEY MICROSTATION DESIGN FILE"
Debug.Print myFile.Path
End Select
Next
End Sub

604

I Chapter 30: Batch Processing I


Each file in the RootFo lder is examined. In this example we look at the
type of file. The file type is associated with the file extension. You can see
the file type by looking at files using Windows Explorer. Another way to
find design files is to look at the file extension.
Sub ProcesslnFolderB()
Dim myFSO As New Scripting.FileSystemObject
Dim myFolder As Scripting. Folder
Dim myFile As Scripting.File
Di m RootFolde r As Str i ng
RootFolder = "C:\MicroStation VBA\BatchProcessing "
Set myFo l der = myFSO.GetFolder(RootFolder)
For Each myF i le I n myFolder.Fi l es
Sel ect Ca se UCase(R i ght(my Fi le.Name , 3))
Case "D GN "
Deb ug .Prin t my Fil e . Pat h
En d Se l ect
Next
End Sub

Instead of looking at the type, we look at the last three letters in the file
name. If we find a DGN file, we know we are looking at a MicroStation
design file and we print it to the Immediate (Debug) window.
We are off to a good start. Instead of printing the path of each DGN file
in the RootFo lder to the Debug window, we should open the file in
MicroStation and 'Process' it. The procedure Proc ess lnFold erB works
well as long as the files we want to process are in the folder
C:\MicroStation VBA\BatchProcessing. Of course, we wouldn't want to
change our code every time we need to process files in a different folder,
now would we?
Sub ProcesslnFolderC ()
Dim myFSO As New Scr i pti ng.F i leSystemObject
Di m myFolde r As Scripting . Folder
Dim myFi l e As Scripting .F ile
Dim RootFolder As String
RootFolder = InputBox( "Enter Root Folder: " )
Set myFo l der = myFSO.GetFolder(RootFolder)
For Ea c h my Fi 1e I n my Fol de r . Fi 1e s
Select Case UCase(Right(myFile.Name, 3))

I Processing All Files in a Folder I

605

Cas e "DGN "


Debug .P rint myFi le. Path
End Selec t
Next
En d Sub

This is a step in the right direction. We use an InputBox to allow the


user to type in the RootFo lder path. It is a little tedious but gets us out of
having hard-coded paths in our procedure.
What would be great is allowing the user to browse for and select the
RootFolder. But how can we do this? When we use VBA, we have access
to a wealth of resources at our fingertips. Let's make use of one of these
resources right now.
We have seen that adding references to DLLs and libraries gives us
instant access to powerful functionality. The reference we want to add is
called "Microsoft Shell Controls and Automation". After we do this we
can run the next procedure:
Su b PickAFolder ()
Di m myS hell As New Shel132 . She l l
Dim myRootFolder As She l 132 .Folder3
Set myRootFolder = myShell.BrowseForFolder(O, "Pick ", 0)
MsgBox myRoo t Folder.Self.Path
End Sub

Pick

!b ~licrostation VBA
[3 B j:I!iMi.f241hGi
[310 BatchA

A ,

BatchA\

Ii:;) BatchA-2

BatchA-3

10 BatchB
Ii:;) BatchC

!b BatchD
il

!b cd material
Ii:;) docs
Ii:::! Fonts

Make New Folder

V '

OK

Cancel

606

I Chapter 30: Batch Processing I

C:\Microstation VBA\BatchProcessing
OK

t;J

Can it be any easier? Add a simple reference


and a few lines of code then users can select
any folder on their computer or anywhere on
their network. Let's put it to work now with
our batch processing code.

Sub Proces s lnFolderD ()


Dim myFSO As New Scripting.FileSystemObject
Dim myFolder As Scripting. Folder
Dim myFile As Scripting.File
Dim myShe ll As New Shel132 . Shell
Di m myRootFo lder As Shel132.Folder3
Set myRootFolder = myShell.BrowseForFolder(O. "Pick ". 0)
Set myFolder = myFSO.GetFolder(myRootFolder.Self.Path)
For Eac h myFile I n myFolder.Files
Se l ect Case UCase(Righ t( myFile.Name. 3))
Case "D GN"
Debug.Print myF il e.Pat h
End Se l ect
Next
End Sub

Now, instead of asking the user to type in the root folder, the user selects
the folder. Each DGN file in the selected folder displays in the
Immediate window.

PROCESSING ALL FILES IN A FOLDER AND SUBFoLDERS


We now want to allow the user to select a folder and process all files in
the selected folder as well as all files in the selected folder's sub folders.
Before we look into what is required to do this, we need to discuss a
programming technique called "recursive execution:'
Normally, one procedure or function calls another procedure or
function. When a function or procedure calls itself, however, it is
"recursive." Let's look at an example that uses the "Microsoft Scripting
Runtime" and "Microsoft Shell Controls and Automation" references.
Sub ProcesslnFolderE ()
Dim myFSO As New Scripting.FileSystemObject
Dim myFolder As Scripting . Folder
Dim myShel l As New Shel132.Shell
Dim myRootFolder As Shel132.Fo l der3

I Processing All Files in a Folder and SubFolders I

607

Set myRootFol der = myShell . BrowseF orF ol der (0, "Pi ck " , 0)
Set my Folder = my FSO .Get Folder(myRootFolder .S elf . Path)
Process Fi l esAndSubs my Folde r
End Sub
Sub ProcessFilesAndSubs (FolderIn As Scripting.Foloer)
Di m my Fil e As Sc rip t i ng .F ile
Di m mySubF ol der As Sc ripting. Fo l de r
For Ea c h my Fi 1e I n Fol de r In. Fi 1e s
Select Case UCase(Right(myFile . Name, 3))
Case "DGN"
Debug.Print myFile . Path
End Select
Next
For Each mySubFo l der In Fo l derIn.SubFolders
ProcessFilesAndSubs myS ubFo l de r
Ne xt
End Su b

Pro cessI nFol derE should look very familiar. We allow the user to select a
folder. After the folder is selected, we call the Process Fi 1esAndSubs
procedure using the variable myFolder to supply the Folderln
parameter.

Now we are inside Proce ssFi 1esAnd Subs . After declaring a couple of
variables, we examine each file in the FolderIn" folder. If the right three
characters of the file name are DGN, we print the file path to the
Immediate window (also called the Debug window). After we finish
looking at each of the files in the supplied folder, we begin looking at
each of the subfolders in the supplied Folder In" folder. For each
subfolder we find, we use it as the argument for the FolderIn"
parameter in Pr ocess Fi 1esAndSubs , the procedure we are already in. This
is what makes this procedure recursive. We are already in the procedure

608

I Chapter 30: Batch Processing I


and we ask VBA to begin executing the procedure again. This time, we
are using a different folder as the "FolderIn" parameter.
C: \~Iic~ostation

Here is the
output of
thi s
Procedu re:

!' I

VBA\BatchP~ocessing\File

A. dgn
B. dgn
VBA\BatchProcess i ng\Fi l e C. dgn
VBA\BatchProcess i ng\Fil e D.dgn
VBA\ BatchP r oc ess ing\File E. dgn
VBA\B atchPro cess ing\B atchA \ Fil e F. dgn
VBA\ BatchProce ssing\ BatchA\ Fi l e G.dgn
VBA\ BatchP rocess i ng\BatchA\Fi le H.dgn
VBA\BatchProcessing\BatchA\File J.dgn
VBA\BatchProcessing\BatchA\BatchA- l\File
VBA\BatchPr oc essing\B at chA \ BatchA-l \ Fil e
VBA\BatchProcessing\BatchA\BatchA- 2\File
VBA\BatchProcessing\Bat chA\BatchA-2\File

-I

c: \Hinostation VBA\BatchP~ocessing\File
C : \Hic~ostation

C: \Hicrostati on
C: \ Hi cr ostation
c: \Hi c r ostati on
C:\Hi crostati on
C: \Hicrostati on
C:\Hicrostation
C:\Hicrostation
C: \ Hi cr ostati on
C: \Hicrostation
C:\Hicrostation

,~:

K.dgn
L. dgn
H.dgn
N.dgn

As you can see here, we began by processing the files in C:\MicroStation


VBA \BatchProcessing. After this, we began looking at the subfolders of
our root path. First we found "BatchA". We process the files in "BatchA"
and then begin looking at the subfolders of "BatchX'. Each time we find
a subfolder we look at the files of that subfolder and then look for
subfolders in that subfolder. It can sound a little confusing. You can see
we found two subfolders named "BatchA-l" and "BatchA-2" under the
path C:\MicroStation VBA \BatchProcessing.
The net result of using the code shown above in this manner is that
every file in every subfolder, as well as those in the root folder, is
identified. After they are identified, they can be processed.

CREATING A USER INTERFACE FOR FILE SELECTION


We know how to read an ASCII file. We know how to process all files in
a specific folder (and its subfolders). What happens if we only want to
process specific files in a folder? We could make a modification to
procedures already shown in this chapter to display a MessageBox
asking if the user wants to process a file before doing so. Although this
would give the user a little choice, the user would have to sit in front of
the computer answering "Yes", "Yes': "No': "Yes", "No': "No". Choice? Yes.
Batch processing? Not really.
We will create a user interface that allows the user to select the files to be
processed. After the files are selected, we have a GO button that begins
the processing. Two ListBoxes will be in the GUI. One will display all
files in the currently selected folder. The other will hold the files that

I Creating a User Interface for File Selection I

609

have been selected for batch processing. We want the user to be able to
choose which folder to select from. We will use buttons to move
"Selected" or "All" files from one ListBox to the other. We will also allow
the user to double-click a file to move it from one ListBox to the other.

File~

In Folder:

Files To Process:

GO

Here is the code behind the interface.


Private Sub btnBrowse_Click ()
Dim myFSO As New Scripting.FileSystemObject
Dim myFolder As Scripting. Folder
Dim my Fi 1e AsS c rip tin g . Fi 1e
Dim myShell As New Shel132.Shell
Di m myRootFolder As Shel132 .Folder3
Set myRootFolder = myShell.BrowseForFolder(O, "Pick", 0)
If myRootFolder Is Nothing Then Exit Sub
Set myFolder = myFSO.GetFolder(myRootFolder . Self.Path)
txtCurrentFolder.Text = myRootFolder.Self.Path
lstFilesInFolder.Clear
For Each myFile In myFolder.Files
Select Case UCase(Right(myFile . Name, 3))
Case "DGN"
I f I sF i 1e I n ( my Fi 1e . Pat h, 1s t Fi 1esT 0 Pro c e s s )
= False Then
lstFileslnFolder.Addltem myFile.Path

610

I Chapter 30: Batch Processing I


End If
End Select
Next
End Sub
Pri vate Su b btnGo C1 i ck( )
Dim I As Long
For I = 1 To lstFilesToProcess.ListCount
Debug.Print lst Fi lesToPr ocess.L ist ( I - 1)
Next I
End Sub
Private Sub btnlntoA11 C1ick ()
Dim I As Long
For I = 1 To l stF i l~sInFolder . L is tCount
lstFi l es l nFolder.Se l ected(I - 1) = True
Next I
MoveSelection lstFileslnFolder. lstFilesToProcess
End Sub
Private Sub bt nOutA11 C1 i ck ()
Dim I As Long
For I = 1 To lstF i lesToProcess.ListCount
lstFilesToProcess . Selected(I - 1 ) = True
Next I
MoveSelection lstFilesToProcess. lstFileslnFolder
End Sub
Private Sub btnlntoSe1ected C1ick ()
MoveSelection lst Fi les lnFolder. lstFilesToProcess
End Sub
Private Sub btnOutSing1e_C1ick( )
MoveSelection ls tFilesToProcess. lstFileslnFolder
End Sub
Private Sub lstFi1eslnFo1der_Db 1C 1 ick ( ByVal Cancel As _
MSForms.ReturnBoo l ean)
MoveSelection lstFileslnFolder. lstFilesToProcess
End Sub

I Creating a User Interface for File Selection I

611

Pri vate Sub 1s tFi 1esToProcess_Db1 C1 i c k ( ByVa 1 Cancel As _


MSForms.ReturnBoolean)
MoveSelection lstFi l esToProcess. lstF i lesInFolder
End Sub
Function IsFi1elnCFilePath As String. ListToCheck As ListBox) As Boolean

Dim I As Long
IsFileIn = False
For 1= 1 To ListToCheck.ListCount
If StrComp(ListToCheck.List(I
IsFileIn = True
Exit Functi on
End If
Next I
End Function

1). FilePath)

o Then

Function MoveSe1ection CListBoxF r om As Lis tBox . ListB oxTo As ListBox)

Dim I As Long
For 1=1 To Lis t BoxFr om .ListCoun t
If ListBoxFrom.Selected(I - 1) = True Then
If IsFi l eIn(ListBoxFrom. List(I - 1). ListBoxTo)
= False Then
ListBoxTo.AddItem ListBoxFr om. List (I - 1 )
El se
End If
End If
Next I
For I = ListBoxFrom.ListCount To 1 Step -1
If ListBoxFrom.Selected(I - 1)
True Then
ListB oxFrom .Re moveItem I - 1
End If
Next I
End Function

612

I Chapter 30: Batch Processing I

Files To Process:

Files In folder:
~tchPl'o)cessinQ

File A.,jO:ln

>>:.1 Move Selected Files for Processing I

GO

A few items worthy of mention that may not be apparent at first glance:
[B

We set the MultiSelect property of the ListBoxes to 'Extended'


so more than one item can be selected and the user can use the
<Control> and <Shift> keys to aid in the selection of items in
the list.

[B

To simplify the design process and reduce the potential for


bugs, only one procedure, "MoveSelection" is used to move
items from one ListBox to the other. Each of the buttons calls
this same procedure, even the double-click events of the
ListBoxes

[B

Clicking the GO button simply prints the files in the Process


ListBox to the Immediate window. The reader can insert
whatever code will process the files.

[B

Although not in this project, the addition of a few lines of code


allows the user to select a CheckBox specifying to "Search SubFolders" as well as the selected folder.

[B

The "Current Folder" TextBox is locked. This allows the user to


select and copy the path from the TextBox to the Windows
Clipboard but the user cannot type into the TextBox. This is

I Logging File Batch Processing I

613

helpful because we can be sure that the path in the TextBox


actually exists because it is being selected by the user, not typed
in on the keyboard.

LOGGING FILE BATCH PROCESSING


Log files are nothing new. They tell us what has been done, what hasn't
been done, what is being done, and what is about to be done.
Here are some of the ways we can track our processing activities:
lB Using a log file; for example, an ASCII file with a ".log" file
extension.
lB Track processing activities in a database.
lB Store processing information in the registry.
lB Log activities over the Internet.
lB E-mail transaction logs to various recipients.

Using a Log File


Using a log file is perhaps the easiest method of logging activities. Some
of the benefits of using a log file are:
lB .log files are easy to find and read using Notepad
lB .log files can reside on the local computer or on the network
lB .log files can be backed up with other resources
lB .log files are not dependent on other resources such as Internet
connectivity
lB .log files can be used so batch processing programs can 'recover'
in case of power outages, file corruption, etc.
There are a few questions we need to answer when working with .log
files. First, where will the file be located? It could be in a fixed hardcoded folder such as C:\Log Files. It could be located in the same path as
the .mvba file. It could be located on a network share such as Z:\Batch
Processing\Log Files.
Saving files to the same folder where the .mvba file is located is very
useful. If we are running a macro, we know an .mvba file exists. If

614

I Chapter 30: Batch Processing I


the .mvba file has been saved, it has an actual path. If we can find that
path, we can write a .log file to the path and we can easily find it.
Sub TestLogFile ()
Writ eT oLo g "Op enin g C:\ te st.d gn "
En d Sub
Sub WriteToLog(LogFi l eText As String)
Dim myVBE As Obje ct
Dim xSplit As Variant
Dim FFile As Long
Set myVBE = Application . VBE
xSplit = Split(myVBE.ActiveVBProject.Filename. "\ " )
xSplit(UBound(xSplit)) = "BatchProcess.log"
FFi l e = FreeFile
Open Join(xSp li t. " \ " ) For Append As #FFil e
Pri nt #FFi 1e. LogF i 1eText
Clos e ifF Fi 1e
End Sub

What happens when we have multiple users working from the


same .mvba file? Yes, the log file will be appended to but we could have a
problem if one computer attempts to open it when it is already opened
by another computer or user.
Let's look at an example that creates a unique .log file for each computer
name. This uses a Windows API function named GetComputerName.
'General Dec l arat i ons Area
Public Declare Function GetComputerName Lib "kerne132" Al i as
"GetComputerNameA " (ByVa l lp Buffe r As String. _
nSize As Long) As Long
Sub TestLogFileB( )
Writ eToLogB "Ope ni ng C: \test . dgn "
End Sub
Sub WriteToLogB ( LogFileText As String)
Dim myVBE As Object
Dim xSplit As Variant
Di m FFile As Long
Set myVBE = Application.VBE

I Logg ing Fil e Batch Processing I


xSplit

615

Spl i t (myVBE.ActiveVBPr oj ect.Fi l ename . "\ " )


xSplit(UBound(xSp lit )) = "Batc hPr oc ess_ " & This ComputerN ame & " . l og "
FFi le = FreeF i le
Open Joir(xSplit. "\ " ) For Append As #FFile
Print #FFile. LogFileText
Clos e If FFi 1e
End Sub
=

Function Thi sCo mpu te rN ame() As String


ThisCo mputerN ame = Space(255)
Ge t Compute r Na me ThisComp uter Name . Len( Thi sComputerName)
ThisCo mputerN am e
Left(Th is Com pute r Na me. I nSt r (l . _
Thi sCo mput er Name. Chr(O)) - 1)
End Func ti on

Now, each computer running this code has its own BatchProcess .log
file. We could base the log file on the "UserName" that is logged into the
computer but this could cause problems because we may log onto
multiple computers using the same "UserName" that has been
established specifically for batch processing. It is more likely that the
computer name on the network will be unique from machine to
machine rather than the user logged into each computer.

Tracking Activities with a Database


Tracking activities with a database can be extremely useful. Databases
can be queried easily for information. Reports can be made from
databases. An .asp file can be created and run from a web browser to
provide real-time tracking of batch processing. The benefits are
numerous.
We will jump into databases later in this book so we will not provide any
examples right now. Keep logging activities in mind, though, for when
we get into databases.

Storing Information in the Registry


The Windows Registry has become a common storage medium for all
sorts of information. Whenever we see a "Recently Opened File" list it is
likely stored in the Registry. Software serial numbers, installation dates,
installation locations, and file associations are among some of the other
things we will find there. Although we don't want to add great volumes

616

I Chapte r 30: Batc h Processing I


of information to the Registry, we can add some logging information
such as "CurrentFile" and "LastFile".
Sub WriteToRegistry()
SaveSetting "MicroStation VBA", "Batch Processing", _
"LastFile", "C:\FileA.dgn"
SaveSetting "MicroStation VBA" , "Batch Processing", _
"CurrentF i le", "C: \FileB.dgn"
End Sub

File

Edit 'View ' Favorites

Help

Learning MicroStation VBA


Microsoft Visual Basic Addlns

~I

Writing information to the Registry is simple. Reading information


from the Registry is just as simple.
Sub ReadFromRegistry()
Dim Las t File As String
Dim CurrentFile As Stri ng
LastFile = GetSetting("MicroStation VBA", _
"Batch Process i ng", "LastFi l e" )
CurrentFile = GetSett i ng("MicroStation VBA " , _
"Batch Pr ocess i ng " , "Cur r ent File " )
Debug.Pr i nt Last File
De bug .P ri nt Curre nt Fi l e
End Sub

Logging Activities over the Internet


The Internet is used for all sorts of activities. Normally we use it to get
information. It can also be used to transmit information. For example, if
we open a web browser to

http://www.google.com/search?q=MicroStation
Google returns thousands upon thousands of pages related to
MicroStation.

I Logging File Batch Processing I

617

If, however, we entered


http://www.microstationlogging.com ?filename= filea. dgn

this fictitious web site could log the file, the date/time, the IP Address,
etc. But we don't want the user to have to type in the URL every time a
file is processed. Right? Let's have our software send the request.
It is time for another Reference.

"Microsoft Internet Controls"


Sub LogToWeb ()
Dim LocalFile As String
Di m myInet As New Inter net Explorer
Loca l File = "filea.dwg"
myInet.Nav i ga t e _
.. ht tp: //www.microstationlogging . com?f il ename= . &
Loca lF i 1e
While my In et .Bu sy
DoEvents
Wend
End Sub

The code consists of only a few lines. LogToWeb navigates to the specified
URL, in this case, the fictitious website, www.microstationlogging.com.
If this were a real website, and if the default page at that web site received
the parameter "filename", it could log the file name sent to it into a
database on the web server.
Logging information to the Web has its advantages. For one, it doesn't
matter where in the world the individual who is running the program is.
If the computer is connected to the Internet, it can have its activities
logged. Web servers are specifically designed for high traffic, high
volume situations so we could have hundreds or even thousands of
people using this site to log their activities without causing any
problems. Others may hit the site to see real-time statistics as to what is
happening, who is working and who isn't, average time per file, etc. The
sky's the limit. And it is all possible by making a simple reference in VBA
and then navigating to a specific URL.
This same technique can be used to get information from a website.
Although this is not a chapter on Internet technologies, a small example
won't hurt.

618

I Chapter 30: Batch Processing I


Sub GetFromW eb()
Dim I As Long
Dim LocalFile As String
Dim mylnet As New InternetExplorer
Dim TheURL As String
Dim xSplitA As Variant
Dim xSplitB As Variant
Dim xSplitC As Variant
Dim xSplitD As Variant
Dim TrainingDate As Date
Dim Tra i ningLocat i on As Str i ng
Di m Train i ngCost As St ri ng
Di m MsgBoxText As String
TheURL = "http : / /bentleyins t itu t e.bentley . com / " &
"coursein f o . as px?co urse =T RC00 1810- 1/0001 "
myln et . Navigate TheURL
Whi l e mylne t. ReadySta t e (> READYS TATE_COMPLE TE
DoEv ents
Wend
xSp l itA = Spl i t(mylnet . Document . bod y . innerHTML . "I nPersonLabC l as ses " )

xSpl i tB = Sp l it(xSplitA(l) , "<T R " )


For I = LBound(xSp l itB) To UBound(x Sp litB)
xSplit C = Split(x Sp l itB (I) , "( TO >" )
If IsArray(xSpl i tC) = True Then
I f UBound(xS pli tC) >= 3 Then
xSplitD = Split(xSplitC(l), " ( H)
"I "))
Tra i ningDate = CDate(Replace(xSplitD(O),
xSp l itD = Split(xSp l itC(2), " ( H)
TrainingLocation = xSpl i tD(O)
xSp l it D = Split(xSp lit C(3) , " ( H)
Tr a i ni ngCost = xSp li tD(O)
MsgBoxText = MsgBoxText & TrainingDate & vbTab &
Train i ngCost & vbTab & Tra i ningLocation & vbCr
End If
End I f
Next I
MsgBox MsgBoxText , vbInformation , "MicroStation VBA Training "
End Sub

I Logging File Batch Processing I

619

The Bentley Institute sponsors VBA training from time to time. We can
find training dates, locations, and costs on th eir web site. This macro is
designed to display this information in a MessageB ox.

2/20/2006
3/ 10/2006
4/24/2006

f 450.00 180 Wardour Street, London UK


US$ 500.00
Huntington Beach, CA USA
f 450.00 180 Wardour Street, London UK

As the time of this writing, three classes are scheduled. Their dates,
costs, and locations are shown here in this MessageBox. The code is
designed to find specific HTML tags and annotation to drill down to the
information we want. The HTML for this page may change in the future,
but this shows how easy it is to create our own information gathering
tools using VBA.

E-mailing Transaction Logs


E-mail. It seems as though we can't get away from it. At one time it was
restricted to our computers at work or at home. Now it follows us
around on PDAs, cell phones, and Blackberry devices. So, can we use
this technology using VBA? Of course we can, with one string attached.
This string, of course, is a reference.

C2GJ

Available References:

r~icrosoft

APC 6.0 Object Librar y


~licrosoft APC 6.3 Object Library
Microsoft APC 6.4 Object Library
Microsoft AutoDiscovery Type Librar y

Browse .. .

Priority

Help

r~icrosoft COO for Windows 2000 Library

Locat ion:

C:\WINDOWS\systern32\cdosys.dll

Language :

Standard

If the reference does not show up in the list, clicking the Browse button
and browsing to C:\ Windows \sys tem32\Cdosys. dll does the trick.

620

I Chapter 30: Batch Processing I


CDO. What does it stand for? Collaborative Data Obj ects. In plain
English, e-mail.
Sub

TestEmail ()
Dim myMail As New CDO.Message
myMail.To = "batch@microstationlogging.com"
myMail.From

" batch@microstationlogging . com"

myMail.Subject = " MicroStation VBA Batch Process Log"


myMail.HTMLBody

"<b>File name: filea.dgn</b><br>" &


"Computer: " & ThisComputerName & "<br>" &
" Date : 1/1/2005 "

myMail . Configuration.Fields . ltem( '' http : //schemas . microsoft . c om/" & _

" cdo/con fi guration/sendus i ng " )

myM ai l.Co nfigu rat i on.Fi e l ds .It em( '' htt p : // s chemas . mic r osoft . co m/ '' & _

" cdo/config uration/smtpserver " )


"yoursmtpserver . com "
myMa i 1 . Con fig u rat i on. Fie 1 d s . Item ( " http : I I schema s . mi c r 0 soft. c om I " &

"cdo/configuration/smtpserverport " )

25

myMai l .Configura t ion . Fields. Update


myMa i 1 . Send
End Su b

That's about as difficult as it gets. With the right reference and a little bit
of code, our program is now capable of e- mailing batch processing
information to an e-mail address. In order for this code to work
correctly, the "To" property should be set to a legitimate e-mail address
and the SMTPServer Field should be set to a legitimate SMTP server.
And how difficult is it to attach a file to this e-mail?
Sub

TestEmai 12 ()
Di m myMail As New CDO . Message
myMail.To

" batch@microstationlogg i ng . com "

my Mai l . From = "b atc h@mi c r ostat i onlogging.com "


myMail . Subject
myMa i 1 . HTMLBody

" Mi croStat i on VBA Batch Process Log "

=
=

" <b>F i 1 e name : f i 1 ea. dgn</b><br> " &


" Computer: "

& ThisComputerName & " <br>" &

" Date: 111/2005 "

&_

myMail .Configuration.Fields . ltem( '' http : //schemas.microsoft.c om/"

" cdo/configuration/sendus i ng " ) = 2


my Mail . Con fig u rat ion . Fie 1d s . I t em ( " http : I I s c hem as . mi c r 0 soft . com I " & _

"cdo/configuration/smtpserver " )

I Review I

621
" yoursmtpserver.com "

myMai l .Config ur ati on.Fi elds .lte m( "ht tp: // sch emas. mi cr osoft.c ami " & _
" cdo/con f igu r at i on/ sm tp ser ve rp ort " )

25

myMa i 1. Confi gurat ion. Fi el ds. Lpdate


myMa i 1. AddAttachment "C: \ test. dgn"
myMail . Send
End Sub

One line of code. AddAttachment . That's it.

REVIEW
The task of batch processing is accomplished relatively easily. We need
to know which files to process. This can be discovered by using an
ASCII file, having the user select a folder to process, or selecting
individual files within a folder. Next, we need to process the files . Along
the way, we can log our activities through a variety of methods.
Once our code is in place, it matters very little whether we need to
process 5 files or 5,000 files. Our productivity and accuracy can increase
exponentially compared to manually opening each file one by one.

622

I Chapter 30: Batch Processing I

31

The Standards Checker


The Standards Checker helps keep our files in line with established
standards. Using this functionality as it is installed with MicroStation
can do a fairly good job performing basic standards checking - add the
power and flexibility of VBA and nothing can hold us down.
First, you should note that unhandled errors that occur in your
standards checker routines can cause severe errors. Some may even shut
down MicroStation completely. So, you need to take care when working
with standards checker code. Make sure to save your VBA Projects often
and verify that any DGN files open in MicroStation are also saved.
In this chapter:
[8 Basics of implementing the standards checker
[8 Standards Checker settings
[8 Checking for standards
[8 Standards Checker reporting
[8 Automatically loading custom standards checker add-ins

623

624

I Chapter 31: The Standards Checker I


BASICS OF IMPLEMENTING THE STANDARDS CHECKER
Implement the Standards Checker by using the IStandardsChecker
Interface. As with other interfaces we have implemented, use a class
module and write your code within events.
'General Dec l arations
Implements IStandardsChecker

Private Sub I StandardsChecker_AddedC hecker ToStandardsC heckerApps( _


ByVal Applicat i onXMLNode As Object)
End Sub

Private Property Get IStand a rdsChecker_CallF orEachM ode l () As Boolean


End Property

Private Sub IStandardsChecker_CreateSettings()


End Sub

Private Sub IStandardsChecker_DeleteSettings()


End Sub

Pr i vate Property Get IStandardsChecker_Description() As String


End Property

Pr i vate Property Get IStandardsChecker_DialogString() As Str i ng


End Property

I Basics of Implementing the Standards Checker I

625

Pr i vate Sub IS t anda r dsCh ecker_EditSetti ngs(B yV al


I s Rea dO nl y As Bo olean )
End Sub

Private Property Get IStandard sChecker_ FoundSett i ngs() As Boolean


En d Property

Private Sub IStandardsChecker_GetFixDetail (Fixes ( ) As String, _


ByVal Se l ectedFix As Long , FixPropertiesLabel As String , _
FixPropert i es() As Str i ng)
End Sub

Pr iv ate Property Get IStandardsChecker_HasSettings() As Boolean


End Property

Private Property Get IStandardsChecker_IdentityStr i ng ( ) As String


End Property

Pr i vate Sub IStandardsChecker_ RunCheck(ByVal _


ModelToCheck As ModelReference , _
ByVal FirstModel As Boolean, ByVal Opt i ons As Long)
End Sub

Private Pr operty Get IStandardsC hecker_VersionString() As Str i ng


End Property

Six events and seven properties are implemented. As the user begins the
Standards Checker by selecting Utilities> Standards Checker> Check,
the properties in our class module are used in the standards checker
user interface. Let's implement a standards checker that does nothing

626

I Chapter 31: The Standards Checker I


other than implement the most basic properties and uses Debug.Print
statements so we can see the order in which events take place.

Standards Check A
The following code
clsStandCheckA.

is

placed

in

class

module

named

Option Explicit
Implements IStandardsChecker
Private Sub IStandardsChecker_AddedCheckerToStandardsCheckerApps( _
ByVal ApplicationXMLNode As Object)
Debug . Print "AddedCheckerToStandardsCheckerApps "
End Sub
Private Property Get IStandardsChecker_Ca l lForEachModel() As Boo l ean
Debug. Pri nt DCa 11 ForEachModel"
IStand ardsChecker_CallForEach Model = True
End Property
Private Sub IStandardsChecker_CreateSettings()
Debug.Print "CreateSett i ngs "
End Sub
Private Sub IS t andard sChecker_DeleteSet t i ng s ()
Debug.Print "DeleteSettings"
End Sub
Private Property Get ISta ndardsChecker_Description() As String
Debug . Print "D escription "
IStandardsChecker_Description = "VBA StandChk A Desc "
End Property
Private Property Get IStandardsChecker_DialogString() As String
Debug.Print "Dia l ogString "
I StandardsChecker_D i alogString = "VBA StandChk A Dial "
End Property
Private Sub IStandardsChecker_EditSettingsCByVal IsReadOnly As Boolean)
Debug.Print "EditSettings"
End Sub

I Basics of Implementing the Standards Checker I

627

Private Property Get IStandardsChecker_FoundSettings() As Boolean


Debug.Pr i nt " Found Sett i ngs "
Erd Property
Private Sub IStandardsChecker GetFixDe t ail(F i xes() As String . _
ByVal SelectedFix As Long . _
Fi xP r op ertiesLa bel As St r i ng . _
Fi xPr operties( ) As Strin g )
Debug . Print "GetFixDetail "
End Sub
Private Property Get IStandardsChecker_HasSettings ( ) As Boolean
Debug.Print "HasSettings"
IStandardsChecker_HasSettings = True
End Proper t y
Private Property Get IStandardsChecker_IdentityString() As String
Debug . Pr i nt " IdentityString "
"VBA SC A"
IStandardsChecker_Ident i tyS t ring
End Property
Pr i vate Sub IStandardsCheck e r_R unCheck(ByVal
Mode lT oCheck As ModelReference . _
ByVa l Fi rstMode l As Boo l ean . _
ByVal Options As Long)
Debug.Print " IdentityString"
End Sub
Private Property Get IStandardsChecker_VersionString() As String
Debug.Print "V ersionString "
IStandardsChecker_VersionString = "1.0.0.0 "
End Property

Now we need to write some code that adds the above class module to the
Standards Checker in MicroStation. This code is located in a code
module.
Option Explicit
Private StandChk As clsStandCheckA
Sub AddChecker( )

628

I Chapter 31: The Standards Checker I


RemoveChe c ker
Set Stan dChk = New cl sStandC heckA
StandardsCheckerController.AddStandardsChecker StandChk. 1000
End Sub
Sub Re moveChec ke r ()
If Not StandChk Is Nothing Then
StandardsCheckerController.RemoveSta ndar dsChecker StandChk
End I f
Set Stan dChk = Nothing
End Sub

When we run AddChe cker, the class module is instantiated and added to
MicroStation's Standard Checker dialog box.
"'i1:?f~'t::;""~~"0~"~"r"--'~n7'~-::"T:

P'Cr

~~-v~y;':~'}~

~t~!,.d!llJ!~(:hec~J" S~~!I!]gs" .";:;,,, ".. J

<v.~

"'I;

,~~

",>,>

>

~~

':-

.,,'"

<... ~"',.~ ~'? /'",:,,"\_j~

>,>,' >

Settings ---.- - - -: : - - - -- - - -- -

,..

A><

';.v

"> i

- - -- -

:$~tii6d.~H~~:~ IW:OOldWP;;W;
Checks -'----------,,-,-----'---~.---

.CilVBA, StandChk A Dial


~heck Levels

D
D
D

Check Text St~les


Check Dimension Styles
Check Element Templates

o Check Line Styles


D
.C " !]

Cancel

When we run the standards checker, we can see how the properties
returned in the class module are used. In this view we can see "VBA
StandChk A Dial" which comes from the Di a1ogS tri ng property. Prior
to creating the class module, the "VBA Check ~' setting in the
Standards Checker Settings Configuration dialog box was created.
We can select the "VBA StandChk A Dial" CheckBox and can click the
settings button. When we do so, events are triggered and their
associated Debug.Print statements are executed.
At this point we are able to get our own standards checker into the
Standards Checker dialog. A good start. But nothing to write home
about. Before we continue let's talk about the entire standards checker
process.

I Basics of Implementing the Standards Checker I


1

A class module is created that implements the IStandardsChecker


interface.

2 A procedure declares a variable as the class module and then adds it


using AddSta nda rd s Checke r.

3 When the user clicks Utilities> Standards Checker> Configure, the


events and properties are triggered in the following sequence:
IdentityString
DialogString
HasSettings
CallForEachModel
FoundSettings
IdentityString
DialogString

The next dialog we see will vary based on the HasSettings property.
If we return a value of True in this property, we see a Settings
button.
4

After settings are made, we can select our new, custom Standards
Checker add-in.
',' ..<\' ~r$"~'S'''>'''-',,<' ''''ry~'

,.,

"~""'~iflJ:N.~"""

'!

~tii!Jdards Ch!!cke~ Setti,llg!!"

",'-""

~?"<""""<J' ~

'<, " ' \ '"

v-,.. "';::'''''' ,.

,,.:,

+<'" "',,

""""""'-""" "'~'l"""'W"'\..""""'~'"'Y~t-r"'~

,,"

:',,'

Settings ----,----,-

i~~ii;:69S.H~~~ I~W:~
r!1~Il;n~*~~.~ ~,v~a

DX

Checks - - - - - - - , - - - - - - - - - - - - - -----,-

D
D
D
D
D

Check Levels
Check Text Styles
Check Dimension Styles
Check Element Templates
Check Line Styles

OK

Cancel

629

630

I Cha pter 31: The Standards Checker I


If, however, the HasSettings property returns a value of False, we do
not get a Settings button and the standards checker we created is
enabled and ready to be selected.
1

,,~~'I'

,t~,~~,a~ds !=,~~c~er

Settings

:$:~~:;69iHi1.m:~

Se!tings

vi

Check.1>.

~j

Checks ~-----~-----------

o V8A StandChk A Dial


o Check Levels
o Check Text Styles
o Check Dimension Styles
o Check Element Templates
o Check Line Styles
Cancel

If available, the user clicks the Settings button.

The EditSettings event is triggered with a "IsReadOnly" parameter


value of False. When we are given a value of False, we know that we
should allow the user to change the settings for our standards
checker. At this point we can display a dialog box that allows the
user to select various settings for our standards checker.

After the EditSettings event exits execution, the standards checker


we created is enabled and can be selected.

Each time the standards checker we created is selected or deselected,


the IdentityString property is retrieved.

9 When the user clicks the OK button, the standards checker we


created is selected.
Let's implement a Settings dialog box now. In order for a Settings dialog
box to display, we need to have the following code in our
Standards Checker class module:
Private Property Get IStanda r dsChec ker_HasSettings() As Boolean
Debug . Print "HasSe t tings "
ISta ndardsChecker_HasSettings = True
End Property
Private Sub IStandardsChecker_EditSettings(ByVa l IsReadOnly As Boolean)

Debug.Print "EditSettings "

I Standards Checker Settings I

631

UserForml . Show
End Su b

Of course, the Debug.Print statements are only here to help us see the
order in which the events are triggered. The important things are to set
the IStandardsCheckecHasSettings to True and to display a UserForm
in the EditSettings event. At this point, we are ignoring the IsReadOnly
parameter of the EditSettings event.
So, what settings are we going to allow the user to set? By default,
MicroStation has settings for levels, text styles, dimension styles,
element templates, and line styles. What else is there to check?

STANDARDS CHECKER SETTINGS


Standards Checker interfaces can make use of 'Settings:
Pri vate Property Get IStan dardsChecker_HasSettings() As Boo l ean
I Sta nda r dsChecker_Ha sS ett i ng s = Tru e
End Prope r ty

If the HasSettings property is True, the Settings button displays in the


Standards Checker Settings dialog box (Utilities> Standards Checker>
Configure). When the Settings button is clicked, the EditSettings event
is triggered.
Pr i vate Sub ISta ndardsChecker_EditSett i ngs(ByVa l I sReadOnly As
Boolean)
UserForml . Show
End Sub

OK. Now when the Settings button is clicked we display a UserForm


named "UserForml". Let's design a graphical user interface that
resembles the one shown here:
P' [~9.9.~L.~~~ji.i/Yii~~~~:j~~ii~!

File Contains Only l egitimate Room l abels

File Path : IC:\MicroStation V8A\labels2.t xt

P' Automatically Fix Errors


OK

Cancel

632

I Chapter 31: The Standards Checker I


This example allows for the entry and storing of settings relating to
Room labels in a design file. We need to allow the selection of the
following properties:
~

Room labels are written in a file

The file contains only legitimate room labels

The path of the file

Should the standards checker fix errors it encounters?

The interface is fairly simple and straightforward. Entering the data is


the easy part. How will we store the settings? We have several options:
~

Save the settings in an ASCII text file.

Save the settings in the design file so settings are unique to each
design file.

Save the settings in the Windows Registry.

Save the settings as XML in the Settings dgnlib file.

We will examine one way we can save standards checker settings. Our
example here stores values in the Windows Registry. You can find
another method of storing standards checker (storing settings in XML
format in a .dgnlib file) settings in the MicroStation VBA Help file.
In this example we save our settings in the Windows Registry. Here is
the code behind the GUI:
Private Sub UserForm_Init ia l ize ()
chkLabelsWritten.Value =
GetSetting( "VBA Standards Checker ", "Settings", "Labels Written", _

False)
chkCleanF il e .Valu e =
GetSetting("VBA Standards Checker ", "Settings " , "Clea n File " , False)

chkF i xErrors.Value

GetSetting( "VBA Standards Checker ", "Settings", "Fix Errors ", False)

txtFi l ePath. Text

GetSetting( "VBA Standards Checker ". "Settings ", "File Path ", _

"C: \MicroStation VBA\Labels.txt " )

I Standards Checker Settings I

633

En d Sub

Pri vate Sub btnOK_Cl i ck()


If txtFi l ePath . Text

""

Then

MsgBox " A f i le pa t h must be entered ."


Exit Sub
En d I f

SaveSetting " VBA Standards Checker " , " Sett i ngs ", _
" Labels Written ", chk LabelsWritten . Value

SaveSetting "VBA Standards Checker ",

" Sett i ngs ", _

" Clean Fi le " , chkClean Fi le.Value

SaveSetting " VBA Standards Checker " , "S ettings ", _


"Fi x Errors ", chkFixErrors .V alue

SaveSetting " VBA Standards Checker " , " Sett i ngs " , _
" File Path ", txtF i lePath . Text

Unload Me
End Sub

Private Sub btnCancel_Click()


Un l o ad Me
End Sub

When the Settings Form is initialized, we look at the Windows Registry


using the standard VBA call GetSett i ng and providing a default value.
The default value is important because the first time this Settings Form
displays, the Registry will not have the settings information we need.
When the user clicks the OK button, we save the properties of the
CheckBoxes and the TextBox in the Registry using the SaveSett i ng call.
After the values are saved in the Registry, we unload the form.
If the Cancel button is clicked, we unload the form without saving values
to the Registry.

634

I Chapter 31: The Sta ndards Checke r I


CHECKING FOR STANDARDS
At this point, we can add a custom standards checker that displays in the
Standards Checker dialog boxes. We can also display and save settings
made when the Settings button is clicked in the Standards Checker
Settings dialog box.
So, how do we actually check our document? The first thing we need is a
file to check. Let's open the file BSI300AE701-Plan.dgn. It is installed
with MicroStation.
Now we are ready to check this file.
We are going to look at a procedure that checks for, reports, and fixes
(when set to do so) situations relating to the Room Label tags in the file
BSI300AE701-Plan.dgn. It is named "RoomLabelChecks" and it is
divided into seven segments:
[8 Declare variables
[8 Read all room label tags in drawing
[8 Get settings from registry
[8 Read room label file
[8 Check for file's existence
[8 Check for tags in file
[8 Check to make sure labels in the file have tags in the DGN file

Each segment in this procedure is proceeded by a comment matching


the bulleted items above. We check our file by using the RunChec k event
in the IStandardsChecker interface.
Private Sub I StandardsChecker_R unCheck( ByVal
Mode l ToC heck As Mode lRef ere nce , _
ByVal Fir stModel As Boolean,
ByVa l Op t ions As Long)
Rooml ilDel(hecks ModelToCheck
End Sub

And now for the procedure "RoomLabelChecks":


Sub RoomLab e l Che ck s(ModelToScan As ModelReference)

I Checking for Standards I

635

'D ec lar e Var iables


Dim myElementEnum As ElementEnumerator
Dim myScanCriteria As New ElementScanCriteria
Dim myTags() As String
ReDim myTags(O) As String
Dim myTagsFilter() As String
Dim RoomTag As TagElement
Dim I As Long
Dim AutoFix As Boolean
Dim LabelslnFile As Boolean
Dim LegitimateLabels As Boolean
Dim myFileRooms() As String
ReDim myFi leRooms(O) As String
Dim FFile As Long
Dim FileName As String
Di m Line l n As Str i ng

, Read All Room Labe l Tags i n Drawi ng


myScanCriteria.ExcludeAllTypes
myScanCr i t er i a. l nc l udeType msdE l ementTypeTag
Set myE l ementEnum = Mode l ToScan . Scan(myScanCriteria)
Whi le my ElementEnum . MoveNext
Set Roo mT ag = myElementEnum . Current
Se l ect Case UCase(RoomTag . TagDe f init i on Name)
Case "ROOM LABEL "
myTagsFilter = Fi lter(my Tags . RoomTag.Value. True. _
vbBinaryCompare)
If UBound(myTagsFilter) = -1 Then
myTags(UBound(myTags)) = RoomTag . Value
ReD i m Preserve myTags(UBound(myTags) + 1)
End If
End Select
Wend
' *********

If UBound (myTags) )= 1 Then


ReDim Preserve my Tags(UBound(myTags) - 1)
End If
' Get Settings from Registry

636

I Chapter 31: The Standards Checker I


GetSetting( "V BA Standards Checker ", "Settings ",
"Fi le Path " , _
"C:\MicroStatior VBA\Labels.txt " )
GetSetting("VBA Standards Checker", "Settings",
AutoFix
"Fix Errors", _
"False")
LabelsInFile
GetSetting("VBA Standards Checker " ,
"Settings " ,_
"Lab els Written", "Fal se " )
Legiti mateLabels = GetSetting("VBA Standards Checker " , _
"Se ttings ", _
"C lean File", "Fa lse " )
FileName

' Read Room Label File


FFile = FreeFile
If Dir(FileName) <> "" Then
Open FileName For Input As #FFile
While EOF(FFile) = False
Line Input #FFile, myFileRooms(UBound(myFileRooms))
ReDim Preserve myFileRooms(UBound(myFileRooms) + 1)
Wend
If UBound(myFileRoom s) >= 1 Then
ReDim Prese r ve myFileRooms(UBound(myFileRooms) - 1)
End If
Close #FFile
End If

'Check for File's Existence


If Dir(FileName) = "" Then
Debug.Print "Th e File" & Fi l eName & " does not exist. "
If AutoFix = True Then
Open FileName For Output As #FFile
For I = LBound(myTags) To UBound(myTags)
Print #FFile , my Tags(I)
Next I
Close #FFile
Debug . Print "The File " & FileName &
" has been created ."
End If
End If

I Checking for Standards I

637

' Check fo r Tags i n Fi l e


If Labe l s l nFil e = Tr ue Then
For I = LBound(myTags) To UBound(myTags)
myTagsFilter = Filter(myFileRooms, myTags(I), True, _
vbBinaryCompare)
I f UBoun d(myTa gs Fil ter) = -1 Th en
De bu g . Pri nt "Ta g " & my Tags (l) & _
" not f ound in file."
If Auto Fix = Tr ue Then
Open FileNa me For Append As #FF i le
Pri nt #FF i 1 e , my Tags ( I)
Debug.Print " Label " & myTags(l) &
" added to File ."
Cl ose #F File
End If
End If
Next I
End If

' Check to make sure Labe l s i n t he File have


'Tags in the DGN f i le
If Leg i timateLabe l s = Tr ue Then
If Dir(Fi l eName) = " " Then
Debug . Print " Fil e " & FileName & " does not exist ."
El se
For I = LBoun d(my Fi l eR ooms) To UBound(myF i le Rooms)
myTagsFilter = Filter(my Tags , myF i leRooms(I ) , _
Tr ue, _
vbB i naryCompare)
If UBound( myTagsFilter) = -1 The n
Debug . Pr in t "Fi le Room " &
my Fi 1e Roo ms ( I ) &
" i s not in t he DGN Fi l e. "
End If
Next I
End If
End If
End Sub

The procedure is rather lengthy, so let's discuss each segment.

638

I Chapter 31: The Standa rds Checker I


1

Declare variables.

This section is self-explanatory. We use a couple of dynamic arrays


because before the code is executed, we do not know how many room
labels we will find in the drawing or in the file.
2

Read all room label tags in drawing.

We create a ScanCriteria filtering on tags. We then look to see which


tags in th e drawing are "Room Lab els". When we find one, we assign it to
the last element in the dynamic array myTags and then increase the size
of the array, preserving the existing values.
3

Get settings from registry.

Placing the standards checker settings into variables means we only


need to read them once and our code is easier to read than having
multiple GetS et t i ng calls for the same Registry entry.
4

Read room label file.

We specify a file name in the Settings dialog box. This file is to contain
all room label values in our DGN file. If the file exists, we read the file,
placing each line of the file into its own element in a dynamic array.

Check for file's existence.

If the file in which we are placing room label values does not exist, we
create it and populate it if the "Automatically Fix Errors" setting is True.
6

Check for tags in file.

We look at each room label tag found in the drawing file and check for
its presence in the file. If the room label tag is not found in the file, we
add it to the file (if AutoFix is True) and report the error to the Debug
(Immediate) window.
7

Check to make sure labels in the file have tags in the DGN file.

lt is possible that the ASCII file we are looking at has room label values
in it that are not in the drawing. This could be due to data entry errors
or the result of having deleted a room label from the drawing. In either
case, if we find a room label in the ASCII file that does not have an
associated room label value in the drawing, we report the problem by
printing to the Immediate window.

I Standards Checker Reporting I

639

STANDARDS CHECKER REPORTING


Let's expand our standards checker now to use some built-in error
reporting functionality. We add a custom enumeration in the General
Declarations area of the class module and one procedure to the body of
the class module. All code changes are shown in bold.
We use the enumeration "ErrorType" to help describe the nature of the
error we are reporting. As you see here, we have seven elements in the
enumeration.
Private Enum ErrorType
NoError = 0
NotInFile = 1
NotI nDGN = 2
FixedNotlnFi l e = 11
FixedNotlnDGN = 12
NotFixedNotlnFile = 21
NotFixedNotlnDGN = 22
End Enum

When we find something to report, we use the procedure Re portError.


We specify which type of error we encountered as well as the room
number that was not found, corrected, or not corrected.
Private Sub ReportError(Error As ErrorType, RoomNumber As
String)
Dim mySCC As StandardsCheckerController
Set mySCC = StandardsCheckerController
Select Case Error
Case ErrorType.NotlnDGN
mySCC.TotalProblems
mySCC.TotalProblems + 1
Case ErrorType.NotlnFile
mySCC.TotalProblems = mySCC.TotalProblems + 1
Case ErrorType.FixedNotlnDGN
mySCC.FixedProblems = mySCC.FixedProblems + 1
Case ErrorType.FixedNotlnFile
mySCC.FixedProblems = mySCC.FixedProblems + 1
Case ErrorType.NotFixedNotlnDGN
mySCC.IgnoredProblems = mySCC.IgnoredProblems + 1
Case ErrorType.NotFixedNotlnFile
mySCC.IgnoredProblems = mySCC.IgnoredProblem s + 1

640

I Chapter 31: The Standards Checker I


End Select
End Sub

The only thing we do differently in RoomLabelChecks is use the


ReportError procedure when we encounter an error or when we fix an
error.
Sub RoomLabelChecks (Model ToScan As ModelRe f erence)
' Declare Variables
Di m myElementEnum As ElementEnumerator
Dim myScanCriteria As New ElementScanCriteria
Dim myTags() As String
ReDim myTags(O) As String
Dim myTagsFilter() As String
Dim RoomTag As TagEl ement
Dim I As Long
Dim AutoF i x As Boolean
Dim LabelslnFile As Boolean
Dim LegitimateLabels As Boolean
Dim myFileRooms() As String
ReDim myFileRooms(O) As String
Dim FFile As Long
Dim FileName As String
Dim LineIn As String

'Read All Room Label Tags in Drawing


myScanCriteria.ExcludeAllTypes
myScan Criteria.I ncludeType msdEleme ntTypeTag
Set myElementEnum = ModelToScan.Scan(myScanCriteria)
While myElementEnum .M oveNext
Set RoomTag = myElementEnum.Current
Select Case UCase(RoomTag.TagDefinitionName)
Case "ROOM LABEL"
myTagsFilter = Filter (myTags, Roo mTag.Valu e , True, _
vbBinaryCompare )
If UBound(myTagsFilter) = -1 Then
myTags(UBound(myTags)) = RoomTag.Value
ReDim Preserve myTa gs ( UB ound (myTag s ) + 1)
End If
End Sel ec t
Wend

I Standards Checker Reporting I

641

If UBound(myTags) >= 1 Then


ReDim Preserve myTags(UBou~d(myTags) - 1)
End If
' Get Settings from Registry
Fi l eName = GetSetting("VBA Standards Checker", "Settings ", _
"File Path", _
"C: \M icr oStation VBA\Labels.txt")
AutoFix = GetSetting("VBA Standards Checker", "Settings",
"Fix Errors " , "False")
LabelsInF i le = GetSetting( "VBA Standards Checker ", _
"Settings " , "Labels Written " , " False")
LegitimateLabels = GetSetting( "VBA Standards Checker " , _
"Sett i ngs ", "Clean File ", "False " )

'Read Room Label Fi 1e


FFile = FreeFile
If Dir(FileName) <> "" Then
Open FileName For Input As #FFile
While EOF(FFile) = False
Line I nput #FFile , myFileRo oms(UB ound(myFileRooms))
ReDim Preserve myFi l eRooms(UBound(myFileRooms) + 1)
Wend
If UBound(myFileRooms) >= 1 Then
ReDim Preserve myFileRooms(UB oun d(myFileRoo ms) - 1)
End If
Close ifFFile
End If

' Check for File ' s Existence


If Dir(FileName) = "" Then
Debug.Print "The File " & FileName & " does not exist ."
If AutoFix = True Then
Open Fi l eName For Output As #FFile
For I = LBound(myTags) To UBound(myTags)
Pr i ntifF Fi 1e, my Tag s ( I )
Next I
Close #FFile
Debug.Print "The Fi le " & FileName &
" has been created. "

642

I Chapter 31: The Standards Checker I


End If
End If

' Check for Tags i n Fi 1e


If La be l s In Fil e = Tr ue Then
For I = LB oun d(myTag s) To UB ou nd (myTa gs )
myTagsFilter = Filt e r(myFileRooms, myTags ( I ) , Tr ue, _
vb Bi naryCompa r e)
If UBound(my TagsFilter) = - 1 The n
ReportError NotlnFile . myTags(I)
Debug . Print "Tag " & myTags(I) & " not found in file. "
If AutoFix = True Then
ReportError FixedNotlnFile. myTags(I)
Open FileName For Append As #FFile
Pr i ntifF Fi 1 e, my Tag s ( I )
Debug . Print "Label " & myT ags( I ) &
" added to File. "
Close /FFFi 1 e
El s e
ReportError NotFixedNotlnFi l e. myTags(I)
End If
En d If
Next
End I f

' Check to make sure Labels in the Fi le have


' Tags in the DGN f il e
If Le gi ti ma teLabel s = Tr ue Then
I f Di r( Fi le Name) = "" Then
Debug . Pr i nt "File " & FileName & " does not exist. "
El se
For I = LBound( myF il eRooms) To UBound(my FileRooms)
myTagsF i lter = Filter(myTags. myF i leRooms(!l. True. _
vbBi naryCompare)
If UBound(myTags Fi l t er) = - 1 Then
ReportError NotlnDGN . myFileRooms(Il
ReportError NotFi xedNotlnDGN, myFileRoom s (I)
De bug .P rint "Fi l e Room " & myF i leRooms( I ) &
" is not in the DGN File ."
End If

I Standards Checker Reporting I

643

Next
End I f
End If
End Sub

So, we have added a few lines of code. What does this get us?
~

~.'

~ta"!.c!a~ds

After the
Standards
Checker is run,
we are shown:

....

,....

"

....,

' -..

,,~

Ch,eck ~omplete

>-c' ....

,._v ....'"

_~ ~

"~"

Standards Check Complete


114 Problems Found
50 Problems Fixed
64 Ignored Problem
Review Report File reportOlI. xml?

, ~Y(~';::"":~~Y~%l7~ s:1'fJ'(,,'"'-~rfE!~~1,lf/:~''';~'fr:~ "'Y'C~:"";': rry:~~;;.;~"~"f\'-~~

S~~ruJ~~~~ C!~5~.fll!!!,I!I~J!"'~iM ,:',.;;'j~"\i~ /.fflifj;ji~Nl-,~,::?\,>~'

Standards Check Complete


64 Problems Found

oProblems Fixed

64 Ignored Problem
Review Report File reportOI 2.xml?

At this point in the development of our standards checker "add-in", we


are not reporting anything to the Report file. If we click Yes in the
Standards Check Complete dialog box, we see a report.

~~ ' ,

'

.'

.:

C:'Oo(umvnn ,md 5,(1mg<; All U'$en :AIHlllntlo n

BenUey Systems, Inc. Siandards Cl1ecker


Version 8.9.2,12
'

2J on:I'Q.nt'I.vWorkSp~,, proJ"U' E'lI:Jmpl.s ' BuIt4Ino Dgn BSllOOAE10t


Pfiln.dgl"\
ra1i11. IFII~ Puu::HUd

3SOkb OGNVS Ad:mlnlnr.u')r i~fS~; t7

"-",'

~3G1~;5$:&

StandardsChecked:

.. i V8A S.und(flk A. Ot~1

'.,

!;.

1.0.0.0

644

I Chapter 31: The Standards Checker I


We just touched the surface on reporting problems found in our
Standards Checker program. Let's build on it now by adding some code
to the ReportError procedure.
Private Sub ReportError (Error As ErrorType, _
RoomNumber As String)
Dim mySCC As StandardsCheckerController
Di m myProb As StandardsCheckerProblem
Dim myRep As StandardsCheckerReport
Set mySCC
StandardsCheckerController
Set myRep
mySCC.Report
Select Case Error
Case ErrorType . Not lnDGN
mySCC.TotalProblems = mySCC.Tota l Problems + 1
Set myProb = myRep.AddProblem("Room " & RoomNumber &
"is NOT in DGN.", "VBA CheckA", False)
Case ErrorType.NotlnFile
mySCC.TotalPr oblem s = mySCC.TotalPro blems + 1
Set myProb = myRep.AddProblem("Room " & RoomNumber &
" is NOT in file.", "VBA CheckA", Fal se)
Case ErrorType.FixedNotlnDGN
mySCC.FixedProblems = mySCC .Fi xedProblems + 1
Case ErrorType.FixedNotlnFile
mySCC .Fi xedProblems = mySCC .Fi xedProblems + 1
Case Error Type.N otFixedNotlnDGN
mySCC . IgnoredProblems = mySCC.IgnoredProb le ms + 1
Case ErrorType.NotFixedNotln File
mySCC . IgnoredProblems = mySCC.Ignored Problems + 1
End Select
End Sub

Now, when we view the report generated after the checker finishes, we
see the number of problems that were identified.

I Standards Checker Reporting I

645

And if we expand the listing below the file name we see the specific
problems we added above.
~@~

11 Bentley Systems, Inc. Standard. Checker


Edit

fde

VIew

Favorites

Tools

Help

~BENrLEY

Bentley Systems, Inc. Standards Checker


Version 8.9.2.12

IQJ--Files Processed :

DExpand All

o Hide Fixed Problems

~ Hide Ignored Problems

File Nome
,

.0

last

Form.t Author

Modified

nate

Problems
Remaining Ignore

Checked

.
2006103117 20D6f03124
C:lOocument nd SettingslAll U.erslApplicatlon .
O.t"Bentley\WorkSp.ce\ProJect.\Ex.mples\BulldlngI OgnIBSI3DOAE101 350kb OGNV8 Admln15trotor 23'45'22 14'00'52
PI.n.dgn
..
..
#

..i::~~.,

Sile

Model
Ground Floor Pl an

Stondard
V8AC heckA

Ground Floor Plan

V8ACheckA
V8AC he ckA
YBACheckA
V8ACheckA

4
'5

Ground Floor Plan


Ground Floor Plan
Ground Floor Plan

,6

Ground Floor Plan

.~

..:

V8ACheckA, ,

. . .".~,.. ,. :.::~: ~.~:. i,~.

'": ~::"~',;r:~~ .:"~':-~!;"""'~- '~~

.liW!W

!Nt!

Him

64

Description
Room 111 is NOT in fil .
, Room 110 is NOT in fiI .
Room 109 is NOT in file.

,Room H)8 is NOT

J.

f Room

"

,'

.. -. -~~'....~:';~~',.~t.~~:~:,~:;:::~i;;;,;~~~:,,~ .,,~~\~,'

in fil . .

107 is NOT in file.

Room 100 is NOT in fil.,

'"," "-.-. ~

~;.;;;---- ------.-- .~---'"----.- -----'-.- ----.--.-----.---...,...--- -

' ~!

Let's add a little more to the procedure ReportError.


Private
String)
Dim
Dim
Dim
Set
Se t

Sub ReportError(Error As ErrorType, RoomNumber As


mySCC As StandardsCheckerController
myProb As StandardsCheckerProblem
myRep As StandardsCheckerReport
mySCC
StandardsCheckerController
myRep
mySCC.Report

Select Case Error


Case ErrorType.NotlnDGN
mySCC.TotalProblems = mySCC . TotalProblems + 1
Set myProb = myRep.AddProblem("Room " & RoomNumber &
" is NOT in DGN, ", "VBA CheckA ", False)
Case ErrorType.NotlnFile
mySCC,TotalProb l ems = mySCC.TotalProblems + 1
Set myProb = myRep.AddProblem( "Room " & RoomNumber &
" is NOT in file, ", "VBA CheckA ", False )
Case ErrorType.FixedNotlnDGN
mySCC,Fi xedP ro blems = mySCC.Fi xe dP ro blems + 1

I Chapter 31: The Standards Checker I

646

Set myProb = myRep.AddProb l emC"Roo m .. & Roo mNum ber &


.. is NOT in DGN . ... "VBA CheckA". True)
Case ErrorType.FixedNotlnFile
mySCC . FixedProblems = mySCC . FixedProblems + 1
Set myProb = myRep.AddProblemC"Room .. & RoomNumber &
.. is NOT in file .... "VBA CheckA". True)
Case ErrorType . Not FixedNotln DGN
Set myProb = myRep.AddIgnoredProblemC"Room .. &
RoomN umber &
.. i s NOT in DG N. ... "VBA CheckA". UserName. Now)
mySCC.IgnoredProblems = mySCC.IgnoredProblems + 1
Case Err orType .N otF i xed Notln Fi le
Set myProb = myRep.AddIgnoredProblemC"Room .. &
RoomNumber &
.. is NOT in file . ... "VBA Che ckA". Use rName. Now )
mySCC. Ignored Problems = mySCC. Ignored Problems + 1
End Select
End Sub

Now, in addition to adding a problem to the report stating that the


problem was not fixed (the False parameter in AddProblem), we also
add Fixed Problems and Ignored Problems.
Here is the report now:
: ra.

[cit

\\tw

r~

YeQs

0' . (). @ (~ , )')- >j.;:,,,,,,,,,

~ :B..

9 M ::l 811

:~.>~~, ~O~~f.~_~W~~. ~Q;~~~~~.--.----,-,_-,

=-_......-..,- --'-~.=~

i_ ~"

!J g~~ii~~~:=;~o~:::~:j~~~I!~~~~:'::II!~~:~~dlrtO"tJgn1.8SI300A1:f01 .pI~n.tlQn ~50"b OGtM AdRl1hl~tr4tor =5~:~11 ~~,~~4


rmtngI!iMII
I

GIO!Jn~

F!QQ( fJl~11

V8A ChcekA

<).o...>Jfl,,,il'l..

\i1),\Choclu\

:3

ClO!JflHloOll' Plan.

~OQn~'fl~Plall
_~

__=. _.

'

~O~1'Yl

111 J'$: NOT ~ ft9-,

Rftfttl) 111 "NOTinflJo


t'lnom 110 il NOT 11\ f{~.
u.,
'"..
. ___.:....._--''---_,oROqrn '10rt NOrm
"""......

."'. . . .::--'-----------~~-;;-'-""

Now, in addition to seeing problems that have not been fixed, we see
problems that have been fixed and those that have been ignored.

I Automatically Load ing Custom Standa rds Checker Add-Ins I

647

AUTOMATICALLY LOADING CUSTOM STANDARDS


CHECKER ADD-INS
Standards checker functionality can be automatically loaded through
two mechanisms. Both mechanisms use the OnProjectLoad procedure
that can be placed in a MicroStation VBA project. The OnProjectLoad
procedure is executed whenever a VBA project (.mvba file) is opened.
The code in this procedure is the same for both autoload mechanisms:
Sub OnProjectLoad ()
AddChecker
End Sub

So, the OnP rojec tL oad procedure is the same for both autoload
mechanisms. What are the two mechanisms?
1

The first is selecting "Auto-Load" in the VBA Project Manager:

When a VBA project is set to ''Auto-Load'', the project is loaded


when MicroStation starts. When the project is loaded,
OnProj ectLoad is executed and the Standards Checker Add-in we
created is loaded as well.

2 The other method deals with a configuration variable named


MS_STANDARDCHECKERAPPS. If we add the VBA project file
(.mvba file) to this variable, the VBA project is loaded when the
standards checker is initialized. This is preferable to the method
described above because standards checker code is not loaded and
added unless we specifically decide to do something with the
standards checker.

648

I Chapter 31: The Standards Checker I


REVIEW
The Standards Checker interface provides the ability to create our own
custom standards checking programming. The MicroStation VBA
documentation includes additional examples of how to further
implement the IStandardsChecker interface.

32

Using the Windows API


We added references to things such as the "Microsoft Scripting
Runtime;' "Microsoft ActiveX Data Objects;' "Microsoft CDO for
Windows 2000" and so forth. When we did this, we had instant access to
functionality not natively exposed to VBA.
Although working with the Windows API is not as simple and straight
forward as adding a reference, the process is fairly painless and the
results can be powerful.
In this chapter:
[B Declaring API calls
[B Declaring types
[B Utilizing API calls

DECLARING

API CALLS

You can declare Windows API calls in the General Declarations area of a
code module. Once declared, use the API calls as you would use any
other function or procedure.
Here is an example:

649

650

I Chapter 32: Using the Windows APII


Public Declare Function Beep Lib "kerne132 " (ByVal dwFreq As Long. _
ByVal dwDurat i on As Long ) As Long

The function name in the above declaration is "Beep:' It is an amazing


API call that beeps. It beeps as long and as high (or as low) as we ask it
to. You find API functions and procedures inside DLL (Dynamic Link
Library) files. This one is inside the kernel32.dll file. Let's try it out, shall
we?
After declaring the Beep function in the General Declarations area of a
code module, we can use it as follows:
Sub TestBeep ()
Beep 4000. 250
Beep 2000. 250
Beep 1000 . 250
Beep 500. 250
End Sub

Four beeps are heard, each lasting 114 of a second (250 milliseconds) at
different frequencies. The higher the frequency, the higher the beep.
Each beep is half the frequency of the previous frequency. This results in
four notes, each one octave lower than the previous.
The Beep API function is not the most useful API function known to
man, but for the moment, we are focusing on how to declare the
functions. We will see plenty of examples utilizing more powerful and
more useful API functions later.
Many Windows API calls are declared as functions. This means they
return a value. Oftentimes, the value they return tells us whether the
API call worked or if an error was encountered.
In addition to specifying the function's name, location (which DLL file it
appears in), and the parameters, Windows API calls often have an alias.
The alias is important when declaring an API function but we do not
use it in our code - we use the function or procedure name.

DECLARING TYPES
Some Windows API calls use types. A type is similar to an object in that
it has specific properties or members. Often, we declare a variable as one
of these types and then set some of its properties. After the properties
are set, we may use it as a parameter in an API call.

I Utilizing API Ca ll s I

651

Declare API types in the General Declarations area just like API
functions.
Public Declare Sub GetSystemInfo Lib "kerne132 " (lpSystemInfo _
As SYSTE~I_= NFO)
Pub li c Type SYSTEM_INFO
dwO em ID As Lon g
dwPageSize As Lo ng
lpM i ni mumApp li cationAddress As Long
lpMaximumApp l icati onAddress As Lo ng
dwActiveProcessorMask As Long
dwNu mberOfProcessors As Long
dwProcessorType As Lo ng
dwAl l ocationGranu l arity As Long
dwReserved As Long
En d Typ e

The GetSystemlnfo API call uses a "SYSTEM INFO" type. Once


declared we can use the procedure and type in a macro:
Sub TestSystemInfo ()
Dim mySystemInfo As SYSTEM_INFO
GetSystemInf o my Sy stemInf o
MsgBox my Sy stem In fo. dwNumberOf Proce ssors & " Processors ."
End Sub

You will see examples of more types declared as we look at more API
examples.

UTILIZING

API CALLS

There are hundreds of API calls available for our use. Those presented
here are not in any particular order, nor are they necessarily related to
one another in any way. One call may deal with the logical drives on the
computer where the other may deal with screen resolution. In any case,
those listed here should be helpful.

652

I Chapter 32: Using the Wi ndows APII


GetLogicalDrives
Public Declare Function GetLogica l DriveStr i ngs Lib "kerne132 "
Alias "GetLogicalDriveStringsA" _
(ByVal nBufferLength As Long, _
ByVal lpBuffer As String) As Long
Sub TestGet Log i ca l Dr iv eStri ngs()
Dim DriveLetters As String
Dim xSplit As Variant
Dim I As Long
Drive Letters = Space(255)
GetLogica l DriveStrings Len(DriveLetters) , DriveLetters
xSplit = Spl i t(DriveLetters , Chr(O))
Fo r I = LBound(xSp lit ) To UBound(xSp l it) - 2
MsgBox "Drive" & xSplit(I) &" Found ."
Next I
End Sub

Often, when we use Windows API calls and provide a string to a


function, and the function is going to give the string a value, we must
first buffer the string with spaces. The DriveLetters variable is an
example of this. When the variable DriveLetters enters the function
GetLogi ca 1Dri veStr i ngs, it goes in with 255 space characters in it. When
it comes out, we are given a series of drive letter characters separated by
a null character (Chr(O)). We subtract two (2) from the upper-bound
index of the xSplit array and this gives us the drive letters found on our
system.
When Tes t GetLog i ca l DriveStrings is run, we see MessageBoxes with
the drive letters of each drive on the system. All drives are returned,
those physically attached as well as mapped network drives.

GetDriveType
We can use GetLogi ca 1Dri veStr i ngs to get the drive letters on our
system, but how do we know what type of drives they are? Hard Drive?
CD-ROM Drive? Floppy Drive? We can use Get Dri veType . This example
also uses GetLogi ca 1Dr i veStr i ngs .
Public Declare Function GetDrive Type Lib "kerne132" Alias
"GetDriveTypeA " (ByVal nDrive As String) As Long
Public Canst DRIVE_CDROM

I Utilizing API Call s I


Pub 1 i c
Pub1 i c
Public
PU::J 1 i c

653

Const DRIVE - FIXED = 3


Const DRIVE_RMIDISK = 6
Const DRIVE_REMOTE = 4
2
Const DRIV~ REMOVABLE

Sub TestDriveType C)
Dim DriveLetters As String
Dim xSplit As Variant
Dim I As Long
DriveLetters = Space(255)
GetLogicalDriveStrings LenCDriveLetters). DriveLetters
xSplit = SplitCDriveLetters. ChrCO))
For I = LBoundCxSplit) To UBoundCxSplit) - 2
Debug . Pr i nt "Drive " & xSplitCI) & " is a " &
ReturnDriveTypeCCStrCxSplitCI)))
Next
End Sub
Funct i on ReturnDriveType CDr i ve Let t er As Str i ng ) As St rin g
Di m l ngDriv eTy pe As Long
l ng Dr iv eTy pe = Ge t Dri ve Typ eC Dri veLett er)
Select Case l ng Dri veType
Ca se DRIVCCDRO M
Retu r nDr i ve Type
"CD / DVD Dr i ve "
Case DRI VCF I XED
"Ha r d Dr i ve "
ReturnD r i veType
Case DRIVE_RAMDISK
"RAM Disk"
ReturnDriveType
Case DRIVE_REMOTE
"Mapped Drive"
ReturnDriveType
Case DRIVE_REMOVABLE
"Removable Drive "
ReturnDriveType
Case Else
lngDriveType
ReturnDriveType
End Select
End Function
Dr ive
Dr ive
Drive
Drive

C:\
D: \
E: \
Z: \

is
is
is
is

a
a
a
a

Hard Dr i ve
CD/ DVD Drive
Removab l e Dr i ve
Happed Drive

>;

654

I Chapter 32: Using the Windows APII


This computer has a hard Drive (C), a CD/DVD Drive (D), a removable
Drive (E) which happ ens to be a Flash drive, and a mapped drive (Z).

GetComputerName
Public Declare Function GetComputerName Lib "kerne132"
Alias "GetComputerNa meA " (SyVal l pS uffer As String,
nSize As Long) As Long
Sub TestGetComputerName ()
Dim CompName As String
CompName = Space(255)
GetComputerName CompName, Len(CompName)
CompName = Left(CompName, InStr(l, CompName, Chr(O)) - 1)
MsgB ox CompName
End Sub

Knowing the name of the computer on which our code is running is a


useful piece of information. Once again, we use a buffered string. We
look for the null character Chr(O) and get everything to the left of it.

GetVersionEx
What operating system is the computer running? We need only ask
GetVersionEx to find out.
Public Declare Funct i on GetVersionEx Li b "k erne 132 " Alias
"GetVersionExA" (lpVersionInformation As OSVERSIONINFO) _
As Long
Public Type OSVERSIONINFO
dwOSVersionInfoSize As Long
dwMajorVersion As Long
dwMinorVersion As Long
dwBui l dNumber As Lo ng
dwPlatformId As Long
szCSDVersion As String * 128
End Type
Public Const VER PLATFORM WIN32 WINDOWS
Public Const VER_PLATFORM_WIN32_NT = 2

I Utilizing API Calls I

655

Sub TestOSVersion ()
Dim myVerlnfo As OSVERSIONINFO
Dim strSer vi ceP ack As St rin g
myVerlnfo.dwOSVersionlnfoSize = 148
GetVersionEx myVerlnfo
Select Case myVerlnfo . dwPlatformld
Case VER_PLATFORM_WI N32 _WINDOWS
Select Case myVe r ln f o . dwMi norVe r sion
Case 0
MsgBox "Wi ndows 95 "
Case 10
MsgBox "Wind ows 98 "
Case 90
MsgBox "W indows ME"
End Select
Case VER_PLATFORM_WIN32_NT
Select Case myVerlnfo . dwMajorVersion
Case Is <= 4
MsgBox "Windows NT Build " &
myVerInfo.dwBuildNumber
Case 5
Select Case myVerInfo.dwMinorVersion
Case 1
strServicePack = myVerInfo . szCSDVersion
strServicePack = Left(strServ icePack . _
InStr(l. strServicePack. Chr(O)) - 1)
MsgBox "Windows XP Build" & _
myVerInfo.dwBuildNumber & vbCr &_
strServicePack
Case 2
MsgBox "Windows .NET Server Build " &
myVerInfo.dwBuildNumber
Case Else
MsgBox "Windows 2000 Build " &
myVerInfo.dwBuildNumber
End Select
End Select
End Se l ect
End Sub

Windows XP Build 2600


Service Pack 2

A MessageBox displays the operating system and in some cases the build
number and service pack.

I Chapter 32: Using the Windows APII

656

Sleep
At times, we need to temporarily pause program execution. The Sleep
func tion allows us to specify how many milliseconds to sleep.
Public Declare Sub Sle ep Lib "kerne132 " (ByVal _
dwMi lli seconds As Long)

Start Time: 4:35:37 Prvl


End Time: 4:35:38 P~l

OK,

Sub TestSl eep ()


Dim StartTime As Date
Dim EndTime As Date
StartTime = Now
Sleep 1000
EndTime = Now
MsgBox "Start Ti me : " & FormatDateTime(S t artTime. vbLongTime) _
& vbCr & "E nd Time: " & Form atDa t eTime(EndT im e . vbLongTime)
End Sub

In this example, we get the current time into a variable named


"StartTime;' sleep for 1 second (1,000 milliseconds), and then get the
current time into a variable named "EndTime': The last thing we do is
display the StartTime and EndTime in a MessageBox.

FindExecutable
The same .pdf file may be opened with Adobe Acrobat Reader 7 on one
computer and Adobe Acrobat 6 on another computer. Which program
is registered to open a .pdf file? Which program is registered to open
a .jpg file? Fi ndExecutab 1e tells us the path to the program that is
registered to open a particular file type.
Public Declare Function FindExecutable Lib "shel132.dll "
Alias "FindExecutab l eA"
(ByVal lpFile As String, ByVal lpDirectory As String, _
ByVal lpResult As String) As Long
Sub TestFindExecutable()
Dim strFileName As String
Dim strDirName As String
Dim strExeFile As String
strFileName = "eula . pdf "
strDirName = "C: \ Program File s\ Bentley \ Micr oStatio n"
s t r ExeF il e = Spac e( 255)
FindExecutable strFileName , strDirName , strExeFile

I Utilizing API Calls I


strExeFile = Left(strExeFile, InStr(l, strExeFile , Chr(O)) MsgBox strExeFi l e
End Sub

657
1)

C: \Progr am Files\Adobe\Acrobat 6. O\Acrobat\Acrobat. exe

GetDiskFreeSpace
GetDiskFreeSpace gives us information about the provided disk name.
Public Declare Function GetDiskFreeSpace Lib "k erne132 " Alias
"GetDiskFreeSpaceA " _
(ByVal lpRootPathName As String, lpSectorsPerCluster As Long, _

lpBytesPerSector As Long. lpNumberOfFreeClu ste rs As Long. _


lpTota lN umberOfClusters As Long) As Long
Sub Te stF reeDisk Spa ce()
Di m mySpace As Long
Dim lngSectorsPerCluster As Long
Dim lngBytesPerSector As Lo ng
Dim lngFreeClusters As Long
Dim lngTotalClusters As Long
Dim FreeBytes As Double
Dim Tota l Bytes As Double
Dim PercentFree As Double
GetDiskFreeSpace "C:\ " . lngSectorsPerCluster._
l ngBytesPerSector , lngFreeClusters . lngTotalClusters
FreeBytes = FormatNumber(lngFreeClusters / 1000 * _
lngBytesPerSector * lngSectorsPerCluster. 2)
TotalBytes = FormatNumber(lngTotalClusters / 1000 *
lngBytesPerSector * lngSectorsPerCluster. 2)
PercentFree = Round((FreeBytes / Tota l Bytes) * 100 . 2)
MsgBox "Free Bytes : & FormatNumber ( FreeBytes , 2. False, Fa l se, Tr ue )
& KB & vbe r &

"Total Bytes: & FormatNumber (TotalBytes, 2, False, False, True ) &


" KB " & v bC r &

"Percent Free: %" & Percent Free


End Sub

658

I Chapter 32: Using the Windows APII

Free Bytes: 1,969,815.55 KB


Total Bytes: 39,999,500.29 KB
Percent Free: %4.92

GetSystemMetrics
Knowing information about the computer on which our code is running
is very helpful. We can use GetSystemMetrics to return a wealth of
information.
Publ ic Declare Functian GetSystemMetrics Lib "user 32 "
(ByVal nIndex As Long) As Lang
Public
Public
Public
Public
Pub li c
Pub li c
Public
Public
Public
Public
Public
Public
Public
Pub lic
Public
Public
Public
Public
Public
Public
Public
Public
Public
Public
Public

Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst
Canst

SM CMETRICS = 44
SM CMOUSEBUTTONS
43
SM_CXBORDER = 5
SM_CXCURSOR = 13
SM_CXDLGFRAME = 7
SM CXDOUBLECLK = 36
SM CXFIXEDFRAME
SM CXDLGFRAME
SM_CXFRAME = 32
SM_CXFU LLSCREEN
16
SM_CXHSCROLL = 21
SM_CXHTHUMB = 10
SM_CXICON = 11
SM_CXICONSPACING
38
SM_CXMIN = 28
SM CXMINTRACK
34
SM_CXSCREEN = 0
SM_CXSIZE = 30
SM_CXSIZEFRAME = SM_CXFRAME
SM_CXVSCROLL = 2
SM_CYBORDER = 6
SM_CYCAPTION = 4
SM_CYCURSOR = 14
SM_CYDLGFRAME = 8
SM_CYDOUBLECLK = 37
SM CYFIXEDFRAME
SM CYDLGFRAME

I Uti lizing API Ca ll s I


Pub 1 i c
Pub 1i c
Pub 1 i c
Pub 1 i c
Pu b1i c
Pub 1 i c
Pub 1 i c
Pub 1 i c
Pub 1i c
Pub 1i c
Pub 1i c
Pub 1 i c
Pub 1i c
Pub 1 i c
Pub 1i c
Pub l i c
Pub 1 i c
Public
Publ i c

Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const

659

SM_CYFRAME = 33
SM CYFULLSCREEN
17
SM_CYHSCROLL = 3
S~I_CY :CON = 12
SM CYICONSPAClt'-iG
39
SM_CYMENU = 15
SM CYKANJIWINDOW
18
SM_CYMINTRACK = 35
SM_CYMIN = 29
SM_CYSCREEN = 1
SM_CYSIZE = 31
SM_CYSIZEFRAME = SM_CYFRAME
SM_CYVSCROLL = 20
SM_CYVTHUMB = 9
SM_DBCSENAB LED = 42
SM_D EBUG = 22
SM MENUDROPAL IGN MEN T 40
SM_MOUSEPRESEN T = 19
SM_S WAPBUTTON = 23

Sub TestSyste mM etr i cs ()


MsgBox "Screen Resolution : " & vbCr &
GetSyst emMe tri cs(SM_CX SCRE EN) & " X " &
Ge t Sys t emMetr i cs(SM_CYSC RE EN)
MsgBox "Mo use Buttons : " & _
Ge t SystemMetrics(SM_CMOUS EBUT TONS)
End Sub

The procedure TestSystemMetri cs tells us the screen resolution and how


many buttons are on the mouse connected to the computer.

GetTickCount
How long has it been since the computer was started? GetTickCount
answers this question in milliseconds.
Public Declare Function GetTickCount Lib "kerne 132" Alias
"GetTickCount " () As Long

4458484
4460484

OK

Sub TestTickCount ()
Dim StartTicks As Long
Dim EndTick s As Long
StartTicks = GetTickCount

660

I Chapter 32: Using the Windows APII


Sl eep 2000
EndTicks = GetTickCount
MsgBox StartTicks & vbCr & EndTicks
End Sub

GetUserName
Who is logged onto the computer?
Public Declare Function GetUserName Lib "advapi32.dll" Alias_
"GetUserNameA" (ByVal lpBuffer As String. nSize As Long) As Long

Sub TestUserName()
Dim UserName As String
Di m xSp lit As Var i ant
User Name = Space(255)
GetUserName UserName. Len ( UserName)
xSplit = Sp l it(UserName . Chr(O))
User Name = xSp l it(O)
MsgBox UserName
End Sub

Get User Name is useful for logging who is performing what function. If we

get the ComputerName as well, we will know who, where, what, and we
can know when by using the Now function. As for the question "Why?"
Microsoft is still working on that API call.

NOTE: The MicroStation application object has its own UserName


property so we don't need this API call if we are using MicroStation
VEA.

GetWindowsDirectory
Where is Windows Installed? C:\Winnt? C:\Windows?
GetWindowsDirectory tells us.
Public Declare Function GetWindowsDirectory Lib
"kerneI32 " Alias
"GetWindowsDirectoryA " (ByVal lpBuffer As String, _
ByVal nSize As Long) As Long
Sub TestWindowsDir()
Dim WindowsDir As String

I Utilizing API Ca lls I

661

Dim xSp l it As Variant


WindowsDir = Space(255)
GetW i ndowsDirectory Wi ndowsD i r. Ler(WindowsDi
xSplit = Split(WindowsDir. Chr(O))
WindowsDi r = xSp l it(O)
MsgSox Wi ndowsDir
End Sub

r )

Knowing where Windows is installed can be helpful if we are looking for


a particular file (such as in the System32 directory) or if we want to add
our own files or folders under the Windows directory.

LogonUser
Security is on everyone's mind. How do I know that changes being made
on someone's machine are being made by the person that is logged on?
How do I know someone else didn't slide into Fred's cubicle while Fred
is at lunch only to goof up a file?
We know how to get the current user. Let's take a look at how we can ask
the user for a password and with the Windows API validate that the
password entered matches that of the password on the system.
Private Declare Function

Logon User

Li b " Advap i 3Z " Al i as " LogonUserA " _

(SyVal l ps zUse rna me As St r i ng . SyVa l l pszDomain As String . _


SyVal lp sz Passwo r d As Strin g. SyV al dw Logo nTy pe As Long .
Sy Val dwLogonProvider As Long . phToken As Long) As Long
Function CurrentUserName () As String
Dim UserName As Str in g
Dim xSplit As Variant
UserName = Space(255)
GetUserName UserName. Len(UserName)
xSplit = Split(UserName. Chr(O))
UserName = xSplit(O)
CurrentUserName = UserName
End Function
Sub TestLogonUser()
Dim Successful As Long
Dim PasswordEntry As String
PasswordEntry
Successful

InputBox( " Re-Enter Password: ".

"L ogon Va l idati on " )

LogonUser(CurrentUserName ...... Pa ssw o rdEntry . 2. O. 0)

662

I Chapter 32: Using the Wi ndows API I


Sele ct Case Successf ul
Case 1
MsgBox "You have been validated.", vblnformation
Case Else
MsgBox " Invalid Username/Password Combination. ", vbCritical

End Se le ct
End Sub

This example uses an InputBox for entering the password. Although this
is not necessarily the best way to ask for a password (because it is visible
to anyone looking over one's shoulder), the code still demonstrates the
API call. We have a function named Cur ren tU ser Name that returns the
current user name. Notice that we leave the domain an empty string. If
we were on a domain, we would specify it in the provided parameter.
LogonUser. Very powerful.

MessageBeep
Feedback is good, right? In addition to visual feedback we can provide
audible feedback through a variety of methods. The MessageBeep
function plays the .wav file currently applied in the "Sounds and Audio
Devices" section of the Control Panel. These are the sounds we hear
when different MessageBoxes display, only we get the sound without the
MessageBox.
Pub l ic De clare Funct i on Mess ag eBeep Li b "us e r3Z "
(ByVal wType As Long) As Long
Pub l ic
Publi c
Public
Public
Public

Const
Const
Const
Const
Const

MB OK = &HO&
MB_ ICONS TOP = &H1 0&
MB_ICO NQU ESTI ON = &HZO&
MB_ICONEXC LAMATION = &H30&
MB_ ICO NAS TER IS K = &H40&

Sub TestMessageBeep ()
Message Beep MB_OK
Sleep 500
MessageBeep MB_ ICONSTOP
Sleep 500
MessageBeep MB_ ICONQUESTION

I Utilizing API Calls I

663

Sl eep 500
MessageBeep MB_IC ONEXC LAMA TION
Sleep 500
MessageBeep MB_ICONASTERISK
End Sub

PlaySound
Here is another API call that deals with audible feedback. PlaySound
allows us to specify which .wav file is to be played and how it is to be
played.
Public Dec l are Functio n

PlaySound

Lib "winmm . d l l " Alias " PlaySoundA " _

(ByVa l l pszName As String , ByVal hModule As Long, _


ByVa 1 dw Fl ags As Long ) As Lon g
Public Const SND_SY NC = &HO
Public Const SND_ASYNC = &H1
Public Const SND_LOOP = &H8
Sub TestPlaySoundA ( )
Pl aySound "C: \Windows\Media\ch i mes .wav ", 0 , SND_SYNC
PlayS ound "C:\Windows\Media\chord.wav " , 0 , SN D_SYNC
End Sub

This first example uses the "SND_SYNC" flag. This means the
chimes.wav file plays until it is finished and then the chord.wav file
plays.
Sub TestPlaySou ndB ()
Pl aySound "C: \W i ndows\Media\chimes .wav " ,
Sleep 150
Pl aySo und "C: \ Win dows\ Med i a\ch ime s . wav " ,
Sleep 200
Pl aySound "C: \ Wi ndows\ Media\chi mes.wav " ,
Sleep 250
PlaySound "C:\Windows\Med i a\ch i mes . wav ",
Sl eep 300
PlaySound "C: \ Windows\Media\chimes.wav " ,
End Sub

0 , SND_ASYNC
0, SND_ASYNC
0 , SND_ASYNC
0 , SND_ASYNC
0 , SND_ASYNC

When we use the "SND _ASYNC" flag, the specified file begins playing
and the code continues executing without waiting for the file to finish
playing. By placing sleep calls between each line of code, we hear 150

664

I Chapter 32: Using the Windows APII


milliseconds of chimes.wav and then we hear 200 milliseconds, then
250 milliseconds, and so forth . P1 aySound can play only one .wav file at a
time, so each time it is called, any file already being played is stopped
and the new file is played.
The next example would be extremely annoying if we didn't know how
to turn it off. We use the "SND_LOOP" flag in conjunction with the
"SND _ASYNC" flag to play the sound over and over. The sound
continues to play even after the procedure has finished executing. Even
if the VBA project is unloaded, the sound continues to play over and
over and over until MicroStation is closed down or TestP1 aySoundD is
executed.
Sub TestPlaySoundC ()
PlaySound "C:\Windows\Med i a\chord . wav ", 0 , _
SND_LOOP + SND ASYNC
End Sub
Sub Te s tPl aySoundD ()
Pl aySound vbNull Stri ng, 0 , 0
End Sub

Shell Execute
In a previous example, we were able to discover which application was
registered to open a specific file. Shel l Exec ute actually opens the file
using the application registered to handle the file.
Public Declare Function ShellExecute Lib "she l 132.dll" Alias
"ShellExecuteA" (ByVal hwnd As Long, _
ByVal lpOperation As String, ByVal lpFile As String, _
ByVal lpPa rameters As String, ByVal lpOirectory As String, _
ByVal nShowCmd As Long) As Long
Sub TestShellExecuteA()
ShellExecute 0 , "OPEN", _
"C: \Windows", vbMinimizedNoFocus
"Greenstone.bmp " ,
End Sub
Sub Te stS hell ExecuteB ()
Shell Execute 0, "OPEN", _
"Greenstone.bmp ",
"C:\Windows", vbMaximizedFocus

I Utilizing API Calls I

665

End Sub

We show two examples here. One executes the program and opens the
file minimized and the other executes the program and opens the file
maximized.
In a previous chapter we worked through an example that created
an .html file. We could use She 11 Execute to display the file immediately
after it is created. This is much better than creating the file and then
asking the user to find the file and double-click on it.

SHGetFilelnfo
SHGetFileInfo can be used for a variety of things. One thing it can do is
tell us what kind of a file we are looking at.
Public Type SHFILEINFO
hlcon As Long
ilcon As Long
dwAttributes As Long
szDisplayName As String * 255
szTypeName As String * 80
End Type
Pub 1i c
Publ i c
Publ i c
Public
Publ i c
Pub 1i c
Pub 1i c
Public
Public
Pub 1i c
Pub 1i c
Pub lic
Pub l i c
Publ i c
Publ i c

Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const

SHGFI_ATTRIBUTES = &H800
SHGFI - EXETYPE = &H2000
SHGFI - DISPLAYNAME = &H200
SHGFI - ICON = &HIOO
SHGFI - ICONLOCATION = &HIOOO
SHGFI - LARGEICON = &HO
SHGFI - LINKOVERLAY = &H8000
SHGFI_OPENICON = &H2
SHGFI - PIDL = &H8
SHGFI - SELECTED = &HIOOOO
SHGFI - SHELL ICONSI ZE = &H4
SHGFI - SMALLICON = &Hl
SHGFI - SYSICONINDEX = &H4000
SHGFI_TYPENAME = &H400
SHGFI - USEFILEATTRIBUTES = &HIO

Sub TestGetFilelnfo()
Dim myFI As SHFILEINFO

666

I Chapter 32: Using t he Window s API I


SHGetFilelnfo " C: \ test . dgn " , SHGF I _ATTRIBUTES, myFI, Len ( myFI), _
SHGFI_DISPLAYNAME + SHGFI_ICON + SHGF I _T YPENAME
Debug.Print Replace(myFI.szDisplayName, Chr(O), '''')
Debug.Pr'nt Replace(myFI.szTypeName, Chr(O), "")
End Sub

The file name and the file type display.


test.dgn
Bentley ~! i croStation Design File

REVIEW
We have touched on a handful of Windows API calls here. There are
hundreds more. Some API calls deal with the display; they allow us to
draw to specific windows. Other API calls deal with reading and writing
files. Yet others deal with system memory.
Entire books and websites have been dedicated to the topic of Windows
API calls. Windows API calls are one of the reasons why we can say "Yes,
you can do that with VBA:'

33

Using Third Party


ActiveX Controls and
DLLs
We all know there is no reason to keep reinventing the wheel. Using
existing resources speeds the development process and can result in a
more bug-free application.
In this chapter:
[B Using ActiveX controls
[B Using DLLs

USING ACTIVEX CONTROLS


ActiveX controls are used in our graphical user interface development.
We have already used TextBoxes, ComboBoxes, CommandButtons,
labels, and other controls.

667

668

I Chapter 33: Using Third Party ActiveX Controls and DLLs I


-----------: ::~N ';( ~;~: , ~
I~- A abl '@;j IiliI p- I.-..J O...J ..!..i..:J "".'!J 11

When we insert a UserForm in our


VBA project, the Toolbox dialog box
normally displays. Right-clicking on the
Toolbox allows us to add Additional

Controls.

TooJboH"J~~' .. ~:~
Contlols

<

e,dditional Controls ...

Delete ItJ:f

Customize Item

e,vailable Contlols:

rl8] Mi~I~-;~ft F-;ms 2.0Te.tB;;;-- - - ;AJ,


118] Miclosoft Forms 2.0 ToggleButton

1
'0 Miclosoft Help 2.0 Contents Control

Cancel

o Miclosoft Help 2.0 Inde. Contlol


o Miclosoft Hielalchical Fle.Glid Control 6.0 (S
o Miclosoft IE Obiect 'Wlappel Sample Control
o Miclosoft ImageComboBo. Control 6.0 (SP6)

o Microsoft ImageList Control 6.0 (SP6)


w:
o Miclosoft ImageList Control. velsion 5.0 (S P;
o MiClOsolt InkE dit Contlol
o Miclosoft InkPicture Contlol

Show -,,---- _. _ .. - ......,

o ~elected Items Only


Forms 2. 0 SpinButton-- "
Location

C \ 'WIN DO'WS \S ystem32\FM 20.DLL

As you scroll through the list of items, you see a great variety of
Controls. The list on each computer will be different because controls
are added when software is installed. Some of the controls shown in the
image above are installed when Visual Basic 6.0 is installed.
The fact that a control is shown in the list does not mean you can use it,
because some controls require a license. Let's look at a few controls that
are available to us.

I Using ActiveX Controls I

669

SUIl

Mon
1

30

Tu e

Wed

Thu

Fri

Sat

10

11

-12

13

14

-15

16

17

18

19

20

21

22

23

24

2S

26

27

28

29

30

-'

13

:3

10

For
demonstration
purposes, I will use the
"Calendar Control 11.0"
control in this example.
This control is installed
with Microsoft Office.
Select it from the list of
Available Controls and
click the OK button.

Once it shows up in the Toolbox we can place it on our form with other
controls we want to use.
Clicking on the button displays the selected date.
Pr i vate Sub bt nDi s play Select i on _Cl i ck()
MsgBox Calendar l .Va l ue
End Sub

If you are writing an application that uses a non-standard ActiveX


Control, you need to make sure that you can successfully deploy the
application to other computers.
In this example, I would need to distribute the .mvba file (the
MicroStation VBA Project file) as well as the Calender Control (the .ocx
file). But giving someone an .ocx file is not enough. The control needs to
be registered on the user's computer.
Registering an ActiveX Control is quite simple. From the Start button in
Windows, click Run and type "cmd" in the window and then click the
OK button.
When a Command window opens, you simply need to type:
r egsvr32 "c : \program f i les\ Mi cr osoft Offi ce\Officell\mscal . ocx "

670

I Chapter 33 : Using Third Pa rty ActiveX Controls and DLLs I


and press the <Enter> key. If the path where the .ocx file is different, it
should be reflected in the command.

(;t)

DliRegisterServer in c:\Program Files\Mjcrosoff Office\Officell \mscal. ocx succeeded . .

As mentioned, the ActiveX Control should be tested on machines where


it was not previously installed to make sure the application developed
can be successfully deployed to others.
There are many places on the Internet you can go to find ActiveX
Controls. And many sites allow free evaluations of the Controls prior to
purchase.
In addition to purchasing ActiveX Controls, you can create your own
using Visual Basic 6.

USING EXISTING

DLLs

There are two fundamental ways to use DLLs in our programming. Each
method has been used in this book already.
Method 1: Declaring DLL Functions as in the Windows API chapter.
Method 2: Adding a reference to the D LL and using it.
Since we have already devoted a chapter to using the Windows API, we
will turn our attention to adding references.
We have added references to a number of different DLLs but have done
so in the interest of discussing specific topics. Now, we are going to look
at a number of references in greater detail.
Let's first look at the "Microsoft Shell Controls and Automation"
reference.

I Using Existing DLLs I

671

So, we just added a new reference to our VBA project. Perhaps we


haven't used it before. Perhaps we have used it a little bit. How can we
find out what this reference will do for us? The first thing we should do
is take a peek at the Object Browser and filter on the reference we just
added.

Classes
Q <globals>
@I DF Constraint
@j DSheliFoldeNiewEvents
@I Fold er
@I Fold er2
@I Folder3
@I
@I Folde rltems
@I Folde rltems2
@I Folderltem s3
~ Fo lderltemVe rb
~ Fold erlte mVerbs
@I lNeVINIIDEvents
@j IPasspo rtCli entSe rvices
@I lSea rchComman dExt

Class

Member

Members of'F olde rltem'

r:Jj

'1
!t: /1J

r1jI Ap pli ca tion


@i' GetFol der
r1jI GetLink

~ :i "'~ InvokeVe rb

I'''; r1jI IsBrowsab le


,- 1@i' IsFil eSystem
; jl r1jI
t '\1 r1jI

UJ '!'

@'I
r1jI
@i'
r1jI

IsFol der
IsLink
ModifyDa te
Name
Parent
Path
Size
Type

" "~Ve rb s

,~

Clas s Folderltem
Member of Shell32
Oefinrtion of interface Foldernern

One of the best ways to discover what a particular reference can do is


clicking on an object, which displays the members of the object. Let's
look at some code.
Sub TestShellA( )
Dim myShell As New Shel132.Shell
Dim myFolder As Shel132.Folder3
Dim myFolderZ As Shel13Z.Folder3
Dim myItem As Shel13Z.Folderltem
Set myFol der = myShell . BrowseForFol der(O. "Br owse " . 0)
MsgBox myFolder.Self.Path
For Each myItem In myF older. Items
Debug . Print myItem . Name & vbTab & myltem.Type
Next
End Sub

672

I Chapter 33: Using Third Party ActiveX Controls and DLLs I


First, we are asked to select a folder.
. ...
'

,'"'

'"

~,,,,..,.

~~~-

~ ,~'

,-.

..

!l!~~I!lr.fl;l.I.~~! . ".'", ":', ....~...w:., i, ,J~J~


Browse

El

fO

Bentley

ID Documentation
ID Licensing

El

C)!IiMto

til
assemblies
tillD config
ID docs
til ID featuremodeling
ID icons
til
mdl
til

to

~i

.!~,
[ Make New Folder

After displaying the path of


the selected folder in a
MessageBox, we display
each item in the folder in
the Immediate window.

OK

~ l,,-_c_a_nc_el~

assemblies File Folde~


at17l.dll
DLL Fi le
automationdgn.dll
DLL File
bdtidoc.htm HTML Document

And we see the name of the object as well as its type.


One of the great things about the Shell32 BrowseForFo 1der method is
that we can specify a root folder.
Sub TestShellB()
Dim myShell As New Shel132 . Shell
Dim myFolder As Shel132.Folder3
Dim myFolder2 As Shel132.Fo ld er3
Dim myItem As Shel132.FolderItem
Set myFolder = myShel l .BrowseForFolder(O, "Browse ", 0,_
"C:\Program Files\Bentley")
For Each myItem In myFolder.Items
Select Case myItem.Type
Case "F ile Folder"
Debug.Print "Folder: " & myItem.Name
End Select
Next
End Sub

I Using Exi sting DLLs I


Now, when a user runs
this, he must select either
the root folder or a folder
in the root folder's path.

Browse

fQ

673

Documentation

This is especially helpful


when you ask the user to
select something from a
Project folder. It is faster
for the user because it
[ ~lake New Folder I
Cancel
begins in the correct
folder. It is good for you
because you know the user will select something from the root folder
path.
Ie:, Licensing

iii

~licroStation

Here are a few more examples. They are all harmless, even the
ShutdownW i ndows call. When you use ShutdownWi ndow s, you should be
prompted as to what you want to do. Simply click Cancel and everything
will be OK. It is a good idea to save open documents before using this
call, just to be safe.
Sub TestShellC ()
Dim myS he11 As New Shel132 . She 11
myShe1 1.Fi ndComputer
En d Su b
Sub Tes t She ll D()
Dim myShe11 As New Shel132.She11
myShe 11 . ShutdownWi ndows
End Sub
Sub TestShel l E()
Dim myShe11 As New She l1 32 . She11
myShell.Open "C; \Program Fi 1e s\Bent1ey"
End Sub

674

I Chapter 33: Using Th ird Party ActiveX Controls and DLLs I

Back

[~ I jJ Search [e~

Folders i

[ill].

i Address lid C:\Program Files\Bentley


v ,j ~ Go
~~::~~~~~~A~~Na~m~e~~~--------~------~S~i2e~T~yp~e~~
File and Folder Tasks
~
lrcj~~~~~~6:t:~t:r;;6J
File Folder

ro

IiiJ

~'lake a new folder

~'j! ICl Licensing

",c' IClMicroStation

File Folder
File Folder

Publish this folder to


the Web
Share this folder

"Open" displays the contents of the provided path in an Explorer


window.

Microsoft Scripting Runtime


We have already used the Microsoft Scripting Runtime. Let's explore it
in greater detail. The primary object we will work with is the File System
object.
Our first example looks for all "Bentley MicroStation Design File" files
in a specific folder:
Sub TestFSOA()
Di m myFSO As New Scripting.FileSystemObject
Dim myFolder As Scripting. Folder
Dim myFile As Scripting . File
Set myFolder = myFSO . GetFolder("C:\Program Files\Bentley\MicroStation")

For Ea c h my Fi 1e I n my Fold e r . Fi 1e s
Select Case myFile.Type
Case "Bentley MicroStation Design File"
Debug.Print myFile.Name
End Select
Next
End Sub

The Immediate windovv' displays the names of the files matching the
specified criteria.
In addition to looking at files in a folder, you can get the subfolders in a
given folder and all of its subfolders as well. The next example provides a

I Using Existing DLLs I

675

comprehensive listing of all folde rs under a root folder. This utilizes a


technique called recursive execution (see Chapter 30).
Sub TestFSOB ()
Dim myFSO As New Scripting.FileSyste mObjec t
Dim myFo l der As Scripting. Folder
Dim mySubFolder As Scripting. Folder
Set myFolder = myFSO . GetFolder( "C:\Program Files\8entley " )
For Each myFolder In myFolder.SubFolders
TraverseFolders myFolder
Next
En d Sub
Sub TraverseFolderS (Fo l de rIn As Sc ri pt i ng. Folder)
Dim NextSubFolder As Scr i pting. Fo l der
De bu g.Pr i nt FolderIn .Pat h
Fo r Each NextSubFo l der In Folde r In.S ubFolders
TraverseFo lders Nex tS ubFo lder
Next
End Sub

C:\Program
C:\Pr ogram
C:\Program
C:\Program
c: \ Program
c: \ Program
c: \ Program
c: \ Program
C:\Pr ogram
c: \ Program
C: \ Program
c: \ hogram
C:\Program
C:\hogram
c: \ Program
C: \Program

Files\Bentley\Documentation
Files\Bentley\Licensing
Files\Bentley\HicroStation
Files\Bentley\HicroStation\assemblies
Files\Bentley\HicroStation \ assemblies\ECFrame1>Jork
Files\Bentley\ lhcroStat ion\ assemblies\ECFrame1>Jork \ extensions
Files\Bentley\ lhcroStation\ assemblie3\JSpace
File 3\Bentley\lhcroS tation\ a33emblie3 \ JSpace \AddIn3
Files\Bentley\HicroStation\as,emblie3\JSpace\managed
Files\Bentley\ lhcroS t ation\ aS3emblies\JSpace \ resource
Files\Bentley\HicroStation\a33emblies\JSpace\unmanaged
Files\Bentley\HicroStation\ config
File,\Bentley\HicroStation\config\appl
Files\Bentley\HicroStation\config\database
File3\Bentley\lhcroStation\ config\ system
Files\Bentley\HicroStation\docs

This technique is useful when performing batch operations.


You can use the File System object to get drive information as well as file
and folder information.
Sub TestFSOC ()
Dim my FSO As New Scrip ti ng . Fi leSystemObject
Dim myDr i ve As Scripting . Drive
Dim I As Long

676

I Chapter 33: Using Third Party ActiveX Controls and DLLs I


For I = Asc("A " ) To Asc( "Z" )
If myFSO.DriveExists(Chr(I)) The n
Set myDrive = myFSO.GetDrive(Chr(I) & ":\")
If myDrive . DriveType = Remote Then
Debug.Print "Drive: " & myDrive .D riveLetter
Debug.Print "Share Name: " & myDrive.ShareName
Debug.Print "Volume: " & myDrive.Volu me Name
Debug.Print "T otal Space: " & _
FormatNumber(myDrive.TotalSize, _
0, False , False, True) & " Bytes"
Debug.Pr i nt
End If
End If
Next
End Sub

81,956,655,104 Bytes

This example gets all network drives and displays information about
each drive.
In the next example, we read an ASCII File using the File System object.
Sub Tes t FSOD()
Dim myFSO As New Scripting.FileSystemObject
Dim myFile As Scripting . File
Dim myTS As Scripting.TextStream
Dim strWhole Fi le As String
Dim xSplit As Variant
Set myFile = myFSO .G etFi l e(
"C: \Program Files\Bentley\MicroStation\config\msconfig.cfg")

Set myTS = myFile.OpenAsTextStream


strWholeFi le = myTS.ReadAll
xSplit = SplitCstrWho l eFile , vbCr)
myTS . Cl ose
End Sub

I Using Existing DLLs I

677

TestFSOD opens the msconfig.cfg file. We use the "ReadAll" method of


the TextStream object. We then split the file into lines by looking for
carriage return characters in the file. Last, but not least, we close the file.

Ex ressron

Value

T e

Come~

Ir.M~~x!mIl"III<ElxPlrelslsiolnlnmldlelfinledlinlclomlel~1>1I1I1I~Elm~IYIiIm~miIiMomd~u'
eI1~.Te!~~Fs~o~DIIIIIII~
lro'l'
,I..

e- xSpl~(O)
"#------------------------------------------------------------ SIring
Module1 .T e~FSOD
e- xSpl~(1)
"#"
SIring
Module1.TestFSOD
II-

ee-

xSpl~(4)

"# msconfig.cfg - Main MicroSlation Configuration F SIring


"#"
SIring
"# Currem Revision:"
SIring

xSpl~(S)

"# $RCStile: msconfig.cfg,v $"

xSpl~(2)
xSpl~(3)

SIring

Module1.TestFSOD
Module1.Te~FSOD

Module1
Module1

.Te~FSOD
.Te~FSOD

If you add a watch to the variable xSplit, you see that the file has been
successfully read and split into the variable xSplit. You can now look at
each line in the file one by one.
Now we use the File System object to write a file by creating a small
HTML file.
Sub TestFSOE()
Dim myFSO As New Scripting.FileSystemObject
Dim myFile As Scripting.File
Dim myTS As Scripting.TextStream
Set myTS = my FSO.CreateTextFile("C:\test.htm", True)
myTS.Writeline "<ht ml>"
myTS.Write l ine vbTab & "<table width=200 border=l>"
myTS .Writeline vbTab & "<tr><t d>Number</td><td>Name</td></tr> "
myTS. Wr ite l i ne vbTab & "<tr><td align=cen te r>l</td>" & _
"<td>Jerry</td></tr>"
my TS .Writeline vbTab & "<tr><td align=center>2</td>" & _
"<td>Candice</td></tr> "
myTS.Writeline vbTab & "</table> "
myTS. Wri tel i ne vbTab & "</html> "
myTS.Close
End Sub

678

I Chapter 33: Using Third Party ActiveX Controls and DLLs I


~""'~ l'f~'X,~"",,1::' ."~*<;Jf"::;J{;v1~";r~;l<',""'/*:J:

t "};) ~";::'''''>:(.:w>''~....~ .... t'.}~'''Y)'''''''

...... ,- .... ,"" "'"

,~ ~;~~s~,:~~e!,,~pll!,r~J~~~~~~ ,
! File Edit View Favorites Tools Help

C Back
. :............. ...............
j

~ ~

". ... .. . . .. . .

LAddres~ I~ C:\test.htm

2
@l Done

til lf J search.... , .. .
. ......., ....1.....

t'i;I. ~ .,~~ j unks~: ,L~ ::.

!Candice

Ii, I 1. .~ My Computer

Nothing fancy, this is just a simple HTML file. When we wrote ASCII
text files before, we used standard VBA calls such as "Open" and "Print".
The last File System object we will look at is the Dictionary object,
which allows us to add item pairs (Key and Item) to a ready-made
collection.
Sub TestFSOF( )
Dim myFSO As New FileSyste mObject
Dim myDir As Scripting.Folder
Dim myDictionary As New Di ct ionary
Dim I As Lo ng
Set myDir = myFSO.GetFolde r ( "C:\Progra m Files \ 8entley " )
RecursiveFo l der myDir. my Dicti on ary
For I = 1 To myDict i onary.Count
Debug.Print myDictionary.Keys(I - 1) & vbTab &
my Di c t ion a r y . It ems ( I
1)
Next
End Sub
Sub RecursiveFolder(FolderIn As Scripting.Folder. _
DictionaryIn As Scripting.Dictionary)
Dim myFile As Scripting.File
Dim mySubDir As Scripting. Folder
For Each myFile In Folder In .Files
If UCase(R ight(my File .Name . 4))
" . CHM" Then
Dictionaryln.Add myFile.Name. myFile.Path
End If
Next
For Each mySubDir In FolderIn.SubFolders

I Using Existing DLLs I

679

RecursiveFolder mySub Dir. Dictio naryI n


Nex t
End Sub

Te st FSO F begins in the folde r C:\Program Files\Bentley and looks for


all .chm (help) files. When one is found it is added to a Dictionary
object. After all files are found, each key and item in the Dictionary is
printed to the Debug window (Immediate Window).
basic he l p. chIn c: \Program Files\Bentl ey\ Documentati on\ basic help. chIn
\~'[
mi c r o;t ati on . chIn
C: \ Pro g ram FileS\Bentl ey\ Documentati on\mic r ostati on . chIn
microstationvba. chIn c: \ Program Files\Bentley\Ui c roStation\microstat ionvba. chIn
r'!ri
readme_microstation. chIn c: \ Program Files\Bentley\UicroStation\ readme_microstatic , li
vba_concept. chIn c: \ Program Files \ Bentley\UicroStation\ vba_concept. chIn

Ii

The FileSystemObject is extremely useful. You can use it to get file,


folder, and drive information, to read and write ASCII text files, and to
catalog files by using the Dictionary object.

Microsoft Speech Object Library


The Microsoft Speech Object Library is another example of powerful
functionality at our fingertips with only adding a reference.
Sub TestSpeechA()
Di m myVoice As New SpeechLib.SpVoice
myVoice.Speak "MicroSteyshen V. B. A. Is Great!"
End Su b

How difficult would it be to add voice/speaking capabilities to your


software without this reference? Granted, we can't always spell as we
normally do. Instead, we need to spell words phonetically (or is that
funeticly?) .
We used a Windows API call to playa .wav file. When you use the
Speech object, you can play volumes of instructions without needing to
install volumes of .wav files. You can even have your Speech object speak
dynamically, giving instructions and feedback as your application runs.

680

I Chapter 33: Using Third Party ActiveX Controls and DLLs I


Microsoft COO for Windows 2000 Library
Even if Windows XP is installed, the "CDO for Windows 2000 Library"
reference should be available. If it is not displayed in the References list,
browse in C:\Windows\System32\Cdosys.dll to add it.
Sub TestC DOA ()
Dim myMsg As New CDO.Message
Dim FieldBase As String
FieldBase = "http://schemas.microsoft.com/cdo /c onfiguration / "
myM sg.To = "you raddress@yourserver .com"
myMsg.From = "myaddress@myserver.com "
myMsg.Subject = "Testing COO Ema il"
myMsg .HT MLBody = "<b>VBA</b><br>For Mi croStation "
myMsg . Con f igu r at ion.F i elds.ltem( Fie l dBase & "sendusing " ) = 2
my Msg . Conf ig ur at i on. Fie l ds .lt em(F i eld Base & "smtpserver " )
"smtp.yo ur se r ve r. com "
myMsg.Con f iguration . Fields . Item(F i eldBase & "smtpserverport " ) = 25
myMsg . Conf i gu r ation .Fi elds . Update
myMsg . Se nd
En d Sub

Sending e-mail using VBA is easy. It takes a few lines of code because
there are a some settings you need to make. Once the settings are in
place, the e-mail is sent.

From:

myaddress@myserver .com

To:

youraddresS@yourserver.com

Cc:
Subject:

Testing COO Email

VBA
For MicroStation

Here is the e-mail that is sent using CDO. And how difficult is it to add
an attachment to the e-mail?
Sub TestCDOB ()
Dim myMsg As New CDO .M essage
Dim FieldBase As String

I Using Existing DLLs I

681

FieldBase = "http://schemas.microsoft.com/cdo / configuration/ "


myMsg.To = "youraddress@yo urserver . com"
myMsg .F rom = "myaddress@ yserver.com "
myMsg.Subject = "Testing COO Email"
myMsg . HTMLBody = "<b>VBA</b><br>For MicroStation"
myMsg . Configuration . Fields.ltem(FieldBase & "sendusing " ) = 2
myMsg . Configuration.Fields . ltem(FieldBase & "smtpserver")
"smtp.yourserver . com "
myMsg.Configuration.F i elds.ltem(FieldBase & "smtpserverport " )
25
myMsg.Configuration.Fields.Update
myMsg.AddAttachment "C:\test.htm"
myMsg.Send
End Sub
L.J!':.._.~~it ~~L

,i nsert ..!..~r::a~ ., Io~~~ .,:tions

tlelp

t 1'!'I ~ 143 X I....

, ~ ~ B.~pI}' I~ Reply ~,:,A~ 1~~ For~arHcl t;);l ~


From :

myaddress@myserver,com

To:
Cc:
Subject:

youraddress@yourserver.com

K)

(~)

Testing COO Email

Attachments: ~ test,htm (273 8)


r -- -

VBA
II For
MicroStation

There are thousands of reasons why you send an e-mail usingVBA.It.s


fortunate that sending an e-mail is so easy.
You need to modify the To, From, and SMTPServer properties to match
individual settings and servers.

DSO OLE Document Properties Reader 2.0


All of the references we discussed so far are installed with Windows XP
but that is not true of DSO. Follow the link:
http://support,microsoft.com/kb/22435 7I

to another link, "Download the DsoFileSetup:' This is what we want


because MicroStation design files are OLE documents. Another term
used to describe this type of file is "Structured Storage" documents.
What does this mean?

682

I Chapter 33: Using Third Party ActiveX Controls and DLLs I


In MicroStation, go to the File> Properties menu item. When the File
Properties dialog box opens, click on the Summary tab.

General

S-um-;';ary

I Statistics 1

Design Properties
Tille:

If=

========~

Subject:

;::L=========

Client:

f=1

Keywords:
Comments:

~. _ __

===== ====
~[=========:;
_ _ _----.-J

Milestones:
: Manager:

Cancel

These properties may look familiar as they are in Microsoft Word and
Excel files, among others. We can read and write these properties in .dgn
files using VBA when a design file is open. The DSOFile reference
allows us to open any structured storage file to read file properties or
write/ create them, even if the application that created the file is not
installed. Let's take a look.

Bentley'Systems, Inc.

Sub TestDSOA ()
Dim myDSO As New DSOFile . OleDocumentProperties
myDSO .Op en "C:\ test . dgn " , True
MsgBox myDSO . SummaryP rope rties.Author
myDSO . Close
End Sub

The author of the file C \test.dgn is "Bentley Systems, Inc:' If we attempt


to read or write properties of a file that is open in an application, we may
be prohibited from doing so.
Our next example writes properties to the test.dgn file.
Sub TestDSOB ()
Di m my DSO As New DSOFile . Ol eDoc umen t Propert i es
myDSO . Open "C: \ t est .d gn ", Fa l se
my DSO . Su mm aryProperties . Author = "Jerry Wi nters "
my DSO . SummaryP r operties.Category
"MicroStat i on VBA "
myDSO . SummaryPropert i es . Key words = "VBA "
myDSO . Save
myDSO . Close

I Using Existing DLLs I

683

End Sub

If we write properties to a file, we must save it before we close it.

Here are the results


in Window s'
Explore r Properties
dialog box:

I::~:, ~~~~;~
Author:

\. . . . . . . . . . . . .-.. . . ... . . . . .... . . . . . . . ...... . . . -.. . ....... . . . ..... . . . . . ....... . . . . . . . . .., I

Category:

I~'licro5tation

Keywords: I'VSA
__

liSA
_ __ _ _
--~~__-_ .

I
_-_---ll

Comments :

Advanced

There are many 'SummaryProperties: many more than are displayed in


this window. Not all apply to MicroStation .dgn files and some are not
supported by MicroStation. The next macro displays the values of each
'SummaryProperty'.
Sub TestDSOC ()
Dim myDSO As Ne w DSOF i le . Ol eDocumentPropert i es
myDSO .O pen "C: \t est . dgn " , True
Deb ug . Pr i nt myDSO . SummaryProperties . Appl i cationName
Debug . Print myDSO . SummaryProperties . Author
De bug . Prin t myD SO. SummaryPropert i es .B yte Count
Debug . Print myDSO . SummaryProperties . Category
Debug . Print myDSO . SummaryPropert i es . CharacterCount
Debug .P rint myDSO . SummaryProperties . CharacterCountWithSpaces
Debug . Print myDSO . Summa ryP roperties . Comments
Debug . Print myDSO . SummaryProperties . Company
Debug . Pri nt myDSO . SummaryProperties.DateCreated
Debug . Print myDSO . SummaryProperties.DateLastPrinted
Debug . Pr int myDSO . SummaryProperties . DateLastSaved

684

I Chapter 33: Using Third Party ActiveX Controls and DLLs I


Debug . Print
Debug.Pr i nt
Debug.Print
Debug.Print
Debug.Print
Debug.Print
Debug.Print
Debug .Pri nt
Debug.Print
Debug . Print
Debug.Print
Debug.Print
Debug . Print
Debug.Print
Debug.Print
Debug. Pr i nt
Debug . Pr i nt
Debug . Pr int
Debug . Pr int
Debug . Print
En d Sub

myDSO .S ummaryProperties . Hidde nSlideCount


myDSO.SummaryProperties.Keywords
myDSO.SummaryProperties.LastSavedBy
myDSO.SummaryProperties.LineCount
myDSO.SummaryProperties.Manager
myDSO .S ummaryProperties.MultimediaClipCount
myDSO . SummaryProperties.NoteCount
myD SO.Su mmaryProperties.PageCount
myDSO .Su mmaryProperties .Parag raphCount
myDSO.SummaryProperties.PresentationFormat
myDSO.Su mmaryProperties.Rev isio nNu mber
myDSO.SummaryProperties.SharedDocument
myDSO.SummaryProperties.SlideCount
myDSO . SummaryProperties.Subject
myDSO . SummaryProperties . Template
myDS O. Summa ryProperties.Thu mbn ail
myDSO.SummaryPropert i es.Tit l e
myDSO . SummaryPropert i es . TotalEd i tT i me
myDSO . SummaryPropert i es . Version
myDSO.SummaryProperties.WordCount

Some of the Summary Properties shown here are read-only. For


example, we cannot modify the 'DateCreated' property.
Not all files are 'Structured Storage' files. One example is a .txt file
created in Notepad. Even though it is not a structured storage file, when
it resides on an NTFS hard drive, some summary properties are
available. Be careful with non-structured storage files. It may look as
though you are entering file properties, but if you e-mail the file or place
it on a non-NTFS drive, the file properties do not follow the file because
the properties are not stored in the file. Instead, they are stored with
(actually alongside) the file.
Sub TestDSOO()
Dim myDSO As New DSOFile .OleDocu mentProperties
my DSO . 0 pen "C: \ t est. txt " . True
MsgBox myDSO . SummaryProperties . Author
myDSO . Close
End Sub

I Using Existing DLLs I

685

TestDSOD looks very much like TestDS OA, only we are opening a differe nt

file. TesUxt is a standard ASCII file. In Windows Explorer, right-click on


the file and select Properties to display a few tabs.
II General l~mmar~L__________________- ,

Title:

~'--s-am-pl-e,-tx-t f-ile- - - - - - - - - - - - - - - - - , i I

~------~

Subject:

~ation VBA

Author:

r J~;~yWi~t~~~ -_---m ----------------'---------1

I'

Category: [ dsofile
~._.~

_ . _ _M .... _ .. __ __ ._._._ .... _ ... _. _ _ _ __ _ _ .. _ _ _ _ M _ _ .... _.H H ..... H

~' I
-

" I

- - - - - - - - - - - -_ _ _ ~-_I

Advanced> >

Low [

Cancel

- -

---

If you compare this dialog box with the one a couple of pages ago, you
discover that this dialog does not have a Custom tab. This indicates that
the file is not a structured storage file and that the properties shown are
due to NTFS functionality. So, you can visually discern the difference
between NTFS properties and structured storage properties. And the
macro TestDSOD runs even though the file is not "Structured Storage:'
But how do you know whether a file is OLE (Structured Storage) or not?
Sub TestDSOE ()
Dim myDSO As New DSOFi le .O l eDocumentProperties
'F irst we try a txt file
myDSO.Open "C: \test . txt ", True
If myDSO.IsOleFile = False Then
MsgBox "The file is not Structured Storage ."
Else
MsgBox "Structured Storage File Found. "
End If
myDSO.Close

686

I Chapter 33: Using Third Party ActiveX Controls and DLLs I


' Now f or th e DGN File
my DSO. 0 pen "C: \ t est . d gn " , Tr ue
If myDSO.IsOleFile = False Then
MsgBox "The file is not StrJctured Storage."
Else
MsgBox "Structured Storage File Found."
End If
myDSO.Close
End Sub

The "IsOleFile" property says whether the file is an OLE Document


(Structured Storage) or a non -OLE Document. This is important to
know because non-OLE documents are limited in their use of
properties.
In addition to SummaryProperties (standard properties), you can create,
read, and write custom properties.
Sub Te stDSOF ()
Di m myDSO As New DSOFil e .O l eDocum entPr ope rtie s
myDSO . Open "C:\test.dg n" , Fals e
If my DSO . IsO 1e Fi 1e = True The n
myDSO.C ust omProper t i es.Add "For Book ", _
"Lea rni ng Mi c roSt at i on VB A"
myDSO . Save
End I f
myDSO .Cl ose
End Sub

In this example, we add a custom property to the specified file IF the file
is an OLE File (Structured Storage). After adding the custom property, it

I Using Existing DLLs I

687

shows up in the Windows Explorer Properties dialog box under the


Custom tab.

! General

Custom

~~U-

_ _ _ _ _ _ _ _ _ _ _ _ _ _- ,

v ,i

Name:
Type:

!Text

Value:

r----------------------------J

vi

!. ......- .. ~- ..----.-----.---..---.---.-------j

Properties:

Name

Value

Type

[F.:~-;B~~k--: Learning ~licro5tation VBA

Text

),-,

1_ _ OK

,II

Cancel

The ability to add Custom File properties is powerful. You may want to
store information regarding the number of cells in a design file in a
custom property. If you do, VBA programming (from within
MicroStation or any other VBA environment) can read and write the
property, even if MicroStation is not installed.
A link was provided to the DSOFile.exe download page earlier. The file is
also located on the CD that accompanies this book.
For additional information regarding reading and writing file
properties, Microsoft's website has documentation and code examples
on it. Searching for "Dsofile" on the Internet gets a large number of
results as well.

688

I Chapter 33: Using Third Party ActiveX Controls and DLLs I


REVIEW
One of the powers of VBA is that you are not limited to the calls directly
exposed by VBA. You can use programming components developed by
others to speed development and augment functionality. We discussed a
few references that, when added, can add significant power to the
software you develop.
Opening the References dialog box in VBA, adding a reference, and
opening the Object Browser are great ways to familiarize yourself with
the functionality exposed by any references on your computer.

34

Working With Excel


Microsoft Excel is used for a great variety of things. A large number of
us use it, even though we may use it differently. Many of us use it for
calculations. Others use it for generating charts and graphs. Others use
it to balance their checkbook.
In the examples in this chapter we will be writing our code in
MicroStation's VBA environment. In a later chapter we will write code in
Excel's VBA environment.
In this chapter
[8 Connecting to Excel
[8 Workbooks, Worksheets, Ranges, and Cells
[8 Tag Extraction into Excel

CONNECTING TO EXCEL
There are three ways to 'connect' to Excel. We will begin by using
"GetO bj ect".

GetObject
Sub TestExce l A()

689

690

I Chapter 34: Working With Excel I


Dim myExcel As Object
Set myExcel

GetOb j ect ( , " Excel.Application")

End Sub

GetObj eet 'gets' an existing instance of Microsoft Excel.


~~}.'O('it~

If Excel is not
running, we see
an error when
we attempt to
'get' Exce l.

"''Pm'' ;\"e!:;$!l " .... '" .'!'-;:-W~;"." '" ' '" - -"' .'

"'P_~ -.,.-

~~~!c,!5.!'!t, !1s.~~l ~as_if ,,, ;: . ',.. ~..

("t

.1

Y.

:,.', ' "

,~

.-

..

""'"\'.""''-;'"

",'

K~ ,,~

Run-time error '429':


ActiveX component can't create object

5,:.ontin',e

When we see this error, we know we just attempted to "Get" Excel and
Excel was not running. If Excel is running, the macro Test Ex eel Aruns
without any problems. But what does TestExcel Ado?
We declare a variable, myExcel as an Object. Then we Set the variable to
the return value of GetObj eet. After the variable myExcel is set, it is the
Microsoft Excel Application. Everything we do to the variable myExcel
impacts Excel.
When we declare a variable as an 'Object', we are performing "Late
Binding". This means that before the Object is Set, the Object doesn't
know who or what it is. When we declare a variable as a specific type of
object, we are performing "Early Binding".
Adding a Reference to the Microsoft Excel Object Library does wonders
for our programming efforts.

Available References:
'-. ".'
.. --........... ---.-.. -..- ...-----.. -..-..- ....-..-- ............ -...---....- .........-..- ....'.c.",

:....1Visual Basic For Applications

__:'

:;;: Bentley ~licroStation DGN 8.9 Object Library


e.;;: OLE Automation

:;g,

;~L..

_..

~
~
Browse".

Once a Reference is made, we can declare variables as specific types of


Objects.
Sub

TestExcelB ()
Dim my Ex c e 1 As Ex c e 1 . APP1 i cat ion
Set my Ex eel

End Sub

Get 0 b j e c t ( , " Ex c e 1 . APP1 i cat i on " )

I Connecting to Excel I

691

The code looks very similar to TestExeelA but the difference is


enormous. In Tes tE xee 1B we are performing "Early Binding". The
variable myExcel knows it is an Excel.Application because it is declared
as an Excel.Application. In addition to knowing what it is, the variable
myExcel knows what it can do. For example, when we type 'myexcel'
and then press the period key, a list pops up telling us what the object
'myexcel' can do. From this variable we can get the ActiveSheet, the
ActiveWindow, the ActiveWorkbook, and a host of other things.

Sub TestExcelC ()
Dim myExcel As Excel.Application
Set myExcel = GetObject(, "Excel. Application" )
msgbox myexcel.
End Sub
" ~i~~tii.~i~Mi~i~~9~r~
....... . .... .. . . .... . .. . ............................

!~

@i' ActiveC ell

@' ActiveChart
@i' ActivePrinter
@' ActiveSheet
@i' ActiveWindow
@' ActiveWorkbook

Early Binding not only improves the speed of software development


because of Intellisense, but it improves performance as well.

CreateObject
If Microsoft Excel is not running or if we want to create a new instance
of the Excel.Application, we can use Crea teObj eet.
Sub

TestExcel D()
Di m my Ex eel As Ex eel . APP1 i c at i on
Set my Excel

Crea t eOb j ec t ( "E xcel . App li cation " )

my Ex eel . Vi s i b 1e = T ru e
myE xcel . Wor kbook s . Add
End Sub

TestExee 10 creates a new instance of Excel. It does not matter whether or


not Excel had been running, a new instance of Excel is created.

Cre ateObj ee t can be useful if multiple instances of Excel are running.


When we use Ge t Ob j ee t, we do not know beforehand which instance of
Excel we will 'get'. When we use Cre at eObjec t, we know exactly which
"Excel.Application" we are using because it is a new instance created.

692

I Chapter 34: Worki ng Wi th Exce l I


New
When a Reference to the "Excel Library" has been added to our YBA
project, YBA understands what an Excel.Application Object is. If a
Reference has been added, we can use the New keyword to 'create' an
Object.
Sub

TestExcelD2 ()
Dim myExcel As New Excel.Application
my Ex c e 1 . Vis i b 1 e

True

my Exce l . Wor kbook s. Add


End Sub

Using New instead of CreateO bj eet can be useful because we do not need
to supply the Class "Excel. Application" as we do with Cr ea t eOb j eet. And
why is this useful? Because it is possible to have multiple versions of an
application that has been exposed to YBA and using the New keyword
will 'create' the version that is referenced.
GetObjee t, Cr eateObjee t, and New are the methods we use to 'connect' to

Microsoft Excel. When we add a Reference to the Microsoft Excel


Object Library, we can declare variables as specific types of objects
(Early Binding). YBA helps us as we work with these variables by
performing syntax checking and also helps us know what properties,
methods, and events we can utilize.

WORKBOOKS, WORKSHEETS, RANGES, AND CELLS


When we use GetObj eet, Cr ea te Obj ee t , or New, we are getting the
Excel.Application Object. Directly under this object is the Workbooks

I Workbooks, Worksheets, Ranges, and Cel ls I

693

Collection (among other things). Workbooks are composed of


Worksheets and Worksheets are composed of Ranges and Cells.

50
500
1000
1500
2000
2500
3000

60
600
1200
1800
2400
3000
3600

v,

>U

This Workbook has three Worksheets. They are named "SimpleGrid':


"ComplexGrid" and "Sheet3". We are currently looking at "SimpleGrid".
The ActiveSheet of this Excel.Application is "SimpleGrid". The
ActiveCell of this Excel.Application has the Address "AI". Its Rowand
Column is (1, 1) respectively.
Let's take a look at the Worksheets collection and Worksheet Objects.
Sub TestExce l E( )
Dim myExcel As Excel.Ap plicatio n
Dim mySheet A As Work s heet
Di m myS heetB As Worksheet
Dim mySheetC As Worksheet
Dim mySheetD As Worksheet
Set my Excel = GetObject(. "Excel.Applicat i on " )
Se t mySheetA = myExce 1 . Act i veSheet
Set mySheetB = myExcel . ActiveWorkbook.Worksheets( "SimpleGrid " )
Set mySheetC = myExcel.ActiveWorkbook .Worksheets( "ComplexGrid " )
Set mySheetD = myExce l .Ac t ive Workbook. Worksheets( "Sheet3 " )
Debug . Print mySheetA.Name
Debug . Print mySheetB.Name
Debug.Pr i nt mySheetC . Name
Debug.Print mySheetD.Name
Set mySheetB
myExcel . ActiveWorkbook.Worksheets(l)
Set mySheetC
myExcel . ActiveWorkbook . Worksheets(2)
Set mySheetD
myExcel.ActiveWorkbook.Worksheets(3)

694

I Chapter 34: Working With Excel I


Debug.Print mySheet B.Name
Debu g . Print my SheetC .N ame
Debug.Print mySheetD.Name
End Sub

We can address Worksheets by getting the ActiveSheet or through the


Worksheets Collection by Name or by Index.
Now that we can get the Worksheets, let's see what we can do about
getting individual Cells.

20

Sub TestExcelF ()
Di m my Exc e 1 As Ex c e 1 . APP1 i ca t i on
Dim myS he et A As Wor kshee t
Set myExc el = Get Ob j ect( . "E xce l .A ppli cat i on " )
Set mySheetA = myExcel .Acti ve Workbook .W orks he ets( "Si mpl eGr id" )
MsgBo x myShee tA . Range (" B1") . Text
End Sub

One of the ways we can address individual Cells is by getting to them


through the Range Object. When we use the Range object, we get the
cell by its address. ''AI'', "C6': "F9", etc. Using an Address such as ''A4'' is
helpful because we can directly relate that to what we see in Excel.
The other way we work with Cells in Excel is through the use of the
Cells Collection.

E~~~-=--=--=~

_____-=-___ _____==_=--==~=--===---=--=--=:=~~~~~=~~=

Sub TestEx c elG ()


Dim myExcel As Excel . Applic a tion
Dim mySheetA As hlorksheet
Set myExcel = GetObj ect (, "Excel. Appli c a t ion")
Set mySheetA = myExcel.Activehlo rkbook. hlorksheets ("SimpleGdd")
MsgBox mySheetA.Cells(
End Sub
Default(lRowindl!)(j. [Columnl ndeXj)

When we work with the Cells Collection we specify the RowIndex and
then the ColumnIndex. Row 1 in Excel has a RowIndex of 1 and
Column "J\, in Excel has a ColumnIndex of 1. We need to make sure we
specify the Row before the Column when working with the Cells
collection.
Sub TestExcelG ( )
Dim my Ex c e 1 As Ex eel . APP1 i cat ion

I Workbooks, Worksheets, Ranges, and Cells I

695

Dim mySheetA As Wor ksheet


Set my Ex c e 1 = Get 0bj e c t (. "E x c e 1 . APP1 i cat io n" )
Set mySheetA = myExcel .Act iveW orkbook.Worksheets( "SimpleGrid " )
~sgBox mySheetA . Cells(4. 6) . Text
End Sub

A MessageBox displays the text found in the cell on the 4th row and 6th
column.
Getting a Cell based on its Rowand Column does not seem as easy as
getting it based on its Address. So, why would we go through the trouble
of Rows and Columns?
Sub TestExcelH()
Di m myExcel As Excel. Appl i cat ion
Dim mySheetA As Worksheet
Dim CurRow As Long
Dim CurCol As Long
Set myExcel = GetObject(. "E xcel.Appl ication " )
Set mySheetA = myExcel .A ctiveWorkbook .Worksheets ( "SimpleGr i d" )
For CurRow = 1 To 7
For CurCol = Asc( "A" ) To Asc("F")
Debug . Pr i nt mySheetA.Range(Chr(CurCol) & CurRow)
Next CurCol
Next CurRow
End Sub
Sub TestExcelJ ()
Dim my Exc e 1 As Ex c e 1 . APP1i cat io n
Dim mySheetA As Worksheet
Dim CurRow As Long
Dim CurCol As Long
Set myExcel = GetObject(. "E xcel.Application " )
Set mySheetA = myExcel .ActiveWorkbook.Worksheets("SimpleGrid")
For CurRow = 1 To 7
For CurCol = 1 To 6
Debug.Print mySheetA.Ce lls(Cur Row. CurCol)
Next CurCol
Next CurRow
End Sub

Both TestExcel Hand TestExcel J print the values of a grid of cells to the
Immediate Window. TestExcel H can do this easily because we are

696

\ Chapter 34: Working With Excel \

dealing with columns A to F. The same code would work with columns
A to Z. But what happens when we get to column ''AX'? When we work
with Range objects, we specify the column with its letter designation of
anything from ''1\' to "IV". Writing code that flows from "z" to "AX' is
not difficult but cumbersome. When we use the Cells collection, we
simply specify column 27 after we finish with column 26 without
worrying about whether we are going from Column Z to AA, AB, and so
forth.
So, which is best? The Cells Collection or the Range Collection?
As we have discussed, each has its strengths and weaknesses. Providing a
Rowand Column numerically is easy to do but difficult to 'translate' the
Cell Column to a lettered Column in Excel. What is the lettered Column
Name associated with column 2ll?
Ranges are great especially when dealing with a relatively small set of
data (Columns A through Z particularly) but become more difficult to
work with when we get to Columns AA through IV. Ranges, however,
can also consist of multiple cells (from Al through D4 for example). So,
that is a definite strength.
If we work with Cells (providing numbers for the columns as well as the
rows), we can help ourselves a little by changing a setting in Microsoft
Excel.
Tools > Options in Excel displays the Options dialog box. Clicking on

the General tab allows us to turn on "Rl Cl reference style". When


"RlCl reference style" is turned on, Columns in Excel appear as
Numbers instead of letters. The formulas in Cells are modified to use
the RlCl style so they will look odd but as far as programming for
Microsoft Excel, seeing the Column Number is a lot easier than

I Workbooks, Worksheets, Ranges, and Ce ll s I

697

counting out 'AP.:.', 'A B", 'AC" to fi nd out what the Colum n Index is of
Column "DC".

Settings

~~i~:~~:tt:;n:::IE!t!on$

o !?rompt for workbook properties


o Provide [eedback with sound

~ Function tooltips

0 00'" on roll with IntelliMouse

~ flecently used file list:

14. -..-- ~-1 entries

I'teb Options... I [ser~ice Options... I

OK

I[

Cancel

After selecting "Rl C 1",


Columns appear as
numbers instead of
letters.

Cell and Range Addresses


Each Cell in Excel has a large number of properties. We will concern
ourselves with only a few of them in this book. The first one we will look
at is the Address.
Sub TestExcelU )
Dim myExcel As Excel . Application
Dim mySheetA As Worksheet
Dim CurRow As Long
Dim CurCol As Long
Dim myCe 11 As Excel. Range
Set myExcel = GetObj ect ( . "Excel . Application")
Set mySheetA = myExcel . ActiveWorkbook.Worksheets("SimpleGrid " )

698

I Chapter 34: Workin g With Exce l I


Fo r CurRow

1 To 7

For CurCo l

1 To 6

Set myCell = mySheetA . Cells(CurRovl, CurCo')


Debug.Print myCell . AddressCTrue, True, xlAl) & vbTab &
myCell . Addr ess(True , True , xlR1Cl)
Ne xt CurCo l
Next CurRow
End Sub

We can get the Address of a Cell


(declared as a Range object) in
$A01
$B01
R1C2
the "xlAl" format or the
R1C3
$C01
$D01
R1C4
"xIRICl" format. As we have just
R1C5
$E01
R1C6
been discussing, the "xlA 1"
HOI
$A$ 2
R2Cl
format gives us the column as a
$B $2
R2 C2
letter and the row as a number.
The "xlRICl" format gives us
both the column and row as numbers as shown in the Immediate
Window after running TestExcel L.
As we see here, Addresses are returned in one of two ways. "$D$2" and
"R2C2" are referring to the same cell in Excel.
Addresses are important to understand because we may need to know
which cells the user has selected. For example, how do we know if the
user has selected the Range "B2" to "B7" or "D2" to "D5"?
Sub

TestExcel M()
Dim myExcel As Excel.App l icat i on
Dim my Se 1e c t ion As Ex c e 1 . Ran ge
Set myExce l

Get Object(, " Excel . App l icat i on " )

Set my Se 1e c t i on

my Ex c e 1 . Se 1e c t ion

Debug.Print mySelect i on.Address(Tr ue, True, xlR1Cl)


End Sub

In this example, the selection was "B2" through "B7" (2,2 to 7,2).

I Workbooks, Worksheets, Ranges, and Cel ls I

699

The colon (:) tells us "B2" through "B7" have been selected. There are
other ways to select cells in Excel.

>

When we usually select cells in Excel we select a range of adjacent cells.


For example, we may select "D4" through "G I 2". It is possible, however,
to hold down the <CTRL> key and select cells that are not adjacent to
one another. As we can see below, four different cells have been selected
now, B2, C4, ES, and F6. The commas indicate that individual cell
addresses are being given.

Here we see what we get when the range of "B2" through "BS" are
selected AND "D2" AND "DS".
In the previous examples, we knew exactly from which cells we wanted
to get values. Our next example is going to display the values of the
selected cells. We will begin by assuming that a range of cells is selected
instead of individual cells.
Sub TestExcel N()
Di m my Exce l As Exce l . App l i cation
Dim mySelection As Excel. Range
Dim myS heet As Exce l . Worksheet
Dim StartRow As Long: Dim St a rtCol As Long
Dim End Row As Lo ng : Di m EndCo l As Long
Dim X As Long: Dim Y As Long
Dim XSplitA As Varia nt: Dim XSp li tB As Va ri ant: Dim XSplitC As Variant

Set myExcel

GetObject(, " Excel.Appl i cat i on " )

Set my Se 1 e c t ion
Set mySheet

my Ex c e 1 . Se 1 e c t ion

my Exce l. ActiveSheet

XSp 1 it ASp 1 it ( my Se 1 e c t i on . Ad d res s (T rue , True , xl R1 C1 ), " : " )


XSplitB

Split(XSplitA(O), "C " )

XSplitC

Split(xSplitA(l) , "C " )

StartRow
Sta rtCol

Replace(XSplitB(O) , " R" , "" )


=

XSpl i tB(l)

700

I Chapter 34: Working With Excel I


EndRow = Rep1ace(XSp1itC(O),
End Co 1

" R",

"" )

XSp 1itC(1 )

For Y = StartRow
For X

~o

EndRow

StartCo1 To EndCo1

Debug.Print mySheet . Ce11s(Y, X) . Text


Nex t X
Next Y
End Sub

In this example, we discover the beginning Rowand Column as well as


the ending Rowand Column. Then we look at each cell in the range and
print the Text property of the cell to the Immediate Window.
Sub TestExcel P()
Dim my Ex eel As Ex ee l . APP1 i cat ion
Dim my Se 1e c t io n As Ex c e 1 . Ran g e
Set my Ex c e 1

Get 0 b j e c t (,

Set mySe 1ection

"E x c e 1 . APP1 i cat ion" )

myExce 1.Se 1ec tion

Dim my Cell As Ran g e


For Each myCe1 1 In mySe1ection
Debug. Pr i n t my Cell . Add res s & vb Tab & my Cell . Text
Next
End Sub

Te stE xc el Nand TestExcel P are very much alike. They accomplish the
same thing. In Tes tE xeel P, we are 'extracting' the address as well. So, if
each of these is doing the same thing, which one is the best one? Fewer
lines of code is good. Knowing how to break out the Address is good
too. Each has its benefits. One is not necessarily better than the other,
they are just different.
Here is another macro to consider. Instead of extracting the Address as
''A 1" style, we will extract it in (Row, Col) style.
Sub TestExcel O()
Di m my Ex c e 1 As Ex c e 1 . APP1 i cat i on
Dim my Se 1e c t ion As Ex c e 1 . Ran g e
Di m myCe 11 As Range
Dim strAdd As Str in g

I Workbooks, Worksheets, Ranges, and Cells I

701

Set myExcel = GetObjec t(, "Excel.Ap plica t io n" )


Set mySel ectio n = myExcel . Sele ct i on
For Each myCell In mySelection
strAdd
myCell.Address(True , TrL.e, xlR1C1)
strAdd = Rep l ace(strAdc , "R" , .... )
strAdd = Repl ace(strAdd , "C", . , .. )
Debug . Pri nt .. ( . & s tr Add & .. ) .. & vbT ab & myCell . Text
Next
End Sub

(2,2)
(2,3)
(2,4)
(3 ,2 )
(3,3 )

200
300
400
400
600

<,)
We need to remember that when we are dealing with Excel, we are
looking at (Row, Col). The Row comes first. When we are accustomed to
dealing with (X, Y) this can take a little getting used to because Excel
thinks in terms of (Y, X).
So far we have done a lot of 'pulling' from Excel. Let's try doing a little
'pushing' now. We are going to change a cell's value, a cell's formula, and
then perform a Copy and Paste operation.
Sub TestExcelR ()
Dim myExce l As Excel . App l ic at ion
Di m myShe et As Worksheet
Se t my Exce 1 = GetO bj ect ( , "Ex ce l. Application " )
Set myS heet = my Exce 1 . Act i veSheet
'Give B1 a new Value
mySheet . Range( "B1" ).Value = 70
' Give B2 a new Formula
mySheet . Range( "B 2" ) .F ormula = "=B$1*$A2*52 "
' Copy B2 to the Windows Clipboard
mySheet.Range( "B2 " ) . Copy
' Select B2 through F7
mySheet . Range( "B2 ", "F7 " ) .Select
' Paste copied formula to selected cells
mySheet.Paste
' Select B2
mySheet.Range( "B2 " ).Select
' Reset Cut/Copy Mode
myExce l .CutCopyMode = Fal se
End Sub

702

\ Chapter 34: Working With Excel \


The code is very straightforward and simple. Comments proceed each
line where we make modifications to the Excel file.
TestE xee l Ris making changes to the "ActiveSheet".

Working with Worksheets


As we know, multiple Worksheets can be found in a single Workbook.
Let's take a look at working with multiple Worksheets.
Sub TestExcel S ()
Dim myExcel As Exce l .Appl ication
Dim myWB As Excel . Workbook
Dim myWSA As Excel. Worksheet
Dim myWSB As Exce l . Wor ks heet
Dim myWSC As Excel. Worksheet
Dim RadValue As Double
Di m InchValue As Double
Dim Fe etValue As Double
Dim CurRow As Long
Set my Ex c e 1 = Get 0 bj e c t (, "E xc e 1 . APP1 i cat ion" )
Set myWB = myExcel.ActiveWorkbook
Set myWSA
myWB . Worksheets( "Sheetl " )
Set myWSB = myWB.WorksheetsC"Sheet2 " )
Set myWSC = myWB.Worksheets( "Sheet3 " )
' Merge Header Rows
myWSA. Range( "Al " , "Dl " ). Merg e Tr ue
myWS B. Range( "Al", "Dl"). Merge Tr ue
myWSC.Ra ngeC "Al " , "Dl " ).Merge True
' Add Titles
myWSA.Range( "Al") . Value = "Degrees "
myWSA . Range( "Al " ).HorizontalAlignment
my WSB . Range( "Al " ) . Va l ue = " Inches "
myWSB.Range( "Al " ).H orizonta l Alignment
myWSC.RangeC "Al " ).Value = "Feet "
myWSC . Range ( "Al") . Hor i zonta l Alignment
'Add Val ues
myWS A.Range C"A2 " ).Valu e
myWSA. Ran ge ( "B2 " ). Va l ue

"Degrees "
"Ra dians "

xlCen t e r
xl Ce nte r
xlCenter

I Workbooks, Worksheets, Ranges, and Cells I

703

CurRow = 3
For RadValue = 0 To 36 0 Step 5
myWSA.Range ( "A" & CurRow) = RadVal ue
myWSA . Range( "B" & CurRow).Value - RadValue * Atn(l) * 4 / 180
CurRow = CurRow + 1
Next RadValue
CurRow = 3
myWSB . Range( "A2 " ) = "Inch "
myWSB . Range( "B2 " ) = "Cent i meter "
For In chVal ue = 1 To 36
myWS B.Rang e( "A" & Cur Row) = Inch Val ue
myW SB . Ra nge("B" & CurR owl .Formul a - "-A" & CurRow & " * 2 . 54 "
Cur Row = CurRow + 1
Nex t I nchValue
CurRow = 3
myWSC . Range( "A2 " ) = "F eet "
myWSC .R ange( "B2") = "M iles "
For Fee tValue = 0 To 20000 Step 1000
myWSC.Range("A" & CurRow) = FeetValue
myWSC.Range( "B" & CurRowl.Formu l a - "-A " & CurRow & " / 5280 "
CurRow = CurRow + 1
Next FeetValue
End Sub

Tes tExce 1S is using three different Worksheets in the same Workbook.


We do not need to switch to the actual Worksheet before we can use it,
we simply address it by its name (Sheetl, Sheet2, Sheet3).
Excel is used to house a great deal of information. One thing it can be
used for is holding X, Y, and Z coordinates for points. Let's take a look at
our first example.

704

I Chapter 34: Working With Excel I


We use the Excel Function RAN 0 to help us generate a random number.
This helps us generate points that fall within a specific area.
A

BC

Here are the


formulas:

The X and Y values are set to be randomly generated between -50 and 50
whereas the Z value will be calculated to be between -25 and 25.
The RA N0 function re-calculates the values whenever the Worksheet is
recalculated. The values don't 'stick', so one person's values will differ
from another person's values.

I Workbooks, Worksheets, Ranges, and Ce lls I

705

Now that we have seen the formulas, let's take a look at the values
generated.
A

BC

1
X
Y
Z
2
-2208716598"
33.06775051
10.80048308
3
-6.328729698
-33.55681358
-15.12641372
4
34.56159917
-10.503424
6.740530447
-4 .622206053
2807424377
11.58952008
5
6
-29.87002472
-42.94468857
-1803490077
7
41.69913054
-25.40802339
17.79778593
0.302771807
-12.71775582
8
18.51114764
-43.30360124
-4.219122549
17.52240842
9
10
27.57192593
36.50908479
-5.833780353
11
27.82085713
-1.916510652
16.59443405
... . .................
12
47.68307758
22. 11381557
-8.423401139
13
-49.79771456'
;)O-.S208630g:fo97469319
. -- ._._. __.__._._.'"
-_._-----_.._-_.--_. '
14
-28.44507751
11 .91563398
23.67553777
15.35135842'- -'-7~715i8G75 ---2246271813
15
16
1621397367
f4071189ii ':5470716498'
17 .... ~94??~3~_~~.
4561203874 '
16S.74~187 ;
18
-36.0 12428862427163771 ' -5.996325708
19
3443S15521: ' 14 51513287; :12j1695913' .
20 . . ... ':25.498429 i 9' . ..... 1:2 18440165 ' . ........ ... :46610566 ; '

21:1.344~9S2 l -3963885911'
-1434005614 "
22
-4035505243': 15:5432170i
-7.858884602'
23
-1846141973 , .3S73125351 1 " :19.68034631 '
24 .. . 37294 10466' :33s'47663-75 -1124281S18 '
25
-6.704128391"- :1S.2653331' :2967s7557,
26 -.-----.-1.553724653'-2912092628
4542'2'73424 '
.. "1
..- ...----.. - ..- .., . _._-_ .. _-- .- -....-. -t---------------- --';'

1
2

3
2
1
2
3
2
1
2

3
2

1
2
3
2
l'
. 2 '"
3'

2
1
2
3
2
. . - 1'

For this example, the number of rows is not fixed. We can have
anywhere from 1 data row to 65,536 rows. The code we will work with
begins by looking on Row 2 and continues executing until it finds a row
where Column A is empty.
Sub

TestExcelT ( )
Dim myExcel As Excel.Applicat i on
Dim myWSA As Exce l .W or ksheet
Dim CurRow As Long
CurRow

Set my Ex c e 1

Get 0 b j e c t ( , " Ex c e 1 . APP1 i cat ion" )

Set myWSA = myExce l .ActiveWor kboo k .Worksheets("Sheetl")


While myWSA.Cells(CurRow, 1)

<> ""

Debug . Print myWSA .Ce lls(CurRow, 1) & "

" &

myWSA.Cells(CurRow, 2) & "

" &

myWSA.Cells(CurRow, 3 )
CurRow

CurRow + 1

Wend
myWSA . Calculate
End Sub

706

I Chapter 34: Workin g Wi th Excel I


If we have a specific number of rows we want to extract, we could use a
For I ... Next structure. But since the number of rows may vary, we use a
Whi 1e ... Wend structure.
The last line of code forces the Worksheet to recalculate, which
generates new random numbers for us.
Let's build upon Test Exeel T by drawing inside MicroStation.
Sub TestExcel U()
Di m my Exc e 1 As Exc e 1 . APP1 i cat ion
Dim myWSA As Exce l .Worksheet
Dim CurRow As Lo ng
Dim myPoin t As Point3 d
Dim myT ex t Node As TextNodeE l eme nt
Dim myRotMatr i x As Matrix3d
CurRow = 2
Se t myExce l = GetO bject(. "Exce l. Application")
Set myWSA = myExce l . Act i veWorkbook. Wor ksheets ( " Sheetl " )
Whi l e myWSA.Cells(CurRow. 1) <> ""
myPoin t .X
myWSA.Cells( CurRow. 1)
myPo i nt.Y = myWSA.Cel l s(CurRow. 2)
myPoint.Z = myWSA.Cells(CurRow. 3)
Set my TextNode = Crea t eTextNodeElement1(Nothing . myPoint. _
my RotMatrix)
myTextNode.Add Text Li ne CurRow - 2
ActiveMode l Reference . Ad dEl ement myTextNode
Cu rR ow = Cu r Row + 1
Wend
myWS A.C al cu la t e
End Sub

Our Worksheet has a column for "Level". In the above example we are
not making use of it. Let's build upon TestExce l U now and place the
TextNode on a specific Level.
If we attempt to place an Element on a Level that does not exist, we will
get an error. Let's create a new Function named CheckLeve 1 that creates a
Level if it does not exist. We will use this Function inside Tes tE xce 1V.
Fun ct i on Chec kL eve 1 ( Le ve 1 Na me As St r i ng) As Leve 1
On Err or Res ume Next
Set Chec kLeve l = Act i veDes i gn Fi 1 e . Level s ( Level Name)

ITag Extraction I

707

I f Er r .Nu mber <> 0 The n


Set CheckL eve l = Act i veDes i gn Fil e . Ad dN ewL eve l CLe ve lN ame)
End If
Err.Clear
t=:nd Funct ion
Sub TestExce l VC)
Dim myExcel As Excel.Application
Dim myWSA As Excel. Wor ksheet
Dim CurRow As Long
Dim myPoint As Poi nt3d
Dim myTextNode As Text odeElement
Dim myRotMatrix As Matrix3d
CurRow = 2
Se t my Exc e 1 = Get 0 bj e c t C. . Ex c e 1 . APP1i cat ion" )
Set myWSA = myExcel . ActiveWorkbook.WorksheetsC "Sheet1 " )
Whi le myWSA.CellsCCurRow. 1) <> ....
myPoint.X
myWSA . Cel l sCCurRow . 1)
myPoint . Y = myWSA.CellsCCurRow. 2)
myP oi nt . Z = myWSA. Cells CCurR ow. 3)
Set myTextNode = CreateText NodeElementlCNoth i ng . _
myPoint . myRotMatrix)
myTextNode.AddTextL i ne CurRow - 2
CheckLevel CStr(myWSA.Ce l ls(CurRow . 4))
myTextNode.Level = CheckLevelC myWSA . CellsCCurRow . 4))
ActiveModelReference.AddElement my TextNode
Cu rR ow = Cu rR ow + 1
Wend
myWSA.Calculate
End Sub

Notice how the Function Check Leve 1 returns a Level Object. We use this
Level Object after we create the TextNode so the TextNode appears on
the correct Level.

TAG EXTRACTION
When we discussed Tags in a previous chapter we stated that we would
see an example of extracting Tag information into Microsoft Excel. We
will begin by modifying the macro ExportFo l derTagsToHH1L. In this

708

I Chapter 34: Working With Excel I


m acro, we had created an HTML document that displays the Tag
information of files in a specific folder.
Sub TestExce l W()
Dim myDGN As DesignFile
Dim myFSO As New Scripting . FileSystemObject
Di m myFolde r As Sc ri pt i ng . Fo l der
Di m myF ile As Sc r ipt i ng. Fi l e
Dim myTagSet As TagSet
Dim myTagDef As TagDefinition
Dim TargetT agset As String
Dim myTag As TagElement
Dim myElemEnum As ElementEnumerator
Dim myF i lter As New El ementScanCr i teria
'New Declarations
Dim myExcel As Excel.App l ication
Dim myWS As Excel.Worksheet
Dim CurRow As Long
'New Code
Set myExcel = New Excel.Appl ication
my Ex c e 1 . Vis i b1e = True
myExcel . Workbooks. Add
Set myWS
myExcel . ActiveSheet
CurRow = 2
TargetTagset

"TitleB l ock "

Se t myFolder - myFSO . Get Fo lder( "C: \ Oocu ments and Sett in gs \ " & _
"All Users\A pp l i cati on Oa t a\ " &
"Bent l ey \ Wor kSpace \ Projects \ " &
"Examp l es\Bui 1di ng\Ogn " )

For Ea c h my Fi 1e I n my Fol de r . Fi 1e s
Se l ect Case myFile . Type
Case "Bent l ey Mi croStation Design Fi le "
'File Nam e and Merge Cell s
myWS.Cells( CurRow, 1) = myFile.Path
myWS.Ran ge( "A" & CurRow & ":F" & CurRow).MergeCe l l s - Tru e
myW S. Range ( "A" & CurRow, "F" & CurRow) .B ord e r Ar ound _

. xl Th ick
'Header
CurR ow = Cu rR ow + 1

ITag Extraction I

709

myWS . Range("B" & CurRow) = "Tag Set Name"


myWS . Range("B" & CurRow) . Font . Bold
True
myWS. Range( "C" & Cu rR ow ) = "Tag Name"
myWS.Range("C" & CurRow).Font.Bold
True
myWS.Range("O" & CurRow) = "Tag Value"
myWS.Range("O" & CurRow).Font.Bold
True
myWS.Range("E" & CurRow) = "IO High"
myWS. Rang e( " E" & CurRo w) .Fo nt.Bo l d True
myWS.Range( " F" & CurRow) = "IO Low"
myWS.Range("F" & CurRow).Font.Bold
True
CurRow = CurRow + 1
'Open the File
Set myOG N = Applicat i on . OpenOesign FileForProgram( _
myFil e .P ath, Tru e)
For Each myTagSet In myOGN . TagSets
Se l ect Case UCase(myTagSet.Name)
Case UCase(Target Tagse t)
myFilter.ExcludeA l lTypes
my Filte r .IncludeType msdEleme nt TypeTag
Set my El emEnum = _
myOGN.Models(l) . Scan( myFilte r )
Wh i le myElemEnum.MoveNext
Set myTag = myElemEnum.Current
'Write to Excel
myWS . Ce l ls(CurRow, 2) = TargetTagset
myW S. Cel ls (C ur Row , 3 ) = _
myTag.TagOefinitio nName
myW S. Cel ls (CurRow . 4) = myTag . Value
myWS.Ce l l s ( CurR ow , 5) = myTag . I D. Hi gh
my WS. Ce ll s(C ur Row, 6) = myTag. I O.L ow
CurRow = CurRow + 1
Wend
End Select
Next
myOGN.C l ose
End Se l ect
Next
End Sub

710

\ Chapter 34: Working With Excel \


The code here is very similar to that in Chapter 28. We open each file
"PorProgram", and extract Title Block Information from the Tags in the
files. We are doing a little bit of formatting as well. We merge a few cells
where the file name is, and draw a border around it. We also change the
font.Bold property of the 'headers' to True.

Getting the Tag Name and Value are helpful in a variety of areas. But
getting the ID values (both High and Low) are helpful as well. Now that
. the Tag information is inside Excel, we can make changes to the Tags in
Excel and then run a Macro to update the .dgn files .

If, for example, the Job Number is changed from "BSBOO" to "BSBOOA':
we could make the change in Excel and run the macro TestExcel X to
update the BSBOOAE30 1-Elevations.dgn file.

Sub TestE xcelX ()


Dim myDGN As DesignFile
Dim myTag As TagElement

ITag Extraction I

711

Dim myEx ce l As Excel. App l i cati on


Dim my WS As Ex c e 1 . W0 r ks he e t
Di m Cu rR ow As Long
Dim FileRow As Long
Set my Excel = GetObject( , "Excel.Application " )
Set my WS = my Exc e 1 . Act i ve She e t
Cur Row = my Excel . ActiveCe l l . Row
Fil e Row = CurRow
While myWS.Cells(F i leRow, l) . MergeCells
False
FileRow = FileRow - 1
Wend
Set myDGN = Appl i cation . Ope nDesi gnF il eF or Prog r am (
myWS.Ce l ls ( FileR ow, I ) , Fa l se)
Dim mylD As DLong
mylD .Hi gh = myWS.Cells(CurRow, 5)
mylD.Low = myW S.Ce lls(C urRow , 6)
Set myTag = myDGN.GetElementByID (myID)
myTag.Value = my WS.Ce ll s(CurRow, 4)
myTag.Rewrite
myDGN.Save
myDGN.Close
End Sub

Once we extract Tags from MicroStation, we want to allow the user to


change the values in Excel. The currently selected Cell in Excel is very
important in this macro because it tells us which Tag to modify.
The Row of the currently selected Cell in Excel is retrieved. We look
upward until we find a row where column A (Column 1) is merged.
When we find a merged Column we know that we have found the row
where the file name is stored. This is the file in which the selected Tag
(actually the selected Cell in Excel) is located. We open the file, get the
Tag Element by using the ID.High and ID.Low values, change the Tag's
value to reflect what is in Excel, Rewrite the Tag Element to the DGN
file, and then save and close the .DGN file. This is all done in a very
short period of time and the user does not see the DGN file open
because we are using Open Desi gn Fi 1eForProgram.
If we open the file, we can see that the Tag Element has been modified to
reflect the value in Excel.

712

I Chapter 34: Working With Excel I


These macros are very powerful because they allow us to extract data
from MicroStation files, and 'modify' them at any time without using
MicroStation, and then 'upload' the modifications back into the
MicroStation file.
We could build on this macro to update all Tags in the Excel file. But we
will allow the reader to do this.

South Elevation and


Facade Treatment Options
1: 100

BS I300A

AE20 1 1.0

REVI EW
Any area we have already discussed relating to Element Creation, Data
Extraction, etc., can be used in conjunction with Microsoft Excel. We
have used Excel to extract data from MicroStation, to create data inside
MicroStation, and to modify data inside MicroStation. Those who use
Microsoft Excel in conjunction with MicroStation will find that many
manual, time-consuming, tedious, error-prone tasks can be
accomplished with the marriage of these two great technologies.

35

Working With Databases


(ActiveX Data Objects)
Access, Oracle, DB2, SQL Server. When there's data to be stored, there is
no shortage of Database platforms to store it. Rather than spending time
on each Database, we will devote our time to learning about ActiveX
Data Objects because with ActiveX Data Objects, we can 'talk' to each of
these database products.

In this chapter:
[B

Primer on ActiveX Data Objects

[B

UDL File Basics

[B

Connections and Recordsets

[B

Structured Query Language Essentials

[B

Extending ActiveX Data Objects

[B

Examining Database Schema

[B

Excel Files as Databases

713

714

I Chapter 35: Working With Databases (ActiveX Data Objects) I


PRIMER ON ACTIVEX DATA OBJECTS
ActiveX Data Objects is a technology created by Microsoft that allows
for simple yet powerful communication with Databases. Which
Databases? Virtually any Database that is 'open: And wh at does 'open'
mean? Microsoft Access is 'open'. SQL Server is 'open'. Oracle is 'open'.
Mainstream databases are 'open'. We can work with them through
standard VBA / ADO (ActiveX Data Objects) calls. Some databases,
however, are proprietary an d as such, are not designed to allow software
developers to work with them. So, in general, most databases available
to us can be accessed through the use of ActiveX Data Objects.
ActiveX Data Objects allow us to work with databases through the use
of several key Objects. Before using "ActiveX Data Objects", we must add
a Reference to it. Let's do so right now.

U ~licrosoft
U Microsoft
C Microsoft
U Microsoft
U Microsoft
U ~licrosoft
U ~Iicrosoft
U Microsoft
U ~Iicrosoft
U ~Iicrosoft
U Microsoft
U Microsoft
U ~licr osoft

ActiveX Data Objects 2.0 Library


ActiveX Data Objects 2.1 Library
ActiveX Data Objects 2.5 Libr ary
ActiveX Data Objects 2.6 Library
ActiveX Data Objects 2.7 Library
ActiveX Data Objects 2.8 Library
ActiveX Data Objects Recordset 2.7 Libr ar\ 22
ActiveX Plugin
Priority
Add-In Designer
ADO Ext. 2.7 for DOL and Security
Agent Control 2.0
Agent Server 2.0
Agent Server Extensions 2.0
[....) Mirrn",nft Anti-<i.nvwArA ';;;hAflF'tp.illtp.Hl1nk - 1.n

Cancel
Browse .. . . J

[
.

.!.l '----'----'
Help

,~~:.~!:L.~~+;;1~~#~.W.~~:0~./'4~:~d!

r OLE Automation -"-' -_., .


. Location:
language:

C:\WINDOWS\system32\STDOLE2. TLB
Standard

When we look in the References dialog box in VBA, we may see a


number of different "ActiveX Data Objects" libraries. In general, it is
best to select the highest Library version available. In this example,
Version 2.8 is the highest one.
After adding a Reference to the "Microsoft ActiveX Data Objects 2.8
Library", new Objects and Constants are exposed and available for our
use. We can see them by looking at the Object Browser and filtering on
the "ADODB" Library.

I Primer on ActiveX Data Objects I

715

Selecting the "Connection" Object displays the Methods, Properties, and


Events.

IAOOOB

:======~

~ }a

'W

---------"vi '" ~___.___________.

L - I_ _ _ _

Classes

rffJ
rffJ
rffJ
,tfJ
rffJ
,tfJ

Me mbers of 'Connection'

ADCPROP _ASYNC1 ~! ,.,~ Exec ute

ADC PROP _AUTOR E ;:! fJ ExecuteC omplete


ADCPROP _UPDATE ~' fJ InfoMessage
ADCPROP _UPDATE
~ Iso lation Leve l
AffectEnum
~ Mode
BookmarkEnum
.,,~ Open
~ Comm a nd
.,~ Ope nSchema
rffJ CommandTypeEnur
~ Properties
rffJ CompareEnum
~ Provider

~[.~~~ii9.~

... . .

! ~i

.,~~

"

, "

Ro li ba ckTran s

Sub Ollen([ConnectionSlring As Siringj, [UserlD As Siring], [Password As Siring j. [Options As Long = -1J)
Member of ADODB .C()l1neetion

When we select the "Open" method, we can see the Open declaration.
A few Objects worth examining for a moment are "Connection",
"Recordset", and "Field". We will be working with these Objects and
their Methods and Properties. Look for Methods such as Open, Update,
Execute, and AddNew.
In a nutshell, ActiveX Data Objects allow us to open a Database, query
its records, modify its records, add new records, etc. Before doing much
with ActiveX Data Objects, however, we need to have a database to work
with.
The USGS (United States Geographic Survey) maintains a system
named the "Geographic Names Information System" (GNIS) . The
Geographic Names from several states have been imported into a
Microsoft Access Database named PlacePoin ts.mdb. This file is located
on the CD accompanying this book. This will be the first database we
work with in this chapter.

716

I Chapter 35: Working With Databases (ActiveX Data Objects) I


Here is a screen capture of some of the data in this database. Notice the
Fields (Columns) and each Record (Rows) .

14022
14023'
14024 1454454 UT
14025 ... !43!6.1 1,.UT

14026 '

14028'

1~35839UT

1429440UT
l!q~l _1!5.1.Q! .t,!T
1~9}.!. __14.!2420 !!I.

49045
.i(04S '

Tooele
L~~e. PoinICemelery . cemelery ;Tooele
... Lak. e P.9inllnler"hange ,cr9ss.ing [Tooele
. !,ake ~oi.~\.~u. ~ .~lion ... ;ppl...... iTooel.e
Lake Powell Overlook . Iocale
;Garfield

4068083 112.26222
,4Q4017N , 1121545.W , 40671}9. 11~26?5..
49. 045
' 404136N 1121547W 406~.~~3 112.26306
49.045 T404323N . 1 121 :i~iw 40.72306 .1122.2.5.28
" 4iioil ' : 375302Nll02508W 3788389 110.418B9
;La~e. Ridg;; "~f r~dge
! Ri~h ~:
~$l@.:.l~"57bji-J ill1 0?2i'l\iji .95()83 1 i 109083
L~~id~_._ ..... ~ el'L . __ .:.~alL~~'!.... _._~Q~.~ .. _._l..4.Q.41.5EN ~'!'!.?[J~8W ~Q 9~1!!.-1110716!
~,,-k!.~g~ . __ .. _ ji.!!g~...
'I\I~e!:
__ . ~Q5.2 ...J1171 ~N _! 1!29~'I\I. 4 128~33 1 114~67

. ':Ji~~~L:j~~-1~~~L~ It!~:~:~~; .

t::~~:

49; Q~!i.

..f~~chhe

..

~~;~; -T1m~;~ ~11~m~~ 1 :1~~~~

:11 ;~~~~

UDL FILE BASICS


ActiveX Data Objects gives us a framework to interact with databases.
We can use Connection Strings to specify the location of the database
we want to work with and which driver should be used to connect to the
database. Connection Strings are often times hard-coded. This means
that the Connection String appears in our code and we must change the
code if any portion of the Connection String changes. Connection
Strings can also be read from configuration files or from the Windows
Registry. Another way to specify the driver and database location is
through the use of a UDL file.
The steps to creating a UDL file are very simple. They are described in
Windows' Help File and can be found by typing 'udl' in the search box.

Us ing Data Link


1 results found for udl

-.. ..-.. -----. -. ..- - - - -. . .-. . .I


1
Suggested Topics (1 results)

Overviews, Articles and Tutorials

Using Data Link

Yo u can use th e Data Link program to c


file that can be referen ce d for fut
a
syste m, you can open the Da ta Link pre

.m

To open Data Link

1.

Open III Wi ndow s Exp lorer.

I UDL File Basics I

717

Open Windows Explorer.

Go to Tools> Folder Options.


F~lder Option~
.....-.....- ......
General ' View

__

--

rn~

......- . . ... -_.. _. ....._.....


LFile Types Offline Files

-(~ -

Ii

Folder views
You can apply the view (such as Details or Tiles) that
you are using for this folder to all folders.

.:A

4iJlJ
". I Apply to All Folders I [,..------,
Reset All Folders
Advanced sellings:

~------~-----------------------------.

!0

Files and Folders


Automatically search for network folders and printers
Display file size information in folder tips
Display simple folder view in Explorer's Folders list
Display the contents of system folders
Display the full path in the address bar
0 isplay the full path in the title bar
Do not cache thumbnails
Hidden files and folders
Do not show hidden files and folders
Show hidden files and folders
Hide extensions for known file types

A .. '

o
o
o
o
o
o
o

o
o

Restore Defaults

In the View tab, uncheck the "Hide extensions for known file types".

Right-click in the folder where the UDL file is to be created and


select New> Text Document.
,i. ;;;;------------;:

I.

Arrange Icons By

.'

i.__~=f!=:_~._... _ ___ .......


!

ij ~;~~~~~~; ~~;i;e ~~~ess Application


~ I~icrosoft PowerPoint Presentation

j (!'] Paint Shop Pro 9 Image


Customize This Folder, .. ......................1 ~ ~ticrosoft Office Publisher Document

~ Play List Document

I _~~~R:~ame

._._. ___...~I+Z

Ell Wave Sou


:

~ ~licrosoft Excel Worksheet

. c:;J1

,.__p_ro_p_e_
rt_
ie_
s ______________

~:Ir-~------------------------

Win Zip File

5 A file such as New Text Document.txt is


displayed and is ready to be renamed.

718

I Chapter 35: Working With Databases (ActiveX Data Objects) I


6 Type the name of the UD L file fo llowed by the extension ". udl" and
press <Enter>.

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

I r::::::::x~s.~::: ~

__!i_o _-,
o KB

. 001PlacePoints ,udl 1

J ,i

Text Document

When asked if you are sure the file extension should be changed,
click the Yes button.
The new UDL file is created. Now we need to open the UDL file and
tell it which driver we want to use (based on the database we are
connecting to) and where the database is located.

Open the UDL file by double-clicking it in Windows Explorer.

,Provider

Provider ! Connection Advanced ,Ail

1. Select or enter a .database name:

OLE DB Provider s
Connectivity Service Provider
MediaCatalogDB OLE DB Provider
MediaCatalogMergedDB OLE DB Provider
MediaCatalogWebDB OLE DB Provider
Microsoft ISAM 1,1 OLE DB Provider
Microsoft Jet 3,51 OLE DB Provider
DB
DB
DB
DB
DB
DB
DB
DB
DB
DB

Specify the following to connect to Access data:

Select the data you want to connect to:

Microsoft OLE
Microsoft OLE
Microsoft OLE
Microsoft OLE
Microsoft OLE
Microsoft OLE
Microsoft OLE
Microsoft OLE
Microsoft OLE
Microsoft OLE

Connection Advanced All

IC: \M icrostation VBA \ PlacePoints, mdb


2, Enter i~formation to log on to the database:
User name: IAdmin
.e~ssword :

r;;

Provider For Data Mining Services


Provider for Inde.ing Service
Provider for Internet Publishing
Provider for ODBC Drivers
Provider for OLAP Services
Provider for OLAP Services 8. 0
Provider for Oracle
Provider for Outlook Search
Provider for SQL Server
Provider for Visual FoxPro

1'"1- --------- --

Jl.lank password

Allow l aving password

~I
lest Connection

OK

Cancel

Help

OK

Cancel

Help

In the Provider tab, select "Microsoft Jet X.X OLE DB Provider".

lOIn the Connection tab, browse to the PlacePoints.mdb file.

11 Click the OK button.

I Connections, Recordsets, and More I

719

Congratulations. Once the basics of UDL file creation are understood,


UDL files can be created very quickly.
When we are in the Provider tab, the list of available 'Providers' may
vary from computer to computer. A review of the Providers list shows
which databases we can work with. In the screen capture previously
shown, we can see a driver for Oracle, SQL Server, Visual Fox Pro, Jet,
and ODBC among others. Any provider with 'Jet' in its n ame is referring
to the driver used to open a Microsoft Access database.

CONNECTIONS, RECORDSETS, AND MORE


Now that we have a Database (PlacePoints.mdb) and a UDL file
(PlacePoints. udl) we can begin working with Connections and
Recordsets in our code.
The Connection Object is 'connected' to the database. It is the first step
to working with the data in the database. Let's look at our first example:
Sub TestConnectionA ()
Dim myDB As New ADODB.Connection
myDB .Ope n "f il e name=C:\MicroStation VBA\PlacePoints.udl"
myDB.Close
End Sub

In our first example, we open a Connection by using the UDL file we


just created and then we immediately close the database connection.
Notice how the variable myDB is declared. It is declared as a "New
ADODB.Connection" .
In addition to declaring a Connection as a "New ADODB.Connection",
we can declare it as an ''ADODB.Connection'' and then set the variable
to a "New ADODB.Connection" as we see in TestCo nne ct i onB.
Sub TestConnectionB ()
Di m myDB As ADODB . Co nnect i on
Set myDB = New ADODB . Connect i on
my DB.O pen "f i l e name=C : \ Mi cr oSt ation VBA\ Place Poin t s .u dl "
myDB . Cl ose
End Sub

Instead of Declaring the Connection as New, we set it to a New


ADODB.Connection on the following line. Aside from the fact that the

720

I Chapter 35: Working With Databases (Active X Data Objects) I


second example uses an additional line of code, the difference between
the two methods is negligible.
Opening and immediately Closing a Connection does not help us at all.
Let's do something with the Connection before we close it now.
Sub TestConnectionC()
Dim myDB As ADODB.Connection
Set myDB = New ADODB.Connection
myDB.Open "file name=C:\MicroStation VBA\PlacePoints.udl"
myDB . Execute "Create Table PlaceNotes " & _
" (Unique I D Counter, PlaceID Long) "
myDB.C l ose
End Sub

The Execute Method allows us to execute SQL statements on the


Connection Object. In the above example, we create a new Table named
"PlaceNotes" with two fields. Here are a few additional examples of
using the Execute Method.
TestConnecti onD adds two more fields to the PlaceNotes table.
Sub TestConnectionO ()
Dim myDB As ADODB.Connection
Set myDB = New ADODB.Connection
myDB.Open "f il e name=C:\MicroStation VBA\PlacePoints.udl"
myDB.Execute "Alter Table PlaceNotes " &
"Add NoteBy Char(SO), NoteDate Date "
myDB.Close
End Sub

TestConnecti onE adds a record to the PlaceNotes table.


Sub TestConnectionE ()
Di m myDB As ADODB.Connection
Set myDB = New ADODB . Connection
myDB.Open "file name=C:\MicroStation VBA\PlacePoints.udl "
myDB.Execute "Insert Into PlaceNotes " &
"(PlaceID. Note8y, NoteOate) VALUES " &
" (1, ' JKW' , '" & Now & ''')"
myDB . Close
End Sub

TestConnecti onF creates an additional field in PlaceNotes.

I Connections, Recordsets, and More I

721

Sub TestConnectionF( )
Dim myDB As ADODB . Connection
Set myDB = New ADODB .C onnect i on
myJB.Oper "file name=C : \~icroStation VBA\P l acePoints.Jcl"
myDB.E xecute "Alter Table Place Notes Add TheNote Memo "
myDB . Cl ose
En d Sub

TestConnect i onG updates all records in PlaceNotes where the NoteBy


field is 'JKW' by setting the "TheNote" field value to 'Reviewed'.
Sub TestConnectionG ()
Dim myDB As ADO DB.C onnect io n
Set myD B = New ADODB.Connection
myDB.Open "f il e name=C : \MicroStation VBA\PlacePoints.udl"
myDB .E xecute "U pdate PlaceNotes Set TheNote = 'Reviewed' " &
" Where NoteBy = ' JKW '"
myDB.Close
End Sub

This chapter is not meant to be a comprehensive tutorial on SQL


(Structured Query Language). It is good, however, to introduce some of
the functionality available to us by using standard SQL statements in an
Execute statement with the Connection Object.
Opening a Connection is time-consuming. Of course, it doesn't take
weeks, days, hours, or minutes. But it can take a second or two. If we
open and close a Connection to a database every time we want to work
with it, we will experience a performance hit. In some instances, we may
want to open a Connection once and keep it open until the application is
terminated. We can look at the Connection's State property to determine
whether it is open or closed.
Sub TestConnectionH ()
Dim myDB As ADODB .Conn ection
Set myDB = New ADODB.Connectio n
myDB.Open "file name=C : \MicroStation VBA \ Pl acePoints . udl "
Se le ct Case myDB.State
Case adStateClosed
MsgBox "Connect i on is Closed ."
Case adStateConnecting
MsgBox "Connection is Connecting. "
Case adStateExecuting

722

I Chapter 35: Working With Databases (ActiveX Data Objects) I


Msg Box "Con necti on is Exe cutin g. "
Cas e ad St at eFe t ch i ng
MsgBox "Connection is Fetching."
Case adStateOpen
MsgBox "C onnection is Open ."
End Select
myDB.Close
End Sub

Test Con nect ion H opens a Connection and then looks at each of the
possible States by using a Sel ect Case statement.
In Tes tCo nnec ti onJ, we are looking at a real-world example of how we
would use the State property. We first look at the variable myDB (which
should have been declared in the General Declarations area of the Code
Module or User Form) to see if it is closed. If it is closed, we open it by
using a UDL file.
Sub TestConnectionJ ()
If myDB.State = adStateClosed Th en
myDB.Open "fil e name=C:\MicroStation VBA\PlacePoints.udl"
End If
MsgBox "U se the Connectio n Object Here"
End Sub

It should be noted here that in Tes tConnect i onJ we are not declaring the

variable myDB or setting it to a New ADODB.Connection. We are


simply checking to see if it is closed. If so, we open it. In order for this
procedure to work correctly, the variable myDB must be declared in
such a way that it is available to this procedure (Public in a Code
Module or in the General Declarations area of the module in which this
procedure is located) and instantiated (set to as a New
ADODB.Connection). For example,
' Genera l Dec l arat i ons
Di m myDB as New ADODB . Connection

Now that we can 'connect' to a database by using a UDL file, let's take a
look at the Connection's ConnectionString property.
Sub TestConnectionK ()
Dim myDB As ADODB . Connection
Set myDB = New ADODB . Connection

I Connections, Recordsets, and More I

723

myDB .Open "file name=C : \MicroStation VBA\PlacePoints . udl "


MsgBox Replace(myDB.ConnectionString. " ; " . vbCr)
myDB.Close
Ene SJb

The Connection String is rather lengthy and is delimited with semicolon


characters. In the above example, we replace the semicolon (;) with a
Carriage Return so we can more clearly see the ConnectionString.

Provider=Microsoft. Jet. OLE DB .4. a


User ID=Admin
Data Source=C:\Microstation VBA\PlacePoints .mdb
Mode=Share Deny None
Extended Properties=''''
Jet OLEDB:5ystem database= ....
Jet OLEDB :Registry Path= ....
Jet OLEDB:Database Password= ....
Jet OLEDB :Engine Type=5
Jet OLEDB: Database Locking Mode=1
Jet OLEDB:Global Partial Bulk Ops=2
Jet OLEDB:Global Bulk Transactions=1
Jet OLEDB:New Database Password=....
Jet OLEDB:Create System Database=False
Jet OLEDB:Encrypt Database=False
Jet OLEDB:Don't Copy Locale on Compact=False
Jet OLEDB:Compact Without Replica Repair=False
Jet OLEDB:5FP=False

When we use a UDL file, the Connection String reflects the settings of
the UDL file. Although we have been depending on the UDL file, it is
possible to open a database and work with it without the use of a UDL
file by providing the ConnectionString when we Open the Connection.
Sub TestCon nect i onL ()
Dim myDB As ADODB.Connection
Dim ConnectionStringVals(O To 2) As String
Set myDB = New ADODB.Connection
ConnectionStringVals(O)
"P rovider=Microsoft . Jet . OLEDB.4 . 0"
ConnectionStringVals(l)
"U ser ID=Admin"
ConnectionStringVals(2)
"Data Source= " &
"C: \Microstation VBA\PlacePoints . mdb"
myDB.Open JO i n(ConnectionStringVals. ";" )
MsgBox myDB.State
myDB.Close
End Sub

In TestConnectionL we are opening the same database as we were by


using the UDL file PlacePoints.udl but we do so by opening the
Connection with a ConnectionString instead of using the UDL file.

724

I Chapter 35: Working With Databases (ActiveX Data Objects) I


Recordsets
The Connection Object is used to 'connect' with the database. The
Recordset is used to 'connect' with the Records in the database.
Sub TestRecordsetA ()
Dim myDB As ADO DB.Co nnec ti on
Dim myRS As New Recordset
Set myDB = Ne w ADO DB . Connect i on
myDB.Open "file name=C:\MicroStation VBA\ PlaceP oints.udl"
myRS.Open "Select * from Points Where County = ' Ventura '", _
myDB, adOpenDynamic, adLockOptimistic
Whi l e myRS.EOF = False
Debug.Print myRS("Des cr iption")
myRS. Move Next
We nd
my RS.Close
myDB . Cl ose
End Sub
TJindmill Canyon
TJomens Imp~o v ement Club of Hueneme
TJood Canyon
TJood C~eek P a~k
TJood Ranch 1027 Dam
TJood Ranch Golf Club
TJoo d Ranch Rese~voi~
TJoods ide Line ar P a ~k
TJoods ide Pa~k
TJo~ld Unive~sity

Xabaagua (histo~ical )
Xocotoc (histor i cal)
Verba Buena Beach
Verba Buena School
Ypuc (historical)

~]i

v;:

L?:;!-

In our first example, we use the Recordset Object to return all fields in
all records where the field 'County' has a value of 'Ventura'. Even though
we are getting all fields (by using the asterisk (*) in the SQL Select
statement), we only display the Description of each record in the
Immediate Window.
We will cover SQL statements later in this chapter. For now, we are going
to keep our attention on the Recordset Object.
In the procedure TestRecordsetA we can see that we use a Whi 1e ... Wend
statement and we look at the EOF (End of File) property. As long as the

I Connections, Recordsets, and More I

725

EOF property is False, we continue to print the Description of the


current record and then move to the next record. Move Ne xt is the method
that moves the Recordset to the next record.
The Open Method is used to specify which data we want returned, which
Connection Object to get it from, what type of cursor we want to use,
and what type of record locking we want as we retrieve the data.
Which data do we want? We specify which data we want by using a SQL
statement. The Connection Object in the Open method of the Recordset
Object points to an active Connection. When we get to the Cursor Type
and Locking Method, we should understand what each does and when
we may want to use them. The descriptions shown here are taken
directly from Microsoft's website.
Cursor Type Constants:
IB adOpenDynamic = 2: Uses a dynamic cursor. Additions,
changes, and deletions by other users are visible, and all types of
movement through the Recordset are allowed, except for
bookmarks, if the provider doesn't support them.
IB adOpenForwardOnly = 0: Default. Uses a forward-only cursor.
Identical to a static cursor, except that you can only scroll
forward through records. This improves performance when you
need to make only one pass through a Recordset.
IB adOpenKeyset = 1: Uses a keyset cursor. Like a dynamic cursor,
except that you can't see records that other users add, although
records that other users delete are inaccessible from your
Recordset. Data changes by other users are still visible.
IB adOpenStatic = 3: Uses a static cursor, which is a static copy of a
set of records that you can use to find data or generate reports.
Additions, changes, or deletions by other users are not visible.
Lock Type Constants:
IB adLockBatchOptimistic = 4: Indicates optimistic batch updates.
Required for batch update mode.
IB adLockOptimistic = 3: Indicates optimistic locking, record by
record. The provider uses optimistic locking, locking records
only when you call the Update method.

726

I Chapter 35: Working With Databases (ActiveX Data Objects) I


[E

adLockPessimistic = 2: Indicates pessimistic locking, record by


record. The provider does what is necessary to ensure successful
editing of the records, usually by locking records at the data
source immediately after editing.

[E

adLockReadOnly = 1: Indicates read-only records. You cannot


alter the data.

The Cursor Type has bearing on a couple of Recordset Properties.


Sub TestRecordsetB ( )
Dim myDB As ADODB.Connection
Dim myRS As New Recordset
Set myDB = New ADODB.Connection
myDB . Open "f ile name=C:\MicroStation VBA\PlacePoints.ud l "
myRS . Open "Se l ect * from Poin ts Where Co unt y = ' Vent ura '" , _
my DB, adO penFo rward Only, adLockReadOnly
MsgBox myRS.RecordCount
myRS . Close
myDB.Close
End Sub

The RecordCount can be a very useful property. When used with


'adOpenDynamic' and 'adOpenForwardOnly', it always returns a value
of - I, however. When we use 'adOpenKeyset' and 'adOpenStatic', the
Recordset Property gives us the number of records retrieved in the
Recordset. For example, when we run TestRecordsetB, we see a
RecordCount of -1 no matter how many records we have retrieved due
to the Cursor Type specified in the Open statement of the Recordset.
Sub TestRecordsetC ()
Di m myDB As ADODB.Connection
Dim myRS As New Recordset
Set myDB = New ADODB.Connection
myDB.Open "file name=C: \ MicroStation VBA \ Pla ceP oi nts.ud l "
myRS.Open "Select * from Points Where County = ' Ventura '", _
myDB, adOpenStatic, adLockReadO nly
MsgBox myRS.RecordCount
myRS . Close
myDB . Close
End Sub

TestRecordsetC displays a RecordCount of2253.

I Connections, Recordsets, and More I

727

Sub TestRecordsetD ()
Dim myDB As ADO DB.C onnec t io n
Dim myRS As New Recordset
Set myDB = New ADODB.Connection
myDB.Open "file name =C: \MicroStation VBA\PlacePoints.udl "
myRS . Open uSe ect * from Points Where County = ' Ventura '", _
myDB, adOpen Dy nami c , adLo ckOpt i mi st i c
Wh il e myR S. EO F = Fal se
Debug.Print myRS( "Descr i ption " )
my RS. MoveNext
Wend
myRS.MoveFir st
Wh i l e myRS . EOF = False
Debug . Pr int myR S( "CellName" )
myRS.MoveNext
Wend
myRS.C l ose
myDB . Close
End Sub

Te st Recordset Duses the MoveF i rst method of the Recordset Object. This

allows us to begin at the top of the Recordset and look through the
records again, possibly looking for different information.

The Find Method


Now we are going to move on to the Fi nd method of the Recordset
Object. It moves the cursor of the current Recordset to the first record
below the current record that matches the criteria. It allows us to search
inside of the Recordset that has already been populated using a Select
Statement. Since it is possible that the Recordset's Cursor is somewhere
in the middle of the Recordset, it is a good idea to use the M0 veFi r st
method of the Recordset so we always begin 'finding' from the top of the
Recordset. To make this work well, we are also Ordering the Recordset
by the CellName Field. This places all records with the same CellName
together in the Recordset.
Sub TestRecordsetE ()
Dim myDB As ADODB.Connection
Dim myRS As New Recordse t
Set myDB = New ADODB.Connection

728

I Chapter 35: Working With Databases (ActiveX Data Objects) I


myDB . Open "file name=C : \MicroStation VBA\PlacePoints.udl "
myRS.Open "Select * from Points Where County = 'Ve ntura ' " &
"Orde r by

CellNa~e ".

myDB . adOpenDyran i c . adLockOptimist i c

myRS. Fi nd "CellName = 'Li on Canyon ' "


While myRS( "CellName " ) = "Lio n Canyon "
Debug . Print "Lion Canyon: " & myRS ( "Oes cription")
myRS .M ov eN ext
Wend
myRS . MoveF ir st
my RS. Fi nd "Ce l lName = ' Oxnard '"
While myRS( "C ellN ame " ) = "Oxnard "
Debug.Print "Oxnard: " & myRS( "D escription " )
myRS.MoveNext
Wend
myRS.MoveFirst
myRS.Find "Ce l lN ame = 'Fi llmore '"
Whil e myRS( "CellN ame " ) = "F i ll more "
Debug.P rin t "F i l lmore : " & myRSC "D escr i pt i on " )
myRS .M ove Next
Wend
myRS.Cl ose
myDB.Close
End Sub

In this example, we are only looking for records where the County =
'Ventura'. We use "Order by CellName" so the Recordset is 'sorted' by the
CellName field. Then we use the Fi nd method to find the first record
where the CellName is 'Lion Canyon'. After looking at each 'Lion
Canyon' Cell, we move on to looking for the first 'Oxnard' cell. And then
we do the same with 'Fillmore'.

The GetString Method


Our next example demonstrates the use of the Ge t St r in 9 method of the
Recordset Object.
Sub TestRecordsetF ()
Dim myDB As ADODB.Connection
Dim myRS As New Reco rdset
Set myDB = New ADODB . Connec t ion
myDB.Open "file name=C: \ Mic r oStation VBA \ PlacePoin t s.udl "
myRS . Open "Select Description , Ce l lName fr om Point s " & _

I Connections, Recordsets, and More I

729

"Wh e r e State = ' CA ' and Po i ntTy pe = ' sc hool ''' , _


myDB , adOpenDynam i c , adLockOp t i mi s ti c
Debug. Pri nt myRS.Get String(adC li pStr i ng, - I, "I " , vbCr)
myRS.Close
myDB . Close
End Sub

Ge t St rin g gets all of the fields of all of the records retrieved in a


Recordset and places them into one large String. We specify the
delimiters that should appear between Fields as well as the delimiter that
is to be used between Rows (records). In this example, we have chosen
to retrieve all records by using -1 as the value for the number of records
to retrieve. If we used a number such as 5, only the top five records
would be returned by Ge tS tr i ng. As for the delimiters, we are separating
each field with the Pipe symbol (j) and each row with a Carriage Return.
YlmOn Elementary School lTorrance
Yllli on Intermediate Scho ollInglewood
Yulupa Elementary Schoo l I Santa Rosa
Yvonne Harmon Development Center for the Handicapped I San Bernardino Horth
Zamora Element ary Scho o ll~oodland
Zamorano Elementary Schoo l illati onal City
Zela Davis Elementary School I Inglewood
Zi on Lutheran School I Oakland East
Zi on Lutheran Schooll San Francisco North
Zion Lutheran School I FOl.Ultain Springs
Zion School lAnaheim
Zoeter School lSeal Beach
Zupanic High School I San Bernardino South

Add New and Update


The AddNew and Update methods of the Recordset Object are used to
create new records and update the values given to fields. Here are two
short examples. We will see additional uses of AddNew and Update as
we continue in this chapter.
Sub TestRecordset H()
Di m myDB As ADODB . Connection
Dim myRS As Ne w Reco rd set
Set myDB = New ADODB . Connection
myDB.Open "file name =C: \MicroStation VBA\PlacePo i nts . ud l "
myRS.Open "Pl aceNotes ", myDB, adOpenDynam i c , adLockOptimistic
myRS.AddNew
myRS( "PlaceID " ) = 4

730

I Chapter 35: Working With Databases (ActiveX Data Objects) I


myRS( "NoteBy " ) = "JK W"
myRS( "NoteDate") = Now
myRS("TheNote") = "New
myRS.Update
myRS.Close
myDB . Close
End Sub

~ote"

Sub TestRecordsetJ()
Dim myDB As ADODB.Connection
Dim myRS As New Recordset
Set myDB = New ADODB.Connection
myDB . Open "file name=C:\MicroStation VBA\PlacePo ints .udl "
myRS.Open "Select * from PlaceNotes Where Pl aceI D = 1", _
myDB , ad Ope nDynam i c, ad LockO pt i mis tic
Whil e myRS. EOF = Fa lse
my RS( " P1 ace I D" ) = 14
myRS.Upda t e
myRS.M oveNe xt
Wend
my RS. Cl ose
myDB . Cl ose
End Sub

In Tes t Reco rd se tH, we add a new record to the table. In Test Recordse tJ,
we query the database and change the PlaceID value in each record
retrieved by the SQL statement. In both examples, we use the Update
method to apply the field values to the database.

SQL ESSENTIALS
Now that we have discussed attaching to databases by using the
Connection Object and the data inside the database by using the
Recordset Object, let's begin looking into the SQL statements that can be

I SQL Essentials I

731

used with the Connection and Recordset Objects. We will do this by


creating several User Forms.

State
County

'I

:.:oJ :: :

:::::::::::: ::::" "

" "j----:.:oJ"""'1
.. :::".": :::::::: : ::::: ::::'"

~~i~t';Y~~

:.:oJ: ::,... ::::::::::: :::: :... '

Description

Report

Add Note

Draw In MicroStation

The first Form we create is shown above. When the Form loads, we need
to query the database for all distinct State values. These values will be
added to the Sta te ComboBox.

Select Statement
The Select statement is the basis for many of SQL statements we will use.
It allows us to specify which fields we want to retrieve, which tables the
fields come from, how to order the records, how to group the fields, etc.
To get the distinct States in the Points table, we use:
Se l ect , Dist i nc t Sta t e fro m Po int s

The Recordset will be populated with a record for each distinct value
found in the State field. In our example here, we will place each State
field's value in the ComboBox named cmbState.
Pr i vate Sub User Form_ In it ia lize()
Dim my DB As New ADO DB. Con ne ction
Dim myRS As New ADODB . Recordset
myDB . Open "f i le name=C: \ Mi c roStation VBA\P l acePoints . udl "
myRS . Open "Se l ec t Dist i nct State from Po i nts ", myDB
Whi le myRS . EO F = Fal se
cmbState . Add l t em myRS( "State " )
myRS .M oveNext
I-Jend
myRS . Cl ose

732

I Chapter 35: Workin g With Databases (ActiveX Data Objects) I


myDB.C l ose
End Sub

When the Form is shown, each unique State is added to the combo box.
The data for this example is being taken from a database created from
information on the USGS website: http://geonames.usgs.gov/stategaz/
index.html. Although all 50 United States were available, only two were
used. One, California, is a fairly large dataset, and the other, Utah, is a
much smaller dataset. So, in our example here, only two states will be
displayed: CA and UT.
When the user selects a State from the cmbState ComboBox, we want
to populate the cmbCounty ComboBox with all of the Counties in the
selected State. But before adding anything to the cmbCounty
ComboBox, we use the Clear method on it as well as on the
cmbPointType and lstDescription controls. If we didn't Clear the
Combo Boxes, County names would continue to be added to
cmbCounty each time a State was selected so that the cmbCounty
ComboBox would no longer display only the Counties from the selected
State.

Where
When we use the 'Where' statement, we begin providing the criteria
specifying which records we want to retrieve. In this example, we want
only records Where the State field is equal to the selected State in the
cmbState ComboBox. Since the State field is a String (Text), we use the
Apostrophe C) to begin and end the value.

Order By
The Order By statement allows us to specify how we want to sort the
Recordset's records. Multiple fields can be specified. We use ''ASC'' for
Ascending and "DESC" for a Descending sort.
Pr ivat e Sub cmbState_Cl i ck()
Dim myDB As New ADOD B.Co nnect i on
Di m myRS As New ADODB. Rprnr dse t
cmbCoun t y . Clear
cmbPointType.Clear
lstDesc r iption . Clear

I SQL Essentials I

733

myDB. Open "fil e name=C: \ Micro Sta tio n VBA\ Pla ce Point s .u dl "
myRS . Open "Sel ect Dis t inct Coun t y fro m Poin ts Wh ere Sta te = " & _
"'" & cmbState . Tex t & "' Order by County ASC ", myDB
While myRS.EOF = False
cmbCounty.Addlte~ myRS( "County " )
myRS . MoveNext
Wend
myRS .Cl os e
my DB .C l os e
End Sub

OK, now when the user clicks a State, the Counties in the database show
up in the frmCounty ComboBox. When the user clicks on a County,
what should happen? Let's populate the cmbPointType Combo Box
with only those Point Types that appear in records with the selected
State and the selected County.
Private Sub cmbCounty_Clic k()
Dim myDB As New ADODB . Connect i on
Dim myRS As New ADODB.Recordset
cmbPo in t Type.Clear
l stDescr i pt i on . Clear
myDB.Open "file name=C : \MicroStation VBA\PlacePoints.udl "
myRS .O pen "Selec t Distinct PointType from Points Whe r e State = " &
"'" & cmbState . Tex t & "' and" &
"County = '" & cmbCounty .T ext &
"&
"Order by Poi nt Type ASC " , myDB
While my RS.EOF = Fal se
cmbPo i nt Type.Addltem myRS( "PointType " )
myRS. Move Next
Wend
myRS . Cl ose
myDB . Cl ose
End Sub

When the user clicks on the PointType ComboBox, we see all the
Descriptions that match all of the selected criteria in the ComboBoxes.
We place the UniqueID in the second column of the Listbox (but hide
the column so it is not visible to the end user).

734

I Chapter 35: Working With Databases (ActiveX Data Objects) I


Priva t e Sub cmbPointType_Click()
Di m myDB As New ADOD B.C onn ecti on
Dim myRS As New ADODB.Recordset
lstDescription.Clear
myDB . Open "fi l e name =C:\MicroStation VBA\P l acePoints . udl "
myRS . Open "Select Description . UniqueID f r om Points Where State

" &

"'" & cm bSt at e .Tex t & "' and" &


"Cou nty = '" & cmbCo unty.T ext & " ' " &
"and PointType

'"

& cmbPointType . Text & '" " &

"Order by Des c ri ption ASC ", myDB


While myRS. EOF = Fal se
lstDescr i pti on . Add It em myRS( "D es cr i pt i on" )
l stD esc rip t i on .L i st (lst De sc r i ption.Li s tC ount - 1 , 1)
my RS( " Un i que I D" )
myRS .M oveNex t
We nd
myRS. Cl ose
myDB .Cl ose
End Sub

All of the above code forms the framework for allowing the user to
select Places from the database.
At this point,
the Form
looks like this:

State
county
Point Type
Description

I CA 3
I Los Angeles
I tower
Hauser Microwave Station ....................................... ................................ .......................... .i

KABC:AM(LosAngeie;;j"

KABC-TV (Los Angeles)


KACE -FM (Inglewood)
KAGL -TV (San Bernardino)
KALI-AM (San Gabriel)
KAVL-AM (Lancast er)
KAm-AM (Pasadena)
KBEl-AM (Canyon Country)
KBIG-FM (Los Angeles)
KBOB-FM (West Covina)
Report

Add Note

..

..:::..l
Draw In MicroStation

Selecting a State of "CX: a County of "Los Angeles", and a Point Type of


"tower" displays all Points meeting this criteria.
Let's move onto the buttons now. We will begin with the Report button.
When this button is clicked and a 'Place' is selected, we create an ASCII
Text file with all of the selected place's field values from the Database.

I SQL Essentials I
First we will look at the
resulting file, then we will look
at the code.
We have already seen how we
can get specific field values by
addressing them by name. We
could do this to create the
report. However, the goal is to
export all field names and
values. To accomplish this, we
use the Fields Collection of the
Recordset Object.

735

File

Edit

~niQue IO

Format

View

Help

71090

USGS_IO 1654661
State
CA
oes cri pti on
Haus er r,li crowave Stat, on
pointType
tower
county LOS Ange l es
TypeA
6
TypeS
037
LatOM S 343249N
LonOM S 1181256W
LatOec 34.54694
LonOec -118.21556
Ref _LatOMS
Ref _LonO~IS
Ref_LatOec
0
Ref_Lonoec
0
Elevation
0
population
0
FederalStatus
CelHlame
Ritter Ridge

Private Sub cmdReport_Click()


If lstDescription .T ext = " " Then Exit Sub
Dim myDB As New ADODB.Connection
Dim myRS As New ADODB.Recordset
Di m myField As Field
Dim FFile As Long
myDB.Open "file name=C:\MicroStation VBA\PlacePoints.udl"
myRS.Open "Se l ect * from Points Where UniquelD = " & _
lstDescripti on.ListC lstDescription.Listlndex, 1) , myDB
If myRS .E OF = False Then
FFile = Fr eeF il e
Open "C:\P l aceReport.txt " For Output As #FF i le
For Each my Field In myRS.Fields
Print #FFile, myField.Name & vbTab & myField . Value
Next
Close fFFFile
End If
my RS. Close
myDB.C lo se
End Sub

When we add the Description of each 'place' matching the selected


criteria to the Listbox, we also add the UniqueID to the second (hidden)
column in the Listbox. We use this value in our query. If we find a
record that matches (and we should always find a matching record), we
open an ASCII Text file for Output and write each Field Name and Value
to the file, and then close the file.

736

I Chapter 35: Working With Databases (ActiveX Data Objects) I


Also worthy of note is the query "Select *" in our Select statement.
When we use the Asterisk character, we are stating that we want to
retrieve all fields in the table.
The Report button seems to work fairly well. Now it's time to turn our
attention to the Add Note button. When the Add Note button is clicked,
we need to display a new UserForm that allows the user to enter the
Note information. The Add Note button tells the Add Note Form which
record is selected by setting the Tag property of the Add Note Form and
then shows the Form.
Private Sub cmdAddNote Click()
If lstDescription . Text = "" Then Exit Sub
frmAddNote.Tag = lstDescription.List(lstDescription._
ListInd ex . 1)
frmAddNote.Show
End Sub
Here is the

Form
frmAddNote

~~;e:B;; Ir--_.......:.._""'--_____
Note: ..

.. .
. ,' ,

...

( .... ...............
OK

::: : ::: :':::::: : ::"::.

When the user clicks the OK button, the following code is executed:
Private Sub btnOK_Click()
If frmAddNote.Tag =
Then
MsgBox "AddNote not executed correctly."
Unload Me
End If
If txtNoteBy . Text = " " Then
MsgBox "Please enter Note By. "
Ex i t Sub
End If
If txtNote.Text = " " Then
MsgBox "Please enter Note. "
Exit Sub

I SQL Essentia ls I

737

En d If
Dim myDB As New ADODB.Connection
Dim myRS As New ADODB.Recordset
Dim myField As Field
Dim FFile As Long
myDB.Open "fi le name =C: \MicroStation VBA\PlacePoints . udl"
myRS.Open "Sele ct * from Place Notes Wh e re UniqueID = 0 ", _
myDB, adOpenDynamic, adLockOptimistic
myRS . AddNew
myRS( "NoteBy " ) = txtNo te By . Text
myRS( "T heNote " ) = txt Not e. Text
myRS( "Place I D" ) = fr mAddNote .T ag
myRS( "NoteDa t e" ) = Now
myRS . Update
Msg80x "Note added ."
Unl oa d Me
End Sub

When we want to add a new record to a table, we have a few options. We


can use an "Insert Into" SQL statement with the Connection.Execute
method. In this example, however, we open the PlaceNotes table and use
the AddNew method of the Recordset Object. This gives us a little more
flexibility than we have by using an "Insert Into" statement. When we
open the Table, we use a Select statement and look for a UniqueID of O.
Why would we do this? If we want to open the entire Table, we would
use something like this:
myRS.Open "PlaceNotes " , myDB , adOpenDynamic , adLockOpt i mist i c

but we would take a huge performance hit because we are opening every
record in the Table. By intentionally opening a Recordset without any
records in it, the Recordset is opened almost immediately because it
does not need to retrieve any data.
The last button we are going to discuss is the Draw In MicroStation
button. The Database we are using has Latitude and Longitude values in
it, which give us Y and X values of the 'places' in the database. We will
use these values to place a Circle and Text Element at the location of the
selected 'Places' from the database.
First we will look at the code behind the button and then we will look at
the results.

738

I Chapter 35: Working With Databases (Active X Data Objects) I


Private Sub btnDraw_Click()
If lstDescription.ListCount = 0 Then Exit Sub
Dim myDB As New ADODB.Connection
Dim myRS As New ADODB.Recordset
Dim myPoint As Point3d
Dim myCircle As ArcElement
Dim myText As TextElement
Dim RotMatrix As Matrix 3d
Dim I As Long
myDB.Open "file name=C;\MicroStat ion VBA\PlacePoints . ud l"
For I = 1 To lstDescription.ListCount
If lstDescription . Selected(1 - 1) = True Then
myRS.Open "Sel ect * from Points Wher e UniquelD
"&
lstDescript i on.List(1 - 1, 1) , myDB
If myRS . EOF
False Then
myPoint.X = myRS( "LonDec " )
myPo i nt . Y = myRS( "LatDec " )
Set myCirc l e = CreateArcElement2(Nothing, myPo i nt, _
0.0025, 0. 0025, RotMatrix, 0, 360)
Act i veMode l Reference.AddElement myCircle
Set myText = CreateTextE l ement1(Noth i ng, _
myRS( "Oescript i on " ), myPoint , RotMatrix)
myText. TextStyle.Height = 0.01
my Text . TextStyle . Widt h = 0.01
ActiveModelReference.AddE l ement myText
End If
myRS.Close
End If
Next I
myDB.Close
End Sub

We use the LonDec and LatDec fields for the X and Y elements of each
Circle Center Point and Text Origin. We display the Description field's
value as a TextElement in MicroStation.
In the previous buttons we used, our work was based on the Listlndex
property of the Listbox. Since we are drawing in MicroStation, we want
to allow the software to place multiple points with only one button click.
This is why we are looking at the Selected Property of each item in the

I SQL Essentials I

739

ListBox. If an item is Selected, we query the database using the


UniqueID hidden in Column 2 of the ListBox.
Let's slow down a little here. We are moving through a lot of code. After
the code shown above becomes clear (or a little less cloudy), we are
going to add one enhancement. We will place the Circle and Text on a
Level with the same name as the Point Type.
Private Sub btnDraw_C li ck()
If lstDescription.ListCount = 0 Then Exit Sub
Dim myDB As New ADODB.Connection
Dim myRS As New ADODB . Recordset
Dim myPoint As Point3d
Dim myCircle As ArcElement
Di m myText As TextEle ment
Dim RotMatrix As Matrix3d
Dim I As Long
myDB.Open "f ile name=C:\MicroStation VBA\PlacePoints.udl"
For I = 1 To l stDescr i pt i on .Li stCount
If lstDescriptio n .Se l ected(I - 1) = True Then
myRS.Open "Select * from Points Where UniqueID
" &
lstDescription.List(I - 1 . 1). myDB
If myRS.EOF
False Then
myPo i nt.X = myRS( "LonDec")
myPoint.Y = myRS( "LatDec " )
Set myCircle = CreateArcElement2(Nothing. myPoint. _
0.0 025.0.0 025. RotMat r ix . O. 360)
myCirc l e. Leve l = CheckLeve l (cm bP oi nt Type.Text)
ActiveModelReference.AddElement myCircle
Set myText = CreateTextElement1(Nothing. _
myRS( "Description " ). myPoint. RotMatrix)
myText.TextStyle.Height = 0 . 01
myText.TextSty l e.Width = 0 . 01
myText. Level = CheckLevel (cmbPoi ntType . Text)
ActiveModelReference.AddElement myText
End If
myRS.Close
End If
Next I
myDB . Close
End Sub

740

I Chapter 35: Working With Databases (ActiveX Data Objects) I


Functi on CheckLevel (L eve l Name As Str i ng) As Leve l
On Erro r Resume Next
Set CheckLevel = ActiveDesignFile.AddNewLevel(LevelName)
If Err.Number <> 0 Then
Set CheckLevel = ActiveDesignFile.Levels(LevelName)
End If
End Function

We only add two lines of code to the Click Event ofbtnDrow and we add
a Function named Chec kLeveL Now, all Places selected are added to the
ActiveModelReference on a specific Level. The Level matches the
selected "Point Type".
As our program stands right now, we have some very good functionality
in place. We can get a report based on the selected item in the listbox.
We can add a note to the selected item in the listbox and we can draw
items selected in the ListBox inside the ActiveModelReference in
MicroStation.

EXTENDING ACTIVEX DATA OBJECTS


As the name implies, ActiveX Data Objects is about more than just
Databases, it is all about Data. Data appears in a variety of forms.
Databases hold data that can change from time to time, but it is not
likely that a database will have entirely different data every 24 hours.
One of the things that makes the Internet so powerful is that it is so
dynamic. It is changing every second of the day. And although the
Internet could be considered one large database, it is probably better
typified as a whole lot of Data rather than a large Database.
When we look at the Provider tab in a UDL file, we will see a reference
to the "Microsoft OLE DB Simple Provider". What can this do for us?
Can we use it to tap into the Data exposed on the Internet? Well, it can
be used for some Data on the Internet.
Let's take a look at the "Microsoft OLE DB Simple Provider". This
Provider is used for creating in-memory databases and can also be used
for working with XML files. We have dealt with XML files in a previous
chapter. Let's take a look at another way to work with them now.
Many web sites use RSS technology. When a button is shown with the
initials "RSS" or "XML", the button links to the site's RSS feed. This feed

I Extending ActiveX Data Objects I

741

normally contains headlines with links to full articles. In addition to


major news networks and other high traffic web sites using this
technology, it is likely that smaller web sites and corporate intranets will
make use of this technology as well (if not now, in the very near future).
For our next example, we will create a new UDL file named RSS.udl. The
Provider is "Microsoft OLE DB Simple Provider': The Data Source for
this UDL file will be "MSXML2.DSOContro1.2.6". We will use this UDL
file and specify the URL of the RSS feed in the Recordset.Open method.
RSS files follow a specific document structure. The three primary levels
are:
Channel
It em
Item Child

We will use a Recordset for the Channel and the Item and will print the
Title, Link, and Description of each Item in the Immediate Window to
get things started.
Sub ReadRSSAC)
Dim MyDB As New ADODB.Connection
Dim MyRS As New ADODB.Recordset
Dim ChannelRS As New ADODB.Recordset
Di m ItemRS As New ADODB.Recordset
MyDB.Open "File name=c:\MicroStation VBA\rss.udl "
MyRS.Open "http : //www.wired.com/rss/index . xml". MyDB
Whi l e MyRS. EOF = Fa l se
Set Channel RS = MyRSC "channel " ). Val ue
While ChannelRS.EOF = False
Set ItemRS = ChannelRSC"item " ).Value
While ItemRS . EOF = False
Debug . Pr i nt I temRSC "tit l e " )
Debug.Pr i nt vbTab & ItemRSC "link " )
Debug.Print vbTab & ItemRS( "description " )
ItemRS.MoveNext
Wend
ChannelRS.MoveNext

I Chapter 35: Working With Databases (ActiveX Data Objects) I

742

Wend
MyRS.MoveNext
Wend
End Sub

When Rea dRSSA is executed, the RSS of the wired. com website displays in
the Immediate Window. Since RSS files are usually updated fairly
frequently, the results shown in the Immediate Window will be different
from day to day and may even be different from hour to hour.

Ring-a-Ding-Ding, Baby
http://bloq. wire d. com/.ex/
Setting the proper mood wi th YOUl: ring tone . <strong>P Ius : </scrong> "Te!edildonic conception" rears its c
Tickr end Slickr Animate Flickx:
http://blog. wired. com/monkeybi te./
Nelil' apps bring scrolling images to desktops. <strong>Plus:</strong> tJhat makes Web 2.0 tick? From the Wit:
The POT.Je r

the SlUl

http://blog . wired. com/gadget./


Foldable solar batter cha.rger pucs life into every gadget in your 8t:senal. <scrong> Plus: </str onq> South 1<

Japan Grows a Beard


http://TNWfiJ . wired . com/netJs/colu.mns/O, 70013-0 . heml ?tiJ=J::S3. index
As Japan's population ages, its numbers are shrinking. Young and old shun a fast-paced life style for a
Devious Tactic Snags Phone Data
h ttp ://www.wired.com/news/ technology /0,70027-0 . htJnl ?tw=r . index
VendoI:3 impet:30nate speech-impait:ed customers in hundt:ed3 of thousands of fraudulent customet:-service cal ,-..

t~~j

,}11

The technology is great. It only takes 22 lines of code to retrieve the


information. So, now that we know the technology is in place and how
to access the data, how can we use what we know in a VBA project?
Let's start a new VBA project. We will name it Chapter 35c.m vba. A
reference to "Microsoft ActiveX Data Objects 2.X Library" needs to be
added. Then we will insert a User Form.
Today's Yahoo Tech RSS Feed: : :

Here is the Form:

I Extending ActiveX Data Objects I

743

The only control on the


Form is a ListB ox. The
ListBox's name is IstRSS. It
has two columns with widths
of 250 points and 0 points
respectively.
In the first column we will
place the Title of each RSS
feed item. In the second
column (which we hide by
giving it a width of 0 points) we will place the URL associated with the
Title in Column 1. When the user double-clicks on an item in the list, a
new web browser opens and displays the story.

Today's Yahoo Tech RSS Feed:


Survey Finds Google Is Strong in China (AP)
American commits suicide in Bulgarian internet forum (AFP)
H&R Block says Intuit to change ad claims (Reuters)
ST~l icro to market chip to detect bird flu in humans (Reuters)
Being a teen is hard work for this entrepreneur (USATODAY.com)
Google gains ground on Baidu in China: study (Reuters)
Microsoft to offer productionready Vista technologies (InfoWorld)
Foolish Forecast: ~Iotorola's Marginal Success (The ~Iotley Fool)
GPl 3 Released for Public Comment (News Fact or )
Intuit A rees to Pull TurboTax Ads AP

IDoubleclick to view story. I

When the selected item is double-clicked we use the Shell Execute API
command to open the default browser on the system and the selected
story appears.
Here is the code:
Private Declare Function ShellExecute Lib "shel 132 .dl l "
Alias "ShellExecuteA "
(ByVal hvmd As Long , _
ByVal lpOperation As String,
ByVal lpF ile As String, _
ByVal lp Para me ters As String, _
ByVal lpDirectory As String, _
ByVal nShowCmd As Long) As Long

744

I Chapter 35 : Working With Databases (Act iveX Data Objects) I


Private Sub lstRSS_DblCl i ck(ByVal Cancel As MSForms.ReturnBoolean)
She l lExecute 0, "OPEN", lstRSS.List ( lstRSS.ListIndex, 1 ) , "", "" ,

End Sub
Private Sub UserForm_Initialize()
Dim MyDB As New ADODB . Connection
Di m MyRS As New ADODB . Recordset
Di m Channe l RS As New ADO DB. Reco rdset
Dim ItemRS As New ADODB.Recordset
MyDB.Open "File name=c: \ MicroStation VBA \ rss.udl "
MyRS.Open "http://rss.news.yahoo.com/rss/tech'', MyDB
While MyRS.EOF = False
Set ChannelR S = MyRS ( "chann el" ) .Va l ue
Whil e ChannelR S. EOF = Fals e
Set ItemRS = ChannelRS(" item " ) .V alu e
Whil e I te mRS .EOF = Fal se
lstR SS . Addl tem Rep l ace( Item RS( "t itl e " ) , vb Lf, "" )
l st RSS .Li st (lstR SS .Li s t Coun t - 1. 1 )

It emRS( " link " )

ItemRS.MoveNext
Wend
Ch annelR S. Mo veNex t
Wend
MyR S.M oveNext
Wend
End Sub

Likely, RSS feeds will be used more frequently. As they do, this code will
become more important and more useful. For example, a company
could create their own RSS feed on their Intranet to display assignments
for personnel. What project am I working on today? Open my RSS
reader and it tells me.

EXAMINING DATABASE SCHEMA


At times, we are faced with the task of using data that we cannot open in
a native application. For example, we may have a Visual FoxPro .dbf file
but may not have Visual FoxPro. The same could be said of many
databases. Access, DB2, etc.

I Examining Database Schema I

745

ActiveX Data Objects gives us the ability to look at the Schema or in


other words, Database Structure of a given database.
Sub TestSchemaA()
Dim myDB As New ADODB.Connection
Dim myRS As New ADODB.Recordset
myDB . Open "file name=C:\MicroStation VBA\PlacePoints . udl"
Set myRS = myDB.OpenSchema(adSchemaColumns)
While myRS . EOF = False
Debug.Print myRS("TABLE_NAME") & "I" & _
myRS( "COLUMN_NAME " ) & "I" &
myRS( "IS_NULLABLE " ) & " I " &_
myRS( "DATA_TYPE " ) & " I " & _
myRS( "CHARACTER_MAXIMUM_LENGTH " )
myRS . MoveNext
Wend
End Sub

We still use a Connection Object and a Recordset Object. We still open


the database. But after it is open, we use the "OpenSchema" method of
the Connection Object. When we get the "adSchemaColumns", each
record returned has a number of different fields. Among them are
TABLE_NAME, COLUMN_NAME, IS_NULLABLE, DATA_TYPE,
and CHARACTER_MAXIMUM_LENGTH. These fields are important
so that we can understand the structure of each Field and Table
contained in the specified Database (specified in the UDL file, that is).

746

I Chapter 35: Working With Databases (Active X Data Objects) I


The values of each of these fields are printed to the Immediate Window.
The Immediate Window does not store an unlimited number of lines in
it. So, at times it makes perfect sense to write the values to a Text File.
File

Edit

Format

View

Help

r.lsySAcces sOb j ects I Oata ITrue 1128 13992


MSysAccessObjectsiIOITruel31
MsysAccess~~LIId I FalseI3 1

r.ISysAcces S~L I Lval ue ITrue 112810


~lsysAcces S~L IObj ectGui d ITrue 172 I
~ISysAcces s~L IObj ectName ITrue 113 0 165
r.lsysAcces S)<loIL I Property ITrue 1130 16S
MSysAccess~Ll v alue lT ruel1301 2 S S

Pl aceN otesiNoteBy iTruel130ls o


PlaceNotesiNoteOateiTruel 71
PlaceNotesiPlaceIOITruel3 1
placeNotesiTheNoteiTruel130 10
PlaceNotesiuniqueIOIFalsel3 1
Points icellNarne iTrue l13 0 130
pointsiCountyiTruel130ls0
Pointsioescription iTruel1301100
po i ntsiE l evationiTruel3 1
PointsiFederalStat us iTruel130 130
points lL atOec lTruels l
points iL atOMS ITrue l13018
pointslLonoeclTrue lsl
pointsiLonOMS ITruel13018
POints iPointType iTruel130 120
Points iPopulation iTruel31
PointslRef_LatOeclTrue l sl
PointslRef_LatOMSITruel130 18
pointslRef_LonOeclTruelsl
PointslRef_LonOMSITruel130l s
pointsistateiTruel13012
pointsiTypeA ITrue l31
PointsiTypeB ITruel13013
points iUn iqueIoiFalsel31
pointslUSGS_IOITruel31

The DATA_TYPE field returns a numeric value that corresponds with


ADO Constants.
IB adArray = 8192 (A flag value, always combined with another
data type constant, that indicates an array of that other data
type.)
IB adBigInt = 20 (Indicates an eight-byte signed integer
(DBTYPE_I8).)
IB adBinary = 128 (Indicates a binary value (DBTYPE_BYTES).)
IB adBoolean = 11 (Indicates a boolean value (DBTYPE_BOOL).)
IB adBSTR = 8 (Indicates a null-terminated character string
(Unicode) (DBTYPE_BSTR).)
IB adChapter = 136 (Indicates a four-byte chapter value that
identifies rows in a child rowset (DBTYPE_HCHAPTER).)
IB adChar

= 129 (Indicates a string value (DBTYPE_STR).)

IB ad Currency = 6 (Indicates a currency value (DBTYPE_CY).


Currency is a fixed-point number with four digits to the right of

I Exam ining Database Schema I

747

the decimal point. It is stored in an eight-byte signed in teger


scaled by 10,000.)
[B

adDate = 7 (Indicates a date value (DBTYPE_DATE). A date is


stored as a double, the whole part of which is the number of
days since December 30,1899, and the fractional part of which
is the fraction of a day.)

[B

adDBDate = 133 (Indicates a date value (yyyymmdd)


(DBTYPE_DBDATE).)

[B

adDBTime = 134 (Indicates a time value (hhmmss)


(DBTYPE_DBTIME).)

[B

adDBTimeStamp = 135 (Indicates a date/time stamp


(yyyymmddhhmmss plus a fraction in billionths)
(DBTYPE_DBTIMESTAMP).)

[B

adDecimal = 14 (Indicates an exact numeric value with a fixed


precision and scale (DBTYPE_DECIMAL).)

[B

adDouble = 5 (Indicates a double-precision floating-point value


(DBTYPE_R8).)

[B

adEmpty = 0 (Specifies no value (DBTYPE_EMPTY).)

[B

adError = 10 (Indicates a 32-bit error code


(DBTYPE_ERROR).)

[B

adFileTime = 64 (Indicates a 64-bit value representing the


number of 100-nanosecond intervals since January 1, 1601
(D BTYPE_FILETIME).)

[B

adGUID = 72 (Indicates a globally unique identifier (GUID)


(DBTYPE_GUID).)

[B

adlnteger = 3 (Indicates a four-byte signed integer


(DBTYPE_I4). )

[B

adLongVarBinary = 205 (Indicates a long binary value.)

[B

adLongVarChar

[B

adLongVarWChar = 203 (Indicates a long null-terminated


Unicode string value.)

[B

adNumeric = 131 (Indicates an exact numeric value with a fixed


precision and scale (DBTYPE_NUMERIC).)

= 201

(Indicates a long string value.)

748

I Chapter 35: Working With Databases (ActiveX Data Objects) I


[B adProp Variant

= 138 (Indicates an Automation

PROPVARIANT (DBTYPE_PROP _VARIANT).)


[B adSingle

= 4 (Indicates a single-precision floating-point value

(DBTYPE_R4).)
[B adSmallInt

= 2 (Indicates a two-byte signed integer

(DBTYPE_I2).)
[B adTinyInt

= 16 (Indicates a one-byte signed integer

(DBTYPE_Il).)
[B adUnsignedBigInt

= 21 (Indicates an eight-byte unsigned

integer (DBTYPE_UI8).)
[B adUnsignedInt

= 19 (Indicates a four-byte unsigned integer

(DBTYPE_UI4).)
[B adUnsignedSmallInt

= 18 (Indicates a two-byte unsigned

integer (DBTYPE_UI2).)
[B adUnsignedTinyInt

= 17 (Indicates a one-byte unsigned integer

(DBTYPE_UIl).)
[B adUserDefined

= 132 (Indicates a user-defined variable

(DBTYPE_ UDT).)
[B adVarBinary
[B adVarChar

= 204 (Indicates a binary value.)

= 200 (Indicates a string value.)

= 139 (Indicates a numeric value.)


adVarWChar = 202 (Indicates a null-terminated Unicode

[B adVarNumeric
[B

character string.)
[B adWChar

= 130 (Indicates a null-terminated Unicode character

string (DBTYPE_WSTR) .)
The Database PlacePoints.mdb has a Table named "Points", and a Table
named "PlaceNotes". When we look into the Schema we can see that
Tables named "MSysAccessObjects" and "MSysAccessXML" also
display. It is not uncommon for databases to create their own tables for
functions such as the indexing of indexed fields. In most cases, it is clear
which tables are 'system' tables and which tables are for our use.

I Excel Files as Databases I

749

EXCEL FILES AS DATABASES


Excel files are divided into Rows and Columns. Right? Well, then, it
makes perfect sense that we should be allowed to open them by using
ActiveX Data Objects. Let's begin by identifying an Excel file (.xls) we
want to work with. The towerdat.xls file is installed with MicroStation.
A search for it on our computer reveals that it is installed somewhere
under "Documents and Settings" in a rather lengthy path. Copying it
and pasting it into a more simple path makes it easier to use.
Let's create a UDL file named Excel.udl.

Here are the


settings in the
UDL file:

Provider

IConnection I .lI.dvanced I All

Select the data you want to connect to:

..Qbs.-'?..8 Provi9~I~L._.._. ____.____ ._._. ___ ._._J~1


Connectivity Service Provider
MediaCatalogDB OLE DB Provider
MediaCatalogMergedDB OLE DB Provider
MediaCatalogWebDB OLE DB Provider
Microsoft ISAM 1.1 OLE DB Provider
Microsoft Jet 3.51 OLE DB Provider

i j

Microsoft OLE DB Provider For Data Mining Services


Microsoft OLE DB Provider lor Indexing Service
Microsoft OLE DB Provider for Internet Publishing
Microsoft OLE DB Provider for ODBC Drivers
Microsoft OLE DB Provider for OLAP Services
Microsoft OLE DB Provider for OLAP Services 8.0
Microsoft OLE DB Provider for Oracle
Microsoft OLE DB Provider for Outlook Search
Microsoft OLE DB Provider for SQL Server
Microsoft OLE DB Provider for Visual FoxPro

61

Next

OK

Cancel

.~

Help

Even though the Jet driver is typically used for connecting to Microsoft
Access databases, we can use it to connect to Excel.

In the Connection tab, we need to browse for the Excel file. By default,
the Browse button's dialog box looks for Microsoft Access Databases
(.mdb). We can select the "*.*" option in the "Piles of type" combo box
and then select the Excel (.xls) file.
File name:

Itowerdat.xls

Files of type:

!AIIFiles ["")

~I
Cancel .

//

750

I Chapter 35: Working With Databases (Acti veX Data Objects) I

Provider Connection Advanced All


Specify the following to connect to Access data:
1. Select or enter a database name:

IC: \Microstation VBA \towerdat. xis


2. Enter information to log on to the database:
User name: lAd min
Patsword:

r ' 1- - - - - - - - - - -

P' Blank password

Allow saving password

When we are working with Microsoft Access files, we are finished


entering information into the UDL file at this point. But when we are
working with Microsoft Excel files, we need to make one more change.

Provider

I C~nnection I Advanced

All

These are the initiafization properties for this type of dal,


value, select a property, then choose Edlt Value below.

Extended Ploperlies
Jet OLEDB:Compact With...

Excel S.O ~
Faf~e

In the All tab, we need to give the "Extended Properties", a value of


"Excel 8.0". When we do this, the Jet driver knows it is working with a
Microsoft Excel file and so works with it accordingly. If we forget to add
this important 'Extended Property', we will see errors pop up because
without this value, the UDL file will treat the file as a Microsoft Access
Database.

I Excel Files as Databases I

751

Now, we want Excel to look as much like a database as possible before


we begin. So, let's remove a few rows at the top of the file and the first
column so we have a Header Row as shown here:
A

1
2
3
4

1R4?7

D
Z DATASET

??444

514 xy=18325.22791.514
526 xy=18414.22609.526
4h~ ",,=1 R4?7 ??444 4h~

1R1q?

??~R?

R4~

Since working with Excel is new territory, how can we tell what we have
to work with? Let's modify our previous "OpenSchema" procedures to
work with the Excel.udl file.
Sub TestSchemaC ()
Dim myDB As New ADODB . Con nection
Di m myR S As New AOOD B.Reco rds et
Dim FFile As Long
myDB . Open "fi l e name=C : \MicroStation VBA\Exce l .udl "
Set myRS = myDB . OpenSchema(adSchemaColumns)
FF i le = FreeF i le
Open "C:\ObSchema . txt" For Output As #FFile
While myRS.EOF = False
Print #FFi l e , myRS( "TABLE_NAME " ) & "I " &
myRS( "COLUMN_NAME " ) & "I" &
myRS( " IS_N ULLABLE " ) & " I" & _
myRS( "DATA_TYPE " ) & " I" & _
myRS( "CHARACTER_MAXIMUM_LENGTH " )
myRS.MoveNext
Wend
Close #FFile
End Sub

When
we
run this
procedure, a new file is
created that contains the
Database Schema of the
Excel file. Let's open it and
take a look at it. It is
named DbSchema. txt.

File

Edit

Format

View

Help

CABLEYUN I Fli True 15 I


CABLEYUNIF21Trueisi
CABLEYUNIF31Trueisi
DATA...RANGEl xy=1832S, 22791 ,S1 4 1Tr ue l13 0 1255
TOWERDAT$I Cable YITruelsl
TOWERDAT$lcable ZITr uelsl
TOWERDAT$I Cable x lTr uelsl
TOWERDAT$IDATASETI Tr ueI1301 2SS
TOWERDAT$l xI Truelsl
TOWER DAT$IYITrUelsl
TOWERDAT$lzITr ueISI

752

I Chapter 35: Working With Databases (ActiveX Data Objects) I


We can see a Table named "TOWERDAT$". That's interesting. When we
look at the Excel file, we find a Worksheet named "TOWERDAT". This
looks promising. Let's see what we can do with "TOWERDAT$".
Sub TestDBExcelA ()
Di m myDB As Ne w ADODB. Co nnec ti on
Di m myR S As New ADO DB .Rec ords et
Dim myField As Fie l d
my DB.Open "file name=C:\MicroStat i on VBA\Excel.udl "
myRS.Open "[TOWERD ATS]", myDB, _
adOpenDynamic, adLockOptimistic
While myRS.EOF = False
For Each myField In myRS.Fields
Debug . Print myField.Name & "I" & myField.Value
Next
Debu g. Print vbCr
Xl18325
YI 22 791
myRS .M oveNext
ZI514
Wend
DATASET lxy=1 832 5, 22 791,514
Cable x l1 8290
End Sub
Cabl e Y12292 9
Cabl e Zl8 94

Now
we
are
getting
somewhere. We are able to get to the data in an Excel file without
opening Microsoft Excel. For that matter, Excel doesn't even need to be
installed on the computer.
Let's see what else we can do.
Sub Test DBExcelB ()
Dim myDB As New ADODB.Connection
Dim myRS As New ADODB.Recordset
Di m CenPt As Point3d
Dim myCircle As ArcElement
Dim RotMatrix As Matrix3d
myDB . Open "file name=C:\MicroStation VBA\Excel . udl "
myRS . Open "[TOWERDATS ]", myDB, _
adOpenDynam i c, adLockOptimistic
While myRS . EOF = False
CenPt.X
myRS( "X" )
CenPt.Y = myRS("Y" )
CenPt.Z = myRS("Z")
Set myCircl e = CreateArcElement2(N othing, _

I Review I

753

CenPt. 4. 4. RotMatrix. O. 360)


ActiveMode l Referen ce.AddE l ement myCircle
my RS. Move Next
Wend
End Sub

N ow, we are drawing in MicroStation based on data in a Microsoft Excel


file that we are accessing via ActiveX Data Objects.

REVIEW
ActiveX Data Objects gives us tools to work with Data. At times this
Data is stored in Databases. This Data can be 'stored' on the Internet in
RSS files. This data can even be stored in a Microsoft Excel file.
Independent of where the data is, ActiveX Data Obj ects can be used to
retrieve the data. The process of connecting to data sources is simplified
greatly by the use of UDL files. Once connected, the Connection and
Recordset Objects can be used to retrieve, manipulate, edit, and add
data.

754

I Chapter 35: Working With Databases (ActiveX Data Objects) I

36

MicroStation Leveraging
Mathcad via VBA
Any time a company opens its product for customization, the consumer
wins. Mathcad is one such product. Mathcad worksheets can be used to
perform calculations and then can 'hand off' the information to
MicroStation through the use of VBA. Of course, Mathcad is not a
Bentley product. And the inclusion of Mathcad in this book should not
be considered an endorsement in any way. The same should be said of
Microsoft Excel and any other third-party products discussed in this
book. That having been said, Mathcad like Excel can be customized and
channels of communication can be opened between Mathcad and
MicroStation resulting in an integrated solution.
In this chapter:
[B A Brief Introduction to Mathcad
[B Adding a Reference and using the Object Browser
[B Basic Macros that communicate with Mathcad
[B Region Objects - The Basis for All Calculations
[B The Mathcad Object Model
[B Driving MicroStation Geometry from Mathcad

755

756

I Chapter 36: MicroStation Leveraging Mathcad via VBA I

A BRIEF INTRODUCTION TO MATHCAD


Mathcad includes functionality that allows us to perform calculations
(simple and complex) in a sketchpad type of environment. Variables can
be used in these calculations and standard mathematical nomenclature
is used so our formulas in Mathcad look just like they do in reference
m aterials we may use. Mathcad provides hundreds of operators and
built-in functions for solving technical problems. Mathcad can be used
to perform numeric calculations or to find symbolic solutions. It
automatically tracks and converts units and operates on scalars, vectors,
and matrices. Not only does Mathcad understand Units, but it takes
care of all conversions from one unit to the next for us (inches to meters,
gallons to liters, etc.) . And should we need a unit that does not appear in
Mathcad out of the box (such as Hands), we can add it to Mathcad.
Additional information about Mathcad can be found by visiting
www.mathcad.com.

ADDING A REFERENCE AND USING THE OBJECT


BROWSER
Before we attempt to communicate with Mathcad in any way, we need to
add a Reference to it in VBA. This is done by using the VBA menu
Tools> References.

~vailable References:

---===---_._----_._..,

[t] r~athcad 12 Automation API

C Mathcad Private Automation Type library

0d

...

OK

Cancel

C Mathcad XSL T Extensions library

!l.rowse ...

[J MAXComponents 1.0 Type library


[J Mead 1.0 Type library
[J McAfee Virus Scan API 1.0 Type library
C McAfee .com Virus Map Typelib
[J McAfee. com Virus Scan Control Type library
C MCLauncher 1.0 Type library
C MediaPlayer 1.0 Type library
C MerchantCard 13.0 Type library
C Messenger API Type library
C Messenger Extensions Type Library

rl Mp,<;<;p.nnp.r PrivAtA

TvnA I ihr i'lrv

Priority
~

[
.

Vi
t,.~j

<=;i""
l:~=-='k=:_=
~r
=U==
_':'
- =.. . ,=-=
-JI, --______"']j, Mathcad 12 Automation API ....

.. ..

iProg'ri,iiii Files\Mathsoft\Mathcad 13\automation .ilil

Location:

C:

Language:

Standard

tlelp

'---="-'----J

I Add ing a Reference and using the Object Browser I

757

Once a Reference has been added, we can use the VBA Object Browser
to 'browse' the Mathcad Object Model.

Me mbers of 'Application'

Classes
(j

~ ActiveWindow

<globa ls>

~ ApPlication

IlI

~ CustomMetadataC oliecti on

~ Applic ati on

~ Custom Meta datalte m


~ IMathcadApplication2

~ DefaultF il ePath

AcliveWorksheet

'''~ CloseAIi

~ IM athcadReg ion2

~ IMathcadWorksheet2
~ IM ati1cadWo rks heets2
~ IMetadata
~ Mathcad Old

~ Mathlnterfa ce
~ MatrixVa lue
riP MCApp Option

riP

MC CustomM etadataType

,t p MCF ileFormat

riP
riP
rfjJ

riP
~
~
~
~

MCReg ionType
MCSaveOption
MCWindowState
MCWorksheetOption
Numeri cVa lue
Region
RegionMetadata
Reg ions

fJ
fJ

Wi ndowActivate d
Wind owDe activated
~ Windows
fJ WorksheetClo sing
fJ WorksheetOp ened

ii i

~ Worksheets

Class AlllllicatiQII
Member of M"thc""
Malhcad Application Object

When we filter the Classes by selecting Math cad" in the top


ComboBox, we can see that we have an Application class in the Mathcad
Object Model. This is the top-level class in Mathcad, We can also see
that we have an ActiveWorksheet Object, These two objects should be
good enough for us to get started.
Let's begin writing a macro in VBA and we will see what we can
accomplish. Before we do this, we need to have Mathcad started and a
Worksheet open, Let's begin by using the file Sample 1,xmcd" which is
located on the CD that accompanies this book.

758

I Chapter 36: Mi croStation Leveraging Mathcad via VBA I


-~ ,:;t<;\!"~~..,.~>;..."",,,,"t;.

>'.N ..... __ ~ '"

'Y

,""~;<":""~ ~"'"t-"

;;>;~...

~""

h~Y;~W ",-w~

,,~~

_ "- ~-<1"""'C'\.'

.":"<."''<~~m""'---~-;;;r;-~I'i!>- >:'<i~

rUi!} /'Aat~cadJfi[Sampl!cJ,,,l!I!!,:dl~~~~~c::.ll:1,~~~;t,~~

. .~._B.!: __~~i~ _c~e_I'I _ ~ns=~_~_. . .~~r~~~.._ r.~~ls__ ...~~m~~I~~~_ .....~~~.~?I'I _ ~=!~ . _. . . _c_...._ . . ._.. . . c. c. .
j DT~1iiI ~~~ '"'{,~ ~

jl Narmal

j Iii 4' [;;;] x=


Here is a po rtion
of t he
Worksheet.

v ;IAr;al

.:'") ,,, -;vl 12

J~ <~ r~ ajJ ~

@D= ~ ~r:

vJL B I

!! r~2:E

IH~~_
e

_______.

RoughLength:= 18ft + Sin


JambShim := ~ in
2

StrikeShim :=

~in

MaxLength :=

300em

:MinLength :=

50 em

FinishLength := RoughLength - JambShim - StrikeShim


FinishLength

= "18' 4'''' FIF

We can see here that we have a variable named "RoughLength': one


named "JambShim", one named "MaxLength': and one named
"PinishLength", among others. Let's begin writing a macro in
MicroStation's VBA environment that will give us the value of the
RoughLength variable from Mathcad.
Sub TestMathcadA ( )
Dim my MCA As Mat hcad.App lic atio n
Dim myMCW As Mathcad , Worksheet
GetObject( , "Mathcad . Application " )
Set my MCA
Set myMCW = myMCA.ActiveWorksheet
End Sub

We saw the Application and ActiveWorksheet objects in the Object


Browser. If we run the macro Tes tMat hca dA we will find that the code
executes but we are not at the point where we are getting the value of the
RoughLength variable in Mathcad.

I Adding a Refe rence and using the Object Browser I

759

Let's take another look at the Object Browser in VBA.


~~~ '

H""~~.f:'"

'~~"<v_,"'<"

N_Ny"'.'"""''"''''''''"'!~' '' '"''<y''(~

.....

\"~'

.....

""-.

<

A~

'''T~''''''''

."......."

"""''''

' ~~j~ ~[fQ]~

Classes
~ Regions
~ StringValue
~ Va lue
~ Window
~ Windows

g[~~~t~~:::~l~tadaia

Mem bers of Wo rkshee t'


@i' Application
fl Changed
'C<; Clo se
@i' Fuli Name
"<; GetOption
GetValue

~ Works heets

,v

Fu nction GetValu e(bslrName As SIring) As Obj ect


Member of MathcadWorksheet

We know we want to get the value of a variable in the Worksheet. So, we


select "Worksheet" in the classes ListBox and begin looking for a
Property or Method that gives us the value we are looking for. As we do
so, we can see a method named GetV a1ue. It uses one parameter,
"bstrName as String". This method returns an Object. But what kind of
object? Another look at the Object Browser reveals an Object (in the
Classes ListBox) named "Value". Let's give the Value Object a try.
Sub TestMathcadA ( )
Di m myMCA As Mat hc ad . App l i cat ion
Di m myMC W As Mat hcad .W or ksheet
Dim myMCV As Mathcad . Value
GetObj ect ( . "Mathcad . Application " )
Set myMCA
Set my MCW = my MCA.Ac t ive Worksheet
Se t myMCV = myM CW. GetValue("RoughLength")
MsgBox myMCV.AsStr i ng & vbCr & myMCV.Type
End Sub
5.6134
Numeric

Now, in addition to connecting to the Mathcad Application and the


ActiveWorksheet, we are getting a Value Object based on the parameter
RoughLength and displaying its "As String" and "Type" properties in a
MessageBox.

760

I Chapte r 36: MicroStation Leverag ing Mathcad via VBA I

Edit ' View Help

0, 0254

CE

If

~0 00 0B

800000
GD [Q Q C2JOG

B0GDGJD

If we look at the RoughLength property


in Mathcad we see a value of 18'-5". But
when we ran the macro TesetMathcadA,
we were shown a value of 5.6134. Why
is this? It must be a units issue. Let's try
dividing 5.6134 by 221 inches and see
what we get.
When we see the magic number 0.0254
we know we are dealing with an inches
to meters conversion issue.

So, when we look at the Object Browser at the function GetValue, we see
that it returns an Object. When we use a variable declared as a "Value"
Object, we can get the ''AsString'' and "Type" properties of the "Value"
object. Is there more we can do here?
When we add a watch to the variable myMCV, we can see the other
properties belonging to the Value Object.

Imag
Integer
Real
Type

Double
Long
Double
string

6
5,6134
"Numeric"

The variable myMCV is declared as a "Value" but we can see here that we
are actually being returned a "NumericValue" (look in the Type column)
Object. Let's change our code a little by declaring the variable myMCV as
a "NumericValue" Object and see what happens.
S ub Te stHathc a dB ()
Dim myHCA As Hathc a d.Application
Dim myHChl As Hathc ad.hlo rkshe e t
Dim myl-ICV As Hathc ad . NurnericValue
Set my~ICA = GetOb j ect ( , "Hathcad . Applic a t ion" )
Set myl-IChl = myHCA . Ac tivehlorksheet
Set myl1CV = myl1ClJ. GetVal ue ("RoughLength")
msgbox mymcv.
End Sub

i1' iA~i3jiin.i

i1' Imag
i1' Integer
r1i' Real
i1' Type

I Adding a Reference and using the Object Browser I

761

Now, when we begin writing code to display properties in a MessageBox


we can see that we have more properties to work with because the
variable was declared as a "NumericValue" object.

5. 6 134

a
6

5 .6134
Numeric

i-UOK ---]1

Sub TestMathcadB()
Dim myMCA As Mathcad.Application
Dim myMCW As Mathcad.Worksheet
Dim myMCV As Mathcad.NumericValue
Set myMCA
GetObj ect ( . "Mathcad.Application")
Set myMCW = myMCA.ActiveWorksheet
Set myMCV = myMCW.GetValue( "RoughLength " )
MsgBox myMCV.AsStr i ng & vbCr & _
myMCV.lmag & vbCr & _
myMCV. l nteger & vbCr & _
myMCV.Real & vbC r &
my MCV .Type
End Sub

Now we are getting somewhere. In this MessageBox, we can see the


''As String" and "Real" property values of 5.6134, an "Integer" property
value of 6, and a "Type" property of "Numeric". Let's try making one
additional change to our code.
Sub TestMathcadC ( )
Di m myMCA As Mathcad . Applicat i on
Dim myMCW As Mathcad . Works heet
Dim myMC V As Mat hcad.S t r in gVal ue
GetObject( . "Mathcad.Application")
Set myMCA
Set myMCW = myMCA.ActiveWorksheet
Set myMCV = myMCW.GetValue("RoughLength " )
MsgBox myMCV . AsStr i ng & vbCr & _
myMCV.lmag & vbCr & _
myMCV.lnteger & vbCr & _
myMCV . Real & vbCr &
myMCV . Type
End Sub

762

I Chapter 36: MicroStation Leveraging Mathcad via VBA I


Instead of declaring myM CV as a "NumericValue" we declare it as a
"StringValue". What happens?
~~,.;'~~<r.":;<~~l~~~'Cf~.,.~ ";'""....,..~"'~,-

,o;m7-=""....,.. ... '"

~~~!!tt!iSv~~t~~!sl\}'&t,~~~ ~. ">~;*~,,

,/

...

~ ~'!'-:~w~~=.<;:'"tJ..~~

,~ h~:~ ~ ~ o:h~l. ';,,,

vv o "

<'1;;

Run-time error '13':


Type mismatch

Instead of getting the value of the Mathcad variable "RoughLength" we


see a Type mismatch error dialog box. Why is this? Because we are
dealing with a Numeric value, not a String value.
Lessons learned: When attempting to retrieve a variable value from
Mathcad and there is a degree of uncertainty as to what type of value we
will be getting, we should declare our Value variable as a "Value" object.
We can then look at the "Type" property and use the appropriate Valuespecific object. Here is an example of this:
Sub TestMathcadD ()
Dim myMCA As Mathcad . Appl i cation
Dim myMC W As Mathcad . Wor ks heet
Di m myMCV As Mathcad. Valu e
Di m myMCNV As Mathcad. Numer i cVal ue
Dim myMCSV As Mathcad.StringValue
GetObject(, "Mathcad.Application")
Set myMCA
Set myMCW = myMCA . ActiveWorksheet
Set myMCV = myMC W. GetValue("RoughLength")
Select Case myMCV.Type
Case "Numeric"
Set myMCNV = myMCV
MsgBox myMCNV.AsString & vbCr &
myMCNV.lmag & vbCr & _
myMCNV.lnteger & vbCr & _
myMCNV.Real & vbCr &
myMCNV.Type
Case "String"
Set myMCSV = myMCV
MsgBox myMCSV.AsString & vbCr &
myMCSV.Type & vbCr & _

I Basic Macros that Communicate With Mathcad I

763

myMCSV . Value
End Select
End Sub

We begin by usmg a generic "Value" object. Then we look at the


Value.Type
property.
If it
IS
"Numeric",
we
use
a
"Mathcad.NumericValue" object. If it is a "String", we use a
"Mathcad.StringValue" object.

BASIC MACROS THAT COMMUNICATE WITH MATHCAD


The more familiar we get with Mathcad, the more we realize how well it
can handle very complex calculations. But even though the calculations
Mathcad handles can be complex, communicating with Mathcad is not
complex at all.
Let's take a look at a few macros that communicate with Mathcad in a
variety of different areas. These macros continue to make use of the
"Sample 1" worksheet.
Sub TestMathcadE()
Dim myMCA As Mathca d. App l i catio n
Dim myM CW As Ma t hcad . Workshee t
Di m myMCV As Ma thc ad.V alue
Dim myMCNV As Mat hcad.Nu mericValue
Set myMCA
GetO bject(, "Math cad.Application")
Set myMCW = my MCA . Act i ve Wor ksh eet
Set myMCV = myMCW.GetValue("RoughLength")
Set myMCNV = myMCV
MsgBox "Roug h Length: " & myMCNV. Rea 1 / 0 .0254 & " Inches."
End Sub

TestMathcadE displays the RoughLength variable in a MessageBox after

converting it to Inches.
Rough Length: 221 Inches.

OK-~

Our previous examples dealt with pulling information from Mathcad.


Let's try changing a variable's value, recalculating the Worksheet, and
then pulling a value.
Sub TestMathcadF()
Dim myMCA As Mathcad.Application
Dim myMCW As Mathcad.Worksheet

764

I Chapter 36: MicroStation Leverag ing Mathcad via VBA I


Dim myMCNV As Mathcad.NumericValue
Set myMCA = GetObject(, "Mathcad.Applicatio n" )
Set myMCW = myMCA.ActiveWorksheet
myMCW.SetValue "JambShim", 0.375 * 0.0254
myMCW.Recalculate
Set myMCNV = myMCW.GetValue( " FinishLength " )
MsgBox "Fin ish Le ngth: " & myMC NV.Re al / 0 . 0254
End Sub

When this macro runs, we see a


MessageBox with a value in it
but the calculation seems to be
off. When we look at the
Mathcad Worksheet we can see that the JambShim variable has a
problem with the value we attempted to give it.
Why is this happening? Values assigned to Variables through ActiveX
Automation are calculated prior to those defined in the Mathcad
Worksheet. So, when we attempted to Set a Value for JambShim, in
addition to the value shown in the Worksheet, Mathcad attempted to
use a value from the SetValue call and this caused a problem.
So, we know why the error is occurring. How do we fix it? One solution
is to remove the "1/2 in" value assigned to the JambShim variable so the
duplicate value assignment is no longer taking place. And instead of
assigning JambShim a value of "0.375 )\- 0.0254", we are going to give it a
value of "3/8". Next, we are going to change the formula for the
FinishLength variable to 'convert' the JambShim value to Feet and
Inches. To best illustrate this change, we will look at the code and then at
the Worksheet.
Sub TestMathcadF2()
Dim myMCA As Mathcad.Application
Dim myMCW As Mathcad.IMathcadWorksheet2
Dim myMCNV As Mathcad.NumericValue
Set myMCA = GetObject(, "Mathcad.Application")
Set myMCW = myMCA.ActiveWorksheet
myMCW.SetValue "JambShim", "3/8"
myMCW.Recalcula t e
Set myMCNV = myMCW.GetValue("FinishLength")
MsgBox "Finish Length: " & myMCNV.Real / 0.0254
End Sub

I Basic Macros that Communicate With Mathcad I

JambShim:=

765

StrikeShim := ~in
2

MaxLength := 300cm
:MinLength :=

SOcm

FinishLen",crth := RoughLength - FIF(JambShim) - StrikeShim

The macro TestMathcadF2 now works correctly because we are allowing


the value for JambShim to be set in our code and we are using the
Mathcad function FI F to convert the provided value (which is 3/8) into
Feet and Inches.
GetValue, SetValue, and Recalculate form the basis for taking existing

Mathcad Worksheets and making changes and retrieving calculations


from them. As long as we make sure Mathcad is being given variable
values from only one source and that the correct unit conversion is
being performed on the values we set, Mathcad formulas and our own
VBA programming will give us consistent, correct results time after
time.
Each Value, Variable, Formula, and Calculation are stored in Mathcad
Worksheets as a "Region".
Sub TestMathcadG ( )
Di m myMCA As Math ca d .A ppli cat i on
Dim myMCW As Mathcad.Worksheet
Dim myMCR As Mathcad.Region
Set myMCA = GetObj ect ( . "Mathcad. Appl i cat ion " )
Set myMC W= myMCA.ActiveWorksheet
MsgBox myMCW.Regions.Count
For Each myMCR In myMCW.Regions
MsgBox myMCR .Typ e
Next
End Sub

The Region.Type property is an Enumeration named "MCRegionType".


Here are their values:
mcBitmapRegion
mcMathRegi on = 1

766

I Chapter 36: MicroStation Leveraging Mathcad via VBA I


mcMetaf ileRegion
mcOLERegion = 4
mcTextReg i on = 0

In the "Sample 1" Worksheet, we see a lot of "mcMathRegion" Regions.


An "mcMathRegion" is a Region that involves numeric calculations and
variable assignments. So, even if a variable is holding a String value, it
qualifies as a MathRegion.

REGION OBJECTS - THE BASIS FOR ALL CALCULATIONS


We just looked at the Region objects of a Worksheet. The Region object
gives us some very basic information such as a Type, and the Region's
location on the Worksheet through X and Y properties. But how are
calculations stored in Mathcad? Considering some of the complex
calculations that Mathcad can make, it is not difficult to imagine that it
would take more than a simple property to store this potentially
complex calculation information. And that is correct.
In addition to declaring a variable as a "Region': we can declare a
variable as a "IMathcadRegion2': When we do, additional properties are
available for our use.

IMathcadWorksheets2

Property Mathlnterface As Mathlnterface


readonly
Member of Mathead .lMatheadRegion2
property Mathlnterface

I Region Objects - The Basis for All Calculations I

767

Here are the Properties for the IMathcadRegion2 Object. One of them is
the "Mathlnterface" property. When we click on "MathInterface" in the
Classes list, we see the following:

--.--------.-------J
Moth cad
v
[_._---

C ___________ ~.I

Cla sses
~ IM ath ca dRe gion2
~ IM athca dWorks heet2
~ IMathcadWorkslleets 2

li!IJ

IMetadata

Memb ers of 'Math Interface'


~ , ~ ErrorMsg
~ HasError
~ UnitsXML
~ XML

~ Mathcad Ol d

li!IJ
li!IJ

MatrixValue
rfjl MCAppOptio n
Class Mathlntenace
Member at Mothcad
Mathcad Mathlntertace object

The MathInterface Object has a property named "XML'. That sounds


interesting. Let's try running the following macro:
Sub TestMathcad H()
Dim myMCA As Mathc ad. Appl i cat i on
Dim myMC W As Mathcad . Work she et
Dim myMCR As Mathcad.IMathcadRegion2
Dim myMCI As Mathcad . MathInterface
Set my MCA = GetObj ect ( , "Mathca d . Appl icati on " )
Set myMCW = myMCA.A ct i veW orks hee t
For Each myMCR In myMCW.Regions
Set myMCI = myMCR . MathInterface
Debug.Pr i nt myMCI.XML
Next
End Sub

When we run this macro, the XML property for each Mathlnterface of
each IMathcadRegion2 Object is written to the Immediate Window.
Here is an example of what is written when we run TestMathcadE :
<ml : define xm l ns :ml= ''h ttp : //schemas . mat hsoft . com/math20 ' )
<ml :i d xml : space =" preserve ")RoughLength</ml : id>
<ml : apply>
<ml : pl us/)
<ml : apply>

768

I Chapter 36: MicroStation Leveraging Mathcad via VBA I


<m l :mu lt sty le = "auto -sele ct " l>
<ml :real font="O ">18</ml :real>
<m l : id xm l :space=" preserve ">ft</ml : i d>
</ ml:ap ply >
<ml :apply>
<ml : mult style= "auto-se l ect " l >
<ml : r ea l fo nt=" O">5</ ml : real>
<ml : i d xml : sp ace =" prese r ve ">i n</m l : i d>
</ml : apply>
</ml : apply>
</ml : def i ne>

This is the data that is associated with the RoughLength variable in our
Worksheet. Let's take a look at a few more:
<ml :define xmlns:m l=''h ttp://schemas .ma thsoft.com/math20 '' >
<ml :id xml :space= "preserve ">JambShim</ml :id>
<ml :placeholder/>
</ml : defi ne>
<ml :define xmlns:ml=''http://schemas.mathsoft.com/math20''>
<ml :id xml :space= " preserve">StrikeShim</ml :id>
<ml:apply>
<ml :mult style="auto-select"l>
<ml:apply>
<ml:div/>
<ml : rea l >l</m l : rea l >
<ml: real >2</ ml : real>
</ml:apply>
<ml :id xml :space="preserve">in</ml :id>
</ml:apply>
</ml : defi ne>
<ml :define xmlns:ml= '' http://schemas.mathsoft . com/math20''>
<m l : i d xml :space="preserve">Max Length</ml :id>
<ml:app l y>
<ml :mult style="auto-select"l>
<ml :rea l >300</ml :real>
<ml :id xml :space="preserve">cm</ml :i d>
</ ml:apply>
</ ml : de fi ne>

IThe Mathcad Object Modell

769

<ml :de fine xmlns:ml= "h ttp://schemas.mathsoft.com/math20 ">


<ml :id xml :space= " preserve ">FinishLength</ml :id>
<ml:app l y>
<ml :minus/>
<ml:apply>
<m l :minus/>
<ml :id xml :space="preserve">RoughLength< /ml :id>
<ml:apply>
<m l :id xml :space= "preserve ">FIF</ml :id>
<ml :id xml :space="preserve">JambShim</ml :id>
</ml:apply>
</ml :apply>
<ml :id xml :space="p reserve ">StrikeShim</m l :id>
</ml :apply>
</ml : defi ne>

We can see here that the calculation for the FinishLength variable in our
Worksheet is the RoughLength minus the Feet and Inch value of the
JambShim variable minus the StrikeShim.

THE MATHCAD OBJECT MODEL


Let's take a look at a couple of the Mathcad Objects we can work with in
Mathcad. As with most ActiveX Automation Object Models, Mathcad's
top object is the Application Object.

Application
ActiveWindow

Height

Top

ActiveWorksheet

Left

Version

Application

Name

Vis ible

CloseAl1

Parent

Width

Defa u ItFi lePath

Path

Windows

FullName

Quit

Worksheets

770

I Chapter 36: MicroStation Leveraging Mathcad via VBA I


In addition to the Application Object, Mathcad has an
IMathcadApplication2 Object. Creating additional Application-type
Objects is usually done to preserve compatibility with the older parts of
an Object Model while introducing new properties and methods. Of
note here are the Get 0pt ion Method, the HWN0 property (often used in
Windows API programming), and the SetOpt i on Method.

IMathcadApplication2
Active

Height

Top

ActiveWindow

HWND

Version

ActiveWorksheet

Left

Visible

Application

Name

Width

CloseA l1

Parent

Windows

DefaultFilePath

Path

Worksheets

FullName

Quit

GetOption

SetOption

For more detailed information regarding the Mathcad Object Model,


the Object Browser in VBA gives us the names and members of Objects,
Properties, Constants, and Enumerations.

I Driving MicroStation Geometry from Mathcad I

771

DRIVING MICROSTATION GEOMETRY FROM MATHCAD


We have seen a few examples of getting and setting Mathcad variables
using VEA. Now, let's look at an example of how Mathcad can be used to
drive the creation of our MicroStation geometry.
+ 5in

Width

:~

1ft

Height

:~

2ft + 2in

HoleDia:~

12nun

MaxSpacing

:~

50nun

OutsideBuffer := 50ml1\

CaicuiateQuantity(Overall)

:~

Overall - OutsideBuffer 2
------HoleDia + MaxSpacing

QtyWidth := trunc(CaicuiateQuantity(Width)) + I
QtyHeight := trunc(CaicuiateQuantity(Height)) + I

ActualSpacing(Overall)

:~

Overall - OutsideBuffer 2
QtyWidth

SpacingX1n

:~

ActualSpacing(Width)

Sp acingYIn

:~

A ctualSp acing(Height)

- HoleDia

HoleDiain := HoleDia
OutsideBufferln :~ OutsideBuffer
Widthln: = Width
HeightIn

:~

Widthln

H eightIn

Height

17 in
26 in

QtyWidth

QtyHeight

6
10

SpacingX1n~

1.705 in

3.205 Ul

SpacingYIn
H oleDiain

0.472 in

OutsideBufferln

1.969 in

Our goal is to draw a 2D plate in MicroStation with a hole pattern. The


holes in the X Plane must be equally spaced and the holes in the Y Plane
must also be equally spaced (but the X Spacing and the Y Spacing can
differ). We want to maintain a 'buffer' around the outside of the plate.
We want to be able to specify the Width and Height of the plate, the Hole

772

I Chapter 36: MicroStation Leveraging Mathcad via VBA I


Diameter, the Maximum spacing between the holes, and the 'buffer' area
around the outside of the plate.
We could look at our goal and decide to perform all of the calculations
inside our code. But there are several advantages to using Mathcad in a
situation like this.
1

When using Mathcad, we can allow the parameters for our plate to
be entered using a large variety of units. For example, the Hole
Diameter can be entered in millimeters and the width and height
can be entered in decimal feet. The 'buffer' can be entered in yards
and the Maximum Spacing can be entered in cubits. The units used
to enter the design parameters are almost irrelevant because
Mathcad takes care of the unit conversions for us. Furthermore,
when we retrieve the value of any variables in the Worksheet, we can
specify in what units we want the value returned.

We can make changes to the calculations in Mathcad without


making changes to our code. So, different design rules could be
implemented in Mathcad and as long as we are 'outputting' the same
parameters, our code will continue to operate flawlessly.

Mathcad is capable of performing calculations using standard


mathematical notation that can be extremely challenging to
perform in VBA.

As we look at our Mathcad Worksheet we can see two types of


statements entered. One of them uses "X = 5" notation and the other
uses "X := 5" notation (notice the colon). The difference between these
types of notation are significant. When we see ":=" in Mathcad we know
a formula with/for calculations is being entered. When we see "="
(without the colon) we know we are displaying the value of a variable or
the result of a formula. Later on, we will see why the distinction between
these two 'types' of statements are important.
Separating the 'logic' from the 'presentation' of the resulting information
is a sensible thing to do in this instance. What we need now is code that
retrieves the results from Mathcad and uses them to draw the plate with
the hole pattern in MicroStation.
In a previous example in this chapter we used the Get Val ue method of
the Mathcad Worksheet Object to get a Mathcad variable's value. We
performed a conversion of the value in VBA from meters to inches.
Since we already have Mathcad taking care of our unit conversions for

I Driving MicroStation Geometry from Mathcad I

773

us, it makes sense to use Mathcad's converted values instead of


performing the unit conversions in our code.
By default, Mathcad stores all length type values in meters. Get Va 1ue

retrieves values in this default unit of measure. Rather than retrieving


values in Mathcad with Get Va l ue, we can retrieve values in the units
shown in our Mathcad Worksheets by looking at the IMathcadRegion2
Object of each Region.
When we look at Mathcad Region objects, we can see that there are
several types of regions. The two we are primarily interested in at this
point are the "define" and "eval" types. The information regarding these
Regions is stored using XML formatting. Here are some samples of the
two types of Regions we are interested in:
<ml :defi ne xml ns :ml= '' http://schemas . mathsoft. com/math20 " >
<ml :id xm l :space=" preserve">Heightln</ml :id>
<ml : i d xml: space= " preserve " >Hei ght</ml : i d>
</ml : defi ne>
<ml:eval xmlns : ml=''http://schemas.mathsoft . com/math20 '' >
<ml :id xml :space= " preserve">Widthln</ml :id>
<ml :u nitOverride> <ml : id xml :space="preserve">in< /ml : id>
</m l : uni tOverri de>
<result xmlns= '' http://schemas.mathsoft.com/math20 '' >
<unitedValue>
<m l : rea 1> 17 </ml : rea 1>
<unitMonomial xmlns=''http://schemas.mathsoft.com/unitslO '' >
<unitReference unit= " inch "></unitReference>
</unitMonomial>
</u nitedValue>
</resul t>
</m l :eval

>

Here we can see a "define" Region and an "eva!" Region. Notice how the
"eva!" Region has an "id" value of "Widthln" and a "real" value of" 17':
These are the Region values of interest to us.
When we want to retrieve a Region from Mathcad, it is best to retrieve
all Regions in the Worksheet at once in a separate function and then
parse them to find the values we are looking for. We will begin with a
Function named GetA 11 Regi ons.
Function GetAllRegions () As String
On Error GoTo errhnd
Dim myMCA As Mathcad.IMathcadApp l ication2
Dim myMCW As Mathcad.IMathcadWorksheet2

774

I Chapter 36: MicroStation Leveraging Mathcad via VBA I


Di m myMCR As Mathcad. IMath cadRegion2
Dim myMC I As Mathcad.MathInterface
Dim DomStrings() As Stri1g
ReDim DomStrings(O)
Set myMCA = GetObject( , "Mathcad . Application " )
Set myMC W = my MCA . Act i ve Worksheet
Dim myReg s As Math cad .Regi ons
Set myRegs = myMCW. Regions
For Ea ch myMCR I n my Regs
Se t myMCI = myMCR . MathInt erfac e
'Debug.Print myMCI.XML
' Debug . Print myMCI.UnitsXML
DomStrings(UBound(DomStrings)) = myMCI . XM L
ReDim Preserve DomStrings(UBound(DomStrings) + 1)
DomS t rin gs( UBound(DomStr i ngs)) = myMCI . Unit sXML
ReD i m Pr es er ve Do mSt r i ngs(UBo und(DomStr i ngs) + 1)
Next
GetAllRegions = Join(DomStrings, vbCr & vbCr & vbCr)
Exit Funct i on
errhnd:
MsgBox Er r .N umbe r & vbCr & Er r.Desc ri ption
End Function

GetA 11 Reg i ons returns a String comprised of Region XML data


separated by three (3) Carriage Return characters. We use these
characters to Split the return value back into Regions in the procedure
GetEva 1s.
GetEva 1s takes the returned String from GetA 11 Regi ons and extracts the
Eval Regions, returning only the Name and Value of each Eval Region
separated by an Equal Sign (=).
Function GetEvals() As Variant
Dim myDOM As New DOMDocument60
Dim myDOM2 As New DOMDocument60
Dim myNode As IXMLDOMNode
Dim myNodeList As IXMLDOMNodeList
Dim EvalName As String
Dim EvalValue As Dou ble
Dim AllRegions As St ring
Dim xSplit() As Stri ng
Dim Eval Array() As Stri ng

I Driving MicroStation Geometry from Math cad I

775

ReD i m Eva l Array(O) As Str i ng


Al l Regions = GetAllRegions
xSpl i t = Spl i t(A ll Reg i ons, vbCr & vbCr & vbCr)
For I = LBound(xSpl i t ) To UBound(xSpl i t )
If xSplit(I) <> "" Then
myD OM. 10adXML xSpl i tC I)
If my DO M.fi r st Chil d .ba se Na me = "ev a l " Th en
Eval Name
EvalValu e

=
=

myD OM.getEl ementsByTa gName ( " ml : i d" ) (0) . Text


myDOM . getElement sByT ag Name(" ml :real") ( O) . Text

Debug. Print Eval Name


Debug.Print Eva l Value
& " = " & EvalValue
ReDim Preserve EvalArray(UBound(EvalArray) + 1)
End If
End If
Next I
If UBound (Eva l Array) > 0 Then
Re Dim Preser ve Eva l Array( UBou nd(Ev a lA r ra y) - 1)
End If
GetEvals = EvalAr r ay
En d Function
EvalArray(UBound(EvalArray)) = EvalName

Now that we have the Region Values we can begin the process of making
use of the values and drawing the plate and its hole pattern in
MicroStation.
Sub DrawFromMathcad ()
Dim Al lEvals() As String
Dim PartWidth As Double
Dim PartHeight As Double
Dim OutsideBuffer As Double
Dim HoleDia As Double
Dim SpacingX As Double
Di m SpacingY As Double
Dim OtyX As Double
Dim OtyY As Double
Dim FilterReturn() As String
AllEvals = GetEvals
FilterReturn = Filter(AllEvals, "Widthln= " )
PartWidth = CDbl (Replace(FilterReturn(O), _

776

I Chapter 36: MicroStation Leveraging Mathcad via VBA I


"Wi dt hln=". ""))
FilterRe:urn
Filter(AllEvals. "HeightIn=")
PartHeight = CDbl(Replace(FilterReturn(O). _
"He i ght l n= ". "" ))
Filte r Re t ur n = Filte r(A llEvals. "Outsid eBuf fe rln =" )
OutsideBuf f er = CDb l (Replace ( FilterReturn (O) .
"OutsideBu f ferln= " . "" ))
Filter Re tu rn = Filter (A llE vals . "H ol eDialn =" )
HoleD i a = CDb l (Replace( FilterReturn(O).
"Ho l eDial n=". "" ))
Fi lterReturn = Filter(AllEvals . "SpacingXln= " )
SpacingX = CDbl(Replace(FilterReturn(O). _
"SpacingXln=". "" ))
FilterReturn = Filter(AllEvals . "SpacingYln=")
SpacingY = CDbl(Replace(FilterReturn(O) . _
"SpacingYln= " . "" ))
FilterReturn = Filter(AllEvals. "OtyWidth= " )
OtyX = CDbl(Rep l ace(FilterReturn(O). _
"OtyWi dth=". "" ))
Fi l terReturn = Fi l ter(AllEvals . "O t yHe i ght= " )
OtyY = CDbl(Replace(F i lterReturn(O) . _
"Ot yHe igh t= ". "" ))
Dr awPar t Pa rtWi dth . Pa rt Hei ght . Ou t s id eBuff e r .
HoleD i a . SpacingX . Spac i ngY . _
Ot yX. Oty Y
End Sub

The Array AIlEvais is 'Filtered' to get only the parameter we want. When
we find it, we get the value associated with the parameter by replacing
the parameter name and the equal sign 'with an empty string and then
converting the remaining text (the numeric value) to a Double by using
the standard VBA CO b1 function. Each of these parameter values are
placed into their own variable. These variables are then used to call a
procedure named OrawPart.

I Driving MicroStation Geometry from Mathcad I


Sub DrawPart(W idth As Double , Height As Double, _
OutBuffe r As Dou bl e, Hol eDia As Double, _
SpacingX As Doub l e, SpacingY As Double,
OtyWidth As Double, OtyHeight As Double)
Di m my Lin e As Li ne Elem ent
Di m myC irc le As El li pseElement
Dim XPos As Double
Dim YPos As Doub l e
Dim RotMa t rix As Matrix3d
Set myLine

CreateL i neElement2(Nothing, _
Point3dFromXY(O, 0), _
POi nt3dFr omXY( Width, 0))
Act i veModelRefere nc e . AddE l ement myL in e
Set my Line

Set myLine

Se t my Li ne

CreateLineE l ement2(Nothing , _
Point3dFromXY(Width, 0 ) , _
Point3dFromXY(W i dth, Height) )
ActiveModelReference.AddElement myLine
Crea te Lin eE lement 2(Noth in g , _
Point3dFr om XY(Width, Height), _
Point3dFromXY(0, Height))
ActiveModelReference.AddElement myLine
CreateLineE l ement2(Nothing, _
Point3dFromXY(O, Height), _
Point3dFromXY(0, 0))
Ac t iveModelReference.AddElement myLine
XPos = OutBuffer + HoleD i a / 2
For X = 1 To OtyWidth
YPos = OutBuffer + HoleDia / 2
For Y = 1 To OtyHeight
' *** Draw the Circle ***
Set myCircle = CreateEllipseElement2(Nothing,
Point3dFromXY(XPos, YP os ) , HoleDia / 2 , _
HoleDia / 2, RotMatrix)
ActiveModelReferen ce.Ad dEl emen t myCir cl e

777

778

I Chapter 36: MicroStation Leveraging Mathcad via VBA I


YPos = YPos + Spac ing Y
Next Y
XPos = XPos + SpacingX
Next X
End Sub

The code is in place. Our Mathcad Worksheet is open and the design
criteria has been entered. The only thing to do now is run the procedure
DrawFromMathcad and see how the calculations we entered into Mathcad
look.
o

I Driving Mi croStation Geometry from Mathcad I

779

When we run DrawFromMathcad and take a look at the results in


MicroStation, we can see that something is not right. The horizontal
spacing appears to be correct but the vertical spacing is not.
A careful review of our formulas in Mathcad reveals that ActualSpacing
function is using the OtyWidth Mathcad variable we defined. Since the
OtyWidth and the OtyHeight variables will likely have different values,
we need to add one more parameter to the Actua 1Spa c i ng function and
provide the value when we calculate the SpacingXln and SpacingYln
values.
. g(O
Q'
Overall - OutsideBuffer 2 - HoleDia
A etu alS paelll
verall. ty):= - - - - - - - - Qty - 1
SpaeingXIn:= AetualSpaeing(Width.QtyWidUl)
SpaeingYln := AetualSpaeing(Height .QtyHeight)

Now that we have added a "Qty" parameter in the Actua 1Spaci ng


function, and provided the parameter in the Spaci ngX I nand Spaci ngY I n
formulas, the values in the Mathcad Worksheet are updated
automatically. We can erase the drawn elements in MicroStation and
run the OrawFromMathcad procedure again.

780

I Chapter 36: Micro$tation Leveraging Mathcad via VBA I

Now the spacing looks correct in MicroStation. Let's add a few


dimensions to verify that we are meeting our design criteria as
established in Mathcad.

I Driving MicroStation Geometry from Mathcad I

781

2.0457

1.9685

The Buffer area looks right. But the spacing between the holes isn't
meeting our criteria. A review of the Formulas and Functions in
Mathcad shows us that the Ca lc ulate Quantity Function has one more
error in it. We need to calculate the quantity of items, not the number of
spaces. So, we will add 1 to the formula so we are retrieving the number
of items that fit in the space, instead of the number of spaces.
C alculat eQ uantity( 0 verall) : =

Overall _. OutsideBuffer2 - H oleDia


H oleDia + M axSpacing

We fix the formula in Mathcad and run the macro from within
MicroStation's VBA environment and the plate is drawn again. Adding a
few dimensions to it shows us the result.

782

I Chapter 36: MicroStation Leveraging Mathcad via VBA I

1.626

o
1.9685

Now, it looks like the spacing between the holes is meeting our criteria
of "MaxSpacing = SOmm". Changing the Width, Height, Hole Diameter,
and OutsideBuffer in Mathcad and running the macro again should
yield similar results - a plate drawn in MicroStation with holes spaced
evenly (in the respective X and Y planes) and spaced within the
MaxSpacing rule.

REVIEW
Someone once said, "Give me a lever and I will move the World:' A
study of the effects of various types of levers shows that with a lever we
can move objects that would be impossible to move without the use of
the lever. Mathcad and other applications that allow us to communicate
with them through ActiveX Automation serve as levers. They allow us to
accomplish tasks previously error prone and time consuming with little
or no effort.
This chapter is by no means a comprehensive reference on the use of
Mathcad or its API. An entire book could be devoted to that topic. The
goal here is to introduce and demonstrate the ease with which we can
take engineering data and calculations from Mathcad and use them in
our design work within MicroStation.

37

Accessing Data from


External Applications
We have controlled MicroStation from within MicroStation's VBA
environment. We have also controlled Microsoft Excel from within
MicroStation's VBA environment. It would stand to reason, then, that
we can control MicroStation from Excel's VBA environment. And we
can.
In this chapter:
[El

ActiveX / COM Basics

[El

References, Early Binding, and Late Binding

[El

GetObject, SetObject, and 'New'

[El

What does 'WithEvents' do for us?

[El

When to run macros from within Excel and when to run them
from within MicroStation

[El

Controlling MicroStation from within Excel

783

784

I Chapter 37: Accessin g Data from External Applications I


ACTIVEX /

COM BASICS

What do ''ActiveX Automation" and "COM" have in common? When


viewed in a 'common' light, they have everything in common because
they are often referred to as the same thing. In essence, when we talk
about ActiveX Automation and/or COM, we are talking about a
mechanism used to allow one application to 'talk' to another application.
We are talking about one application 'driving' the other one or 'deriving'
information from the other one.
For years, application vendors have attempted to come up with ways for
their customers to customize their software. A large variety of methods
have been devised to accomplish this. Dynamic Data Exchange (DDE)
was a method whereby one application could send messages to the
other. Other software developers have come up with clever ways to
simulate the user's interaction with the software including mouse
movement and clicking on the application's buttons as well as simulating
keyboard entry in the other application. And yet others have invented
scripting mechanisms that would read a file and follow instructions
contained in the file.
ActiveX Automation gives us the means to programmatically control
applications through the use of the Application's object model. We
traverse the Object Model until we arrive at the Object that exposes the
Properties, Methods, and Events with which we want to work.
Application Objects have Object Names and Class IDs. Which is easiest
to work with? Let's take a look at the MicroStationDGN Application.
Would we rather use "MicroStationDGN.Application" or "{1 21c46499bb6-11d4-81ce-OOS004ge89cb}"? We refer to Classes by their Names
instead of by their IDs for the obvious reason.
"MicroStationD GN .Application", "Excel.Application': and
"Word.Application" are some examples of Class Names we may know.
These top-level Objects serve as a starting point and allow us to drill
down to Documents, Worksheets, Paragraphs, and the like.
Some Applications have exposed COM Interfaces even though they
have not implemenled or licensed Microsoft's VBA environment. So, the
fact that an Application does not have VBA embedded into it does not
necessarily mean we have no way of communicating with it. The
application may very well be open to customization through COM
(ActiveX Automation) . One example is Internet Explorer.

I References, Early Binding, and Late Binding I

785

Sub Runlntern etEx plor er()


Dim mylnet As Object
Set mylnet = CreateObject("InternetExplorer . Application")
mylnet . Vi s i ble = True
mylne t .Navigate 2 "h t tp: // www. be nt l ey . com "
MsgBox "At Bentley ' s Website."
mylnet.Navigate2 "http://www.micr os oft. co m"
MsgBox "At Microsoft's Website."
End Su b

In the above example, we are 'connecting' to Internet Explorer by


creating a new instance of it, making it visible, and displaying two
websites in it. Internet Explorer has a COM interface so we can control it
using VBA code in MicroStation's VBA environment.

REFERENCES, EARLY BINDING, AND LATE BINDING


We have added many References in this book so far. And why do we do
this? And what can we add a Reference to?
In VBA, go to the menu Tools> References.

Available References :

OK
Cancel

:~:j Microsoft Access 11.0 Object Libr ary


i::J Microsoft
;::J ~licrosoft
[J Microsoft
[J Microsoft
[J ~licrosoft
[J Microsoft
i:J Microsoft
[ J Microsoft

Active Server Pages Object Library


Active Server Pages Object Context Object
ActiveMovie Control
ActiveX Data Objects (Multi-dimensional) 2.
ActiveX Data Objects 2.0 Library
ActiveX Data Objects 2 .1 Library
Priority
ActiveX Data Objects 2.5 Library
ActiveX Data Objects 2.6 Library
.. I
[J ~licrosoft ActiveX Data Objects 2.7 Library
~
[] Microsoft ActiveX Data Objects 2.8 Library
i::J Microsoft ActiveX Data Objects Recordset 2.7 Librar) -',
Mirrn<nft ArtivAX Ph Inin
;~ l

Browse ...

-.!J

~: ::;,'~- gL-~~~-,,]

Help

,Lei

" .. ~, Type Libr ary ---------------------------------------------------- -------------- --------------------------- - ,


Location:

C:\Program Files\Messenger\msmsgs.exe

Language:

Standard

We have seen this before. Let's talk a little more about what we are
actually seeing.

786

I Chapter 37: Accessing Data from External App li cations I


When we look at the References dialog box, we are looking at References
that can be added to our project. These References are stored in files
usually having one of three different file extensions: .olb, .tlb, and .dll.
If we click the Browse button, we see the Add Reference dialog box.

Look in:

IeJ system32

:::J

+-

d IITI

1~
~, 3com _-dmi -------------~
-1 0-41---

1~ 1~5
IB 1~8

~ 1 ~2
~1~4

1r(::) 1031

B2052

ifC:l 1037

fC:l appmgmt

IB 1 ~

B ~~

Ii5!,':
.. ,

I. Open

File name:
Files of type:

IType Libraries ("olb;".tlb;". dtll

Cancel
Help

If we need to add a Reference and it doesn't show up in the References


dialog box we can add it by browsing to it and adding it in the Add
Reference dialog.
Type Libraries can do two things for us. First, they give us the Object
Model - the structure of the Objects with their Properties, Methods,
and Events. With this comes features such as Intellisense - the lists and
tip text boxes that appear as we are writing code. The other thing they
do is provide easy access to the Objects and make it possible to perform
Early Binding.
Early Binding? Late Binding? Binding is the process of taking a variable
and setting it to an Object. When we speak of "Early Binding", we mean
we declare a variable as a specific type of Object. A variable bound early
performs better, allows for intellisense on the variables, and performs
type checking as we develop our code.
Let's review a snippet of code we wrote earlier:
Sub RunlnternetExp l orer ()
Dim myln et As Objec t
Set mylnet = CreateObject( " InternetExplorer.Application " )
mylnet.Visib l e = Tr ue
mylnet . Navigate2 "http : //www . bentley . com "
MsgBox "At Bentley ' s Website ."
mylnet.Navigate2 "http : //www . microsoft.com"

I References, Early Binding, and Late Bind ing I

787

MsgBox "At Microsoft ' s Website. "


End Sub

Are we doing early binding or late binding here? Late binding. Why?
Because we declare the variable mylnet as an Object. Declaring a
variable as an Object provides for a generic Object that could be an
Internet Explorer Application, a Line Element, or a Database
Connection. The variable does not know what type of Object it is until it
is Set with code such as:
Set mylnet

CreateObject( " InternetExplorer.App l ication")

After this line of code is run, the variable mylnet knows what type of
Object it is. When we use late binding, Intellisense is not active because
the variable does not know what type of object it is.
Sub Run I nt e rn e tExpl orer()
Dim myInet As Ob j ect
Set myIn e t = Cre ate Obje c t("InternetE xp l o rer.Appli c ati o n")
myInet.

In the above graphic, when we type 'myInet: (with a period after the
variable name), Intellisense does not show up to help us. If, however, we
declare my Inet as an "InternetExplorer", we have Intellisense to help us if
we have also added the correct Reference, which in this case is
"Microsoft Internet Controls".
Sub RunInte rnetExpl o rer()
Dim myInet As InternetExplore r
Set myInet = Cre a t e Object ( "Inte rnetE x pl o r e r.Ap pli c ation")
myIn e t.
my In e ~ !~~:~f.~:~~:~:~~:::: ::::::::::::::::::::::::::~ ~
www.bentley.co m "
NsgBo ~ Application
si t e. "
myIne @' Busy
/"WWW.micros oft . com "
~lsgBo ,~Cli entToWi ndow
ebsite."
End Sub
@' Container
~ Doc um ent
I
v
"'~ ExecWB

:.

Now, when we type "myInet." we get Intellisense helping us because we


have declared the variable mylnet as a specific type of Object and it is
Referenced correctly.
Code written that uses Late Binding does work. But code written that
uses Early Binding works better, is faster to develop, and easier to debug.

788

I Chapter 37: Access in g Dat a from Externa l App lications I


GETOSJECT, SETOSJECT, AND NEW
When we want to work with an application through ActiveX
Automation, we must 'connect' to it before we do anything else. There
are three ways we do this.
For the next few examples, we are going to develop our code in
Microsoft Excel's VBA environment. We are also going to add a
Reference to the "Bentley MicroStation DGN #.# Object Library':
Sub ConnectToMicroStationA ()
Di m myMSAppCon As Mi croStat i onD GN.App li cat i onObjectConnector
Dim myMSApp As Mi croStat i onDGN.App li cation
Set myM SA ppCo n = Ge t Obje ct ( , _
"Mi croSta ti on DGN. Appl icati onObj ect Connecto r" )
Set myMSA pp = myMSApp Con .Ap pli cat ion
MsgBox myMSAp p.Cap ti on
Set my MSAp p = Noth i ng
Set my MSAp pCo n = Not hin g
End Sub

When this code is run,


we will either see
something like thi s:

us.er interface,dgn, Index (2D - va DGN) - MlcroStation V8XM Edition (Seta)

IL~:~:::::~:::::~
Run-time error '429':
ActiveX component can't create object

or something like this:

~d

tielp

In the first MessageBox, we see MicroStation's Caption. In the second


one, we are told ''ActiveX component can't create object". If we see the
first MessageBox, we know the code worked. If we see the second, we
know it didn't work. Why? Because GetObj ect is used to Get an existing

I GetObject, SetObject, and New I

789

instance of an Object. In this example, MicroStation must be running


before we can use GetOb j eet.
Here is our next example. It makes use ofC reateO bj eet.
Sub ConnectToMicroStationB ()
Dim myMSAppCon As MicroStationDGN.ApplicationObjectConnector
Dim myMSApp As MicroStationDGN.Application
Set myMSA ppCon = _
CreateObject("MicroStationDGN.ApplicationObjectConnector")
Set myMSApp = myMSAppCon.Application
myMSApp . Vi sib l e = True
MsgBox myMSApp . Capt i on
Set myMSApp = Nothing
Set myMSAppCon = Nothing
End Sub

Now, instead of getting an existing


instance of MicroStation, we create a
new instance by using CreateObj eet. If
we run this macro multiple times, we
will end up with multiple instances of
MicroStation running.
A look at the Taskbar shows that ConneetToMi eroStat i onB has been run
four times.
GetObjeet gets an existing instance of an application. CreateO bjeet
creates a new instance of an application. The Ne w keyword can also be
used to create a new instance of an application. And there are two ways
to use it.
Sub ConnectToMicroStationC ()
Di m myMSAppCon As New _
Mi croStat i onDGN . Appl i cationObjectConnector
Dim myMSApp As MicroStat i onDGN . Application
Se t myMSApp = myMSAppCon .Applicat i on
myMSApp . Vis i ble = True
MsgBox myMSApp . Caption
Set myMSApp

Noth i ng

790

I Chapter 37: Accessing Data from External Applications I


Set myMSAppCon
End Sub

Nothing

We can use the New keyword when we declare an Object-type variable.


ConnectToMicroStat~onC causes a new MicroStation application to start.
The keyword 'New' can also be used as follows:
Sub ConnectToMicroStationD ()
Dim myMSAppCon As MicroStationDGN . ApplicationObjectConnector
Dim myMSApp As MicroStationDGN.Application
Set myMSAppC on = New _
MicroStationDGN .A pplicationObjectConnector
Set myMSApp = myMSAppCon.Application
myMSApp . Visible = True
MsgBox myMSApp.Cap t i on
Set myMSApp = Nothing
Set myMSAppCon = No thi ng
En d Sub

In this example, we declare the variable myMSAppCon and then we use


the code to create a new instance of MicroStation.
Set myMSAppCo n

New Mi croStat i onDGN . App l icat i onObjectConnector

We have worked with code that gets an existing instance of MicroStation


as well as code that creates a new instance of MicroStation. The code
works. Now let's discuss when we might want to use these mechanisms.

When to use GetObject, CreateObject, and New


Let's begin with CreateObject and New. There are three reasons we may
want to create a new instance of MicroStation:
1

We need to work with MicroStation and would be happy to work


with an existing instance if one existed, but MicroStation is not
currently running. In this case, we probably used GetObj ect but
received an error so we use either Cr eateObject or New (as used in
ConnectToMi croStati onO) so we can work with MicroStation.

An instance of MicroStation is running but we do not want to


interrupt that session with our code so we create a new instance of
MicroStation.

I GetObject, SetObject, and New I


3

791

Multiple instances of MicroStation are or m ay be running. If we use


Get Ob j ect, we cannot be certain which instance of MicroStation we
will 'attach' to, so we use Crea t eObject or New and we are 'attached' to
a new instance of MicroStation.

The second and third circumstances just described are self-explanatory.


The first, however, should be demonstrated with some code:
Su b Con nect ToMi c r oSt ati onE()
Di m my MSAppCon As MicroStationDGN.ApplicationObjectConnector
Dim myMSApp As MicroStation DGN . App l ication
' Att ach to ex i st i ng or create new if no existi ng MicroSta ti on
On Er ror Resume Next
Set myMSAppCon = GetObject ( . _
"Mi croStationDGN . Applicatio nObjectConnector " )
If Er r . Number = 429 Then
Set myMSAppCon = _
CreateObject( "MicroStationDGN.Applicat i onObjectConnector " )
Err.C l ear
End I f
On Er r or GoTo 0
Set myMSApp = myMSAppCon . App li cat i on
myMSApp.V i s i bl e = True
MsgBox myMSApp.Caption
Set myMSApp = Noth i ng
Set my MSA ppCon
No t hi ng
En d Sub

In this example, we are fully aware that MicroStation may not be


running. We use the statement "On Error Resume Next" so if we do
encounter an error, the code will not stop executing. Immediately after
attempting to GetO bject, we take a look at the Er r . Number to see if we
encountered an error number of 429 (this is the error number we will
get if we attempt to use GetO bj ect and the Object we are trying to get
does not exist). In the above example, if we do see Error 429, we use
CreateOb j ect and clear the Error. Then we use "On Error GoTo 0" to
reset the error handler in VBA.

792

I Chapter 37: Accessing Data from External Applications I


WHAT DOES 'WITHEvENTS' DO FOR US?
We use the keyword Wi thEvents when we want to communicate with
another application and want to be notified when certain events take
place.
Let's create a new User Form in Microsoft Excel's VBA environment. We
will also add a Listbox to the Form. After this is done, we will enter the
following code:
Di m myMSAppCon As Mi croS t ation DGN. ApplicationO bj ectConnector
Di m WithEvents myMSApp As MicroStati onDGN . Application
Pr i vate Sub User Form_ In i t i ali ze()
Set myMSAppCon = GetObject(. _
"MicroStat i onOGN.App li cationObjectConnect or")
Set myMSApp = myMSAppCon.Application
End Sub
Private Sub myMSApp_OnDesignFileClosed(ByVal _
DesignFileName As Str i ng)
ListBoxl.Addltem "CLOSED" & OesignFileName
End Sub
Private Sub myMSApp_OnDesignFileOpened(ByVal _
DesignF il eName As String)
Li s tBox l. Addlte m "OPENED" & Des i gn FileName
End Sub

When we declare the variable myM SApp, we do so with the WithEvents


keyword. This adds the variable myMSApp to the Object ComboBox in
the Code area of the Form. When myMSApp is selected in the Object
ComboBox in the code area of the Form, the events associated with the
Object the variable represents display in the Procedure ComboBox.

I Run Macros from Excel or MicroStation? I

793

When the form is run, it attaches to an existing instance of MicroStation


and each time a file is closed or opened, the code in the appropriate
event is executed .

....(~.Q~.~P..(.:.\P.Qqll))~Q~>.. ~m! ..~.~.tt.iO'9.~\f\.I.I.. I,J.~~r>.\Appli~.il.tigO .Piltil.\Elgot.l.~y.\.w..Qrl9.Pi!~.~.\.p.rQj~~.t.~\1


OPENED C:\Documents and Settings\AII Users\Application Data\Bentley\WorkSpace\Pro jects\1
CLOSED C:\Documents and Settings\AII Users\Application Data\Bentley\WorkSpace\Projects\1
OPENED C:\Documents and Settings\AII Users\Application Data\Bentley\WorkSpace\Projects\1
CLOSED C:\Documents and Settings\AII Users\Application Data\Bentley\WorkSpace\Projects\1
OPENED C: \Documents and Settings\AII Users\Application Data\Bentley\ WorkSpace'\Projects\1

WithEvents allows us to capture events of external applications and


respond to them. In this example, we are adding the names of the files
opened and closed in MicroStation to a Listbox created in Excel's VBA
environment. The possibilities are endless as to what we can do when we
are notified of the events taking place in MicroStation.
It should be stated here that the same Wi t hEve nt s keyword can be used

with reference to the Microsoft Excel.Application Object. We can


capture events taking place in Excel from within MicroStation's VBA
environment.

RUN MACROS FROM EXCEL OR MICROSTATION?


When do we run the macros from within Excel and when do we run
them from MicroStation?

If we can control MicroStation from Excel and can control Excel from
within MicroStation, when do we write code in MicroStation and when
do we write it in Excel? That is a good question. There are several things
that we should consider. They are not listed in order of importance
because the importance of any particular item probably depends on the
situation in which we find ourselves at the moment. For the sake of
discussion, we will use MicroStation and Microsoft Excel as the two
applications in which we are developing.
[B Is MicroStation installed on this computer? If we develop in

MicroStation's VBA environment, we know the code will be run


on a computer with MicroStation installed on it. If we develop

794

I Chapter 37: Accessing Data from Externa l Applications I


in Excel, the code is housed in Excel and it is possible that
MicroStation is not even installed.
[B

Communication over the COM pipeline takes time. Not a lot of


time, necessarily, but it does take time. If we have a lot of data in
Excel and we need to do a lot of work with that data in Excel, an
application may take less time to run if it is run from within
Excel. However, if a majority of the processing is done in
MicroStation, the code should be developed inside
MicroStation's VBA environment.

[B

Are we using GetObj ect or Crea teObj ect? Microsoft Excel is


used by many developers and at times, instances of Excel are
running in the background (even though the application is not
visible). And we do not know if it is running in the background
unless we look for "EXCEL.EXE" in the Processes tab of the
Windows Task Manager. Often times, data will be stored in
Excel and an Application will run from within MicroStation.
The concept is, the user opens the file he/she wants to use in
Excel and Ge tO bj ect is used inside MicroStation's VBA
environment to read the data. However, if an instance of Excel is
running in the background, it (the instance running in the
background) may be the instance of the Excel.Application
object that GetObj ect returns and the program will either crash
or it uses data other than what the user is expecting. By the way,
when using Create Obj ec t, we should always set the Visible
property to True so we don't end up with applications running
in the background.

Choosing the best tool for the job should always be near the top of the
list of things to consider before developing an application. We have
listed several things worthy of consideration and there are others that
may pop up that are specific to a project or task.

CONTROLLING MICROSTATION FROM WITHIN EXCEL


We know that MicroStation VBA projects are stored in .mvba files.
Excel, however, is a little different. When we write VBA code in Excel,
the code is stored in a Microsoft Excel Workbook (.xls file). Excel VBA
programming can also be stored in an Excel Add-In (.xla file). We will
discuss Add -Ins later in this chapter.

I Contro lli ng MicroStation from within Excel I

795

Let's create a new Excel Workbook. We will then get into Excel's VBA
environment by clicking Tools> Macro> Visual Basic Editor.
Next, let's insert a new Module.
: E.~e

!;.dit

~ew

,t~~ Q I ~

Insert

FQ!mat

Q.ebug

,\f1.~

MIl

r' I

'-'1

Bun

.UJ

10015
0

l.dd-lns

~ I ~ @"

'Wndow

tlelp

W '~~ l @ I, Ln I, Coil

VBAProject
f"l

<5 Microsoft Excel Objects


:- Ii!) Sheetl (Sheet!)
. Ii!) Sheet2 (Sheet2)
,. Ii!) Sheet3 (Sheet3)
.. :[) ThisWorkbook

e':l ~Iodules
'... ~ Modulel

The VBA environment in Excel should look a lot like the VBA
environment in MicroStation. One thing that is different, however, is the
Project Window. "Microsoft Excel Objects" are available to us. What are
they? These Excel Objects allow us to write code directly into events
pertaining to specific Worksheets or the Workbook.
So, we have a Code Module inserted. And we want to write code that
communicates with MicroStation. What is our next step? We should add
a Reference to the "Bentley MicroStation DGN #.# Object Library".
With the Reference in place, it is time to write some simple code that
communicates with MicroStation.
Sub XLMSA()
Dim myMSAppCon As MicroStationDGN.ApplicationObjectConnector
Dim myMSApp As MicroStationDGN.Application
Dim myLevel As Level
Dim CurRow As Long
Set myMSAppCon = GetObject( .
"MicroStationDGN.ApplicationObjectConnector " )
Set myMSApp = myMSAppCon.Application
CurRow = 2
For Each myLevel I n myMSApp.ActiveDesignFile.Levels
Sheet1.Cells(CurRow. 1) = myLevel.Num ber

796

I Chapter 37: Access in g Data from Externa l App lications I


Sheetl.Cells(CurRow. 2)
CurRow = CurRow + 1

myLevel . Name

Next
End Sub

When we develop in Excel, some Objects are natively available to us


without any effort on our part simply because we are in Excel. We don't
have to declare them or instantiate them, we just use them. In the above
example, "Sheetl" is one of these object variables. We declare a variable
for the Level and for the Current Row but we don't need to declare a
variable for "Sheetl".
Here are the
results of
running
"XLMSA":

The Level number and Name are placed into Excel's "Sheetl".
N ow, instead of reading Levels from MicroStation's ActiveDesignFile we
are going to create Levels based on what is in Excel. Let's create a new
design file in MicroStation before we continue.
A

When we run XLMSB with the above data in Excel, we get new Levels
created in the new .dgn file. Here is the code:
Sub XLMSB ()
Dim myMSAppCon As MicroStationDGN.ApplicationObjectConnector
Dim myMSApp As MicroStationDGN.App l i cation
Di m myLevel As Level
Dim CurRow As Long
Set myMSAppCon = GetObject( .
"MicroStationDGN . ApplicationObjectConnector " )
Set myMSApp = myMSAppCon.Application

I Controlling MicroStation from within Excel I

797

Cu rRow = 2
While Sh eet l .Ce lls(C urRow. 1) <> n n
Set myLevel = myMSApp.ActiveDesignFile.AddNewLevel _
(Sheetl.Cel;s(C urRow. 2))
myLevel.Number = Sheetl . Cells(CurRow . 1)
my Level .Descriptio~ = Sheetl.Cells(Cu r Row. 3)
CurRow = CurRow + 1
Wend
End Sub

After the above code is run, we can see the results in the Level Manager:
I

6-

Name
Default
Existing Terr ain
Building SiteTerrain
ExistingTerrainM esh
Frame
Buil " iteMesh

,;;;

, Number !

0
1

2
3

Description

File

Logical

Existing Terr ain Level


Building Site Terrain Level
ExistingTerrainM esh Level
Frame Level
BuildingSiteMesh Level

Chapter 17_""
Chapter3?_""
Chapter3?_""
Chapter3?_ ""
Chapter3?_"
Chapter3?_ "

Master
Master
Master
Master
Master
Master

EJ
Do
Do
Do
Do
Do
Do

Running Excel Macros


After we develop code in Excel's VBA environment, we want to give
users the ability to use our macros but not to modify our code. Excel's
VBA environment (as with MicroStation's VBA environment) allows us
to 'lock down' our projects by going to the Project Properties Protection
tab and selecting "Lock project for viewing': But how do we execute
macros in Excel when the project is 'locked down' or if we do not want
our users entering the VBA environment to execute macros? Let's begin
by creating a custom button in Excel.

I Chapter 37: Accessing Data from External Applications I

798

~ta.

:tlindow

Standard

'"

Formatting

;.,:. r' - -

L
,rr

tielp

Borders
Chart

r[f~~i~~;~j L .commands II

Control Toolbox

, Toolbl's:

External Data

--

,h

DBorders
i DChart
1 D Chart Menu Bar
. D Circular Reference
D Compare Side by Side
D Control Toolbox
DOiagram
DOrawing
DOrawing Canvas
D Exit Design Mode
D External Data
DForms
D Formula Auditing

Forms
Formula Auditing
List

....

Picture

....

PivotTable

.~--

Protection

- Iri
,,-~

...

1'---"

._-

Reviewing
Task Pane

Watch Window

.- ..

t,

P.endm~.:.~.:..,... J

~eset ...

Attactl..

dose

Visual Basic

.....

A~

Text To Speech

......

-.-

Qptiona _ _ _ _ _ _ _ _ __

Standard
I ~
i 0 Formatting
i D 3-0 Settings

Drawing

0.e

In Excel, right-click on any Toolbar button and click Customize.


The Customize dialog box opens.

Web
WordArt

"'-"

-_..

.---

S;;ustomize. .. I\.

'\\

Click the New button to begin


the steps to create a new toolbar
in Microsoft Excel.
Name it "MicroStation VBA"
and click 0 K.

I oolbar name:

.~oStation VBA

Ii

OK

iJ I

Cancel

And now for a warning: the new toolbar is very small because it
does not have any buttons or menu items in it.

4 N ext, change to the Commands tab and select "Macros" in the


Categories Listbox. This brings up two 'Commands' - "Custom
Menu Item" and "Custom Button".

I Contro lli ng MicroStation from wi thin Exce l I


5

799

Drag and drop "Custom Menu Item" into the new toolb ar we just
created.
0~1~~J ~ommands 1itio~ L._.______. __ ._. __

II Tocommand
add a command to a toolbar: select a category and drag the
out of this dialog box to a toolbar,
I ~.:
! Dwindow and Help,:,:

I A~~~~~~pes

1"~ ~;:~ ;i:~gTiOilbiOX..

I !'pGlult

co
rr=m=m=an=Q.=s:= = = = = = = . " . 1, C lC
Iio
tom ~lenu Item
'

~m Button

I
......_.'il"I

Builtin Menus

New~lenu

fYlodify 561ectiorJT

.~._..

_ _

_ _ _

__ _ _ _ _ _ . . ... ....

[B,earrange Commands. "

Close

There is a temptation at this point to click the Close button because


we have successfully dragged a new Menu Item to our new Toolbar.
But key to proceeding now is to keep the Customize dialog box
open.
6

Next, right-click on the new Menu Item and take a look at the
context menu.

~opy

Button Image

i:aste Button Image


Re~et

Button Image

g,dit Button Image ...


Change !l,utton Image
Def a\llt Style

Text Qnly (in /~enus)

Assign !:!yperlink
Assign Macro ...

800

I Chapter 37: Accessing Data from Externa l Applications I


7 The Name property specifies what the menu item will display. At
this point it is set to "Custom Menu Item". Let's change it to "&Get
DesignFile Levels':

8 Another right-click on the menu item allows us to "Assign Macro".


Macro name:

Mcros in:

iAll Open Workbooks

Description -

9 We can select a macro and click the OK button. This assigns the
selected macro to the button. From this point on, any time the
button is clicked, the macro XLMSA is run in Excel.
Now that we have renamed and assigned the Menu Item we can run
it after we close the Customize dialog box.
10 The macro we are going to run populates cells in Sheetl. Before we
proceed, let's delete the data in Sheetl. After we do this we can click
the Get DesignFile Levels button just to make sure everything works
correctly.

11 Now we are going to save the Excel file and close it. After the
workbook is closed we will begin a new workbook. Then we will
click the Get DesignFile Levels button again. What happens next
depends on your Security settings in Excel.
We may see
someth ing
like this:

"C: \Microstation

VSA \Bookl ,xis" contains macros.

Macros may contain viruses, It is usually safe to disable macros, but if the
macros are legitimate, you might lose some functionality,

[:,,:ij~:~~(~0~si;;:s.,::,:JI

........,;:,.!;.n_ab_leM_a_cro_s "'i~

L----=t1o_re_In_fo_-'

I Controlling MicroStation from with in Excel I

801

When we assigned a macro to the new menu item, it was assigned to a


macro in a specific file. That link to the file carries over even after the
workbook containing the macro is closed. If Book 1.xls is closed and we
go back to the Assign Macro dialog, we see the following:
~

Assign Macro

~l cros

in:

Ii All Open Workbooks

Description ....

So, even if we close the Excel file in which the macro is written, when we
click the button that links to the macro, Excel opens the workbook so it
can run the macro. Ifwe 'Enable Macros' (if we are prompted to do so),
the macro is run and the Levels are entered. But where are they entered?
They are entered into the Book 1.xls file even though we may have had a
different workbook open when we clicked the button. Let's take another
look at the code that is running:
Sub XLMS A()
Dim myMSAppCon As MicroStationDGN . ApplicationObjectConnector
Dim myMSApp As MicroStationDGN.Application
Dim my Level As Level
Dim CurRow As Long
Set myMSAppCon = GetObject( ,
"Mi croStationDGN . ApplicationObjectConnector " )
Set myMSApp = myMSAppCon.Application
CurRow = 2
For Each myLeve l In myMSApp.ActiveDesignFile.Levels
Sheetl.Cells(CurRow,l)
myLevel . Number
Sheetl.Cells(CurRow, 2) = myLevel . Name
CurRow = CurRow + 1
Next
End Sub

802

I Chapter 37: Accessing Data from External Applications I


So, why are the Levels being written to Book 7.xls instead of in the new
Workbook? Because we are writing to "Sheetl" directly and this object is
pre-defined as belonging to the workbook in which the code is written.
Let's copy and paste the macro XLMSA and rename it to XLMSC. Now we
will make a couple of changes to it so the Levels are written to the
workbook that is open when the menu item is selected:
Sub XLMSC C)
Dim myMSAppCon As MicroStationDGN.ApplicationObjectConnector
Dim my MSApp As MicroStation DGN .Application
Dim my Level As Level
Dim CurRow As Long
Set myMSAppCon = GetObjectC.
"Mi croSta t ionDGN.ApplicationObjectConnector " )
Set myMSApp = myMSAppCon.Application
CurRow = 2
For Each myLevel In myMSApp.ActiveDesignFile.Levels
ActiveSheet.CellsCCurRow. 1)
myLevel.Number
ActiveSheet. CellsCCurRow. 2) = myLevel.Name
CurRow = CurRow + 1
Next
End Sub

The changes are small but the significance of the changes are not. Let's
now assign XLMSC as the macro to run when the menu item is selected.
After we do so, the workbook Book 7.xls is still opened when the menu
item is selected but the Levels are placed into the worksheet that was
active when the menu item was clicked. So, the code is working better
but to have our Workbook open inside Excel each time someone clicks
the button in Excel can be improved.
It makes perfect sense that prior to running a macro, the file in which

the macro resides should be opened. But when we click a menu item we
expect code to run, not workbooks to open and dialog boxes to be
shown asking us to enable macros. So, what is to be done?

I Controlling MicroStation from within Excel I

803

1 Let's open Book 7.xls in Excel. Then save the file Book 7.xls as an
Excel Add-in. Let's use the filename MicroStationVBA Add-In.xla.

Save tn:

...~

La

~ly Recent
Documents

trr~:il

J1

'." .':.\

Desktop

[.;.I.

LQ!licrostation VBA

Too!s

Illbcd material

!l!b docs

iifC:)Documents

!il,:",", Fonts

Iif$ fr om mark
I i!O PlcS

II~
~~~~e
ia

My Documents

'i

r~y Computer

'

My Network

I rl

Places

1!!bBatchProcessing

Code

VB6

i
I

,~Fi;le~na~m~e~:~~~~@W~
l"" . ~*~~~j~m~i'
... ..... ~$~~a~mm~m~~~~
....... ..... ............

! save as

~ype:

Office Excel AddIn (*.xla)

Saving a Workbook (.xls file) as an Add-In (.xla file) performs a sort


of "SaveAs Copy" in that the original .xls file remains open. So, after
we save the file as, we can close down the original Book 7.xls file and
begin a new workbook.

Next go to the Excel menu and select Tools> Add-Ins.

addIns available:

ro.mmMllli . r-. ,.. ~..

!O Analysis ToolPak - VBA


10 Conditional Sum Wizard

iOEuro Currency Tools

I.

OKi
Cancel

!O Getpricing
!O Internet Assistant VBA

iO Lookup Wizard

iO Solver Addin

Analysis ToolPak
Provides functions and interfaces for financial and
scientific data analysis

I.

804

I Chapter 37: Accessing Data from External Applications I


4

Let's browse to the new .:xla file we just created.

Look in:

I~

l;W
My Recent
Documents

rr-;:~
~

Desktop

i'<v~).

t{::!J

IrQI Microstation VBA


rQlBatchProcessing
rQlcd material
rQI docs
rQlDocuments

II

IrQlFonts
IrQl from mark
rQlpics
jrQI Source Code
,'rQl USGS
IOVB6
Mi-:roStationV6P,?Md-In,xla

My Documen~s

jlID

My 'Computer

'I

After selecting the file, click OK


and the file displays in the AddIns dialog and is selected. This
loads the file into memory (we
can work with the file in VBA
but not in the Excel Worksheet
interface) so when the menu
item is clicked, the code is
already loaded and ready to
run.

Analysis ToolPak

o Analysis ToolPak - VBA


o Conditional Sum Wizard
o Euro Currency Tools
o Getpricing
, 0 Internet Assistant VBA
o Lookup Wizard
~1'tiM.m~5!i
Solver Add-in

~rowse",

'P'

To finish the Add -In process,


click OK in the Add-Ins dialog
box.

Now, the Menu Item we added to our custom toolbar is still pointing
to Bookl.xls. We want it to run the macro in the Add-In file we just
created. Let's go back to the Assign Macro dialog by right-clicking
on the Menu Item, selecting Customize, right-clicking on the Menu
Item again, and selecting Assign Macro.

I Review I

805

8 We don't want to run


the macro in
Bookl .xls . Wewantto
run the macro in the
Add-In. But macros
in Add-Ins don't
show up in the list of
available macros, so
just type the macro
name into the Assign
Macro dialog and
click OK.

~'ac ro

name :

i~----~--~ ~-i ~

L._.______
~'9.cros

in:

A _ _ . _ . _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

I
I
I
_1

E,j:t
r'2Cyj",

Open Workbooks

Description

9 After clicking OK in the Assign Macro dialog, click the Close button
in the Customize dialog box and we can test the Menu Item.
When we click the Get DesignFile Levels button now, the macro is
executed immediately because the Add-In is already loaded. If we
change from "Sheetl" to "Sheet2" and click the menu item again, the
Levels are displayed in "Sheet2". Remember, in our code, we are writing
to the ''ActiveSheet'' now so the active sheet in Excel will receive the
Level information.
When we created the Menu Item, we named it "&Get DesignFile Levels".
The Ampersand (&) character specifies which character will be
underlined in the Menu Item. The underlined character means we can
hold down the <Alt> key and press the <G> key (the underlined
character) on the keyboard and the menu item will be 'clicked'. This
means that in addition to clicking the menu item in Excel, we can
perform an <Alt+G> on the keyboard and the macro will run.

REVIEW
Nearly all of the code that has been written to this point in this book can
be run from within Excel's or MicroStation's VBA environment. When
we develop in Excel, we add a Reference to the "Bentley MicroStation
DGN #.# Object Library". When we develop in MicroStation and wish to
work with Excel, we add a Reference to "Microsoft Excel #.# Object
Library". We use GetObj ect to get existing instances of the application we
wish to attach to and CreateObject or New if we want to create a new
instance of the application with which we want to work.

806

I Chapter 37: Accessing Data from External Applications I

38

Writing VB6
Applications
We have written a lot of code in VBA. Now it's time to write some code
in Visual Basic 6.
In this chapter:
[B

Differences between VBA and VB6

[B

VB6 Project Basics

[B

Controlling MicroStation with VB6

[B

Compiling and Distributing Applications

DIFFERENCES BETWEEN

VBA AND VB6

VBA and VB6 have a great deal in common. Of course, they both make
use of the language, "Visual Basic". Projects are broken out into Code
Modules, User Forms, and Class Modules. Each environment allows
References to be added to the project. There are also some differences.
[B

In VBA, Code Modules, Classes, and User Forms are all


contained in a single .mvba file. In VB6, Code Modules, Classes,
and User Forms each have their own file (.bas, .ds, and .frm

807

808

I Chapter 38: Writing VB6 Applications I


files) and a single Project file (.vbp) which brings them all
together.
[B

MicroStation VBA projects must be run from within


MicroStation's VBA environment. Excel's VBA projects must be
run from within the Excel VBA environment. VB6 projects are
compiled into executable programs (.exe) and can be run
independent of any other application. VB6 projects can also be
compiled into DLL files and ActiveX Controls (.ocx files) .

[B

Since VBA Projects are self-contained, ch anges made to Code


Modules, Classes, and User Forms in a VBA project remain in
the project. Multiple VB6 projects can utilize the same Code
Module, Class Module, or User Form file. So changes made to
VB6 resources may be reflected in multiple VB6 projects. This
is a powerful feature but be careful that changes made to a
resource file (.bas, .ds, .frm) in one project do not negatively
impact any other project.

[B

A VBA project can be distributed by providing a single .mvba


file. VB6 projects compile to a single file (.exe, .dll, .ocx) but are
best distributed using a Setup program that installs DLLs and
other resources (such as the Visual Basic Runtime Libraries)
that may be needed for the program to run correctly.

[B

Since VBA programs must be run from within the host


application, we know the host application is on the user's
computer. Since VB6 compiled programs can run independent
of any host application, it is possible that someone could install
a program designed for MicroStation on a computer without
MicroStation installed.

[B

Even after VBA programs are distributed, it is easy to debug


them because debugging can take place and source code can be
stepped through on the user's machine (as long as the code is
not 'Locked'). After a VB6 program is compiled, the code
cannot be stepped through or viewed on the user's machine.

[B

The VBA environment is installed with applications such as


MicroStation and Microsoft Excel. V B6 must be purchased and
then installed to use it.

IVB6 Project Structure I

809

VB6 PROJECT STRUCTURE


We have already identified the fact that VB6 Projects are similar to VBA
projects in that they make use of Code Modules, Class Modules, and
User Forms.
When we begin a new VB6 Project, there are a number of different types
of VB6 projects we can create. We will begin by creating a "Standard
EXE':

New

IExisting I Recent I

Wnw

'0,'.

~.

VB Wizard
~tanager
,.",

'0,'.

.9;i

l?r

~ .
W

ActiveX EXE

ActiveX DLL

ActiveX
Control

VB Application
Wizard

Addin

Data Project

ActiveX
Activex
Document DII Document Exe

.....

,.",

"'.

open~
Cancel
Help

Don't show this dialog in the future

810

I Chapter 38: Writing VB6 Applications I


When we begin a new VBA project, a Code Module is created by
default. When we begin a new "Standard EXE" project in VB6, a User
Form named "Forml " is created by default.
EJle !;.dit Iiew !:roject FQ!'mat Qebug Run Q>tery Diagram Iools 8.dd-Ins 'tindow t!elp

Alphabeti~

, , , , , . ' : : : : : : : : : : : : : : :: 1""""",..- --

::::J

jForm! Form

lcategorized!

~~~:~~~~c~: "~Ji~ri~- ~:~'_

AutoRedraw

['~J ....J P"


.

... . .,.'"

-,

1=l1J.

"

.: : : :. :::: :<
.........-' '-''- "....
' ' "",
' '- '- ' ....
' ' -'

Eel.. -'-'"" .

c; g'E:l

False

'&H
"-8'O:'O':'O:-O:O:~O- 1

~ 83 .' .'>' ...~

. . . . .. , . ., '
.

-11

[G A f85T

' ~'': """"",--1

f(9 , '-

'.zl

@j

~~

::11
The IDE of VB6 is nearly identical to that of VBA. Here, we can see the
Proj ect, Properties, Toolbox, and User Form.
Let's take a couple of controls
from the Toolbox and place
them on the Form. We will
place a ComboBox, a Label and
a CommandButton on the
Form.

level

At this point, we do not need to


do anything to the project. We
are simply going to compile the
program by going to the VB6
menu and select File> Make Projectl.exe.

.. -. .... ... " , .. ., "


:

Execute

I VB6 Project Structure I

811

The Make Project dialog box displays and here we specify a file location
and a file name.

Save jn: \C) VB 98

r------------c~-----------------------_,

liD ~lSXM Addln


t:! XlsXml
it:! RainyDayCode Addin L:l C2. EXE
!IDSetup
L:lCVPACK.EXE
IbsummarYlnformation LtL.i~<~~~:
ie::)Templa t e i : J . V86.EXE
it:! Wizards
t:QVISDATA.EXE

File name:

IP,oiect1 .exe

~
Cancel I
Help
Qptions...

The file Project7 .exe is created and it can be executed whether


MicroStation is running or not, or whether MicroStation is installed or
not.

Execute

When we run our new program,


we see the Form we created. It
can be Maximized or Minimized.
It can be re -sized to any size.
There are Form Properties that
control the use of these Form
sizing functions but by default,
Forms are inserted into a proj ect
with the ability to Minimize,
Maximize, and Resize.

When we save our project, we are asked to supply the names and
locations for two files: Form l.frm and Project 1.vbp. This differs from
VBA where we are only asked to supply the name and location of a
single .mvba file.
Just to review, we have created three distinct files at this time. We
created a .frm file (User Form), a .vbp file (VB6 Project File) and an .exe
file (Compiled Executable). When we are ready to distribute our
application to others, of these three files , the only file we need to
distribute is the .exe file.

I Chapter 38: Writin g VB6 Applications I


Let's 'Remove' the current project we are working on and create a new
project in VB6. We remove projects by either shutting down VB6
entirely or by going to the File menu in VB6 and selecting File> Remove
Project.

When we create a new project, a new


User Form is created for us again. We
are not going to use this new Form so
we will remove it by going to the
Project Window and right-clicking
on the Form, then selecting Remove

E.ile !;.dit \!Jew I:roject FQrmat ~ebug Bun

Ij IirH;;bI~I ,)!, ~_~~I"


f3

Project - Projectl

Project! (Projectl)

["I e::J Forms

Forml .

. El l'1l!p-!II!!
.I!II
. ,,1m""""',",
. I!!I
.. in
..

E!!il

,<L .. '::'w~'"

ViewOWect

1m View CQde

Iformi Form

"

'

'

'

~ ~ Prope[ties
_

After we remove the new default


Form, we can see that we now have a
project named Projectl" with
nothing in it. Let's add the Form from
the previous project by going to the
menu and selecting Project > Add

812

____'_H' _____ H" .. _ .

2ave Forml
Save Forml 8.5".

RMU(OKe(Jraw ...... ~ !:rint " ,

File.

- - - - _......_-"--_.

r;-

Doc!!,;ble
tlide
Component" ,

Look in:

Iell VB 6

File name:

IForm1.frm

Qpen~

Files of lYpe:

IVB Files [". frm;".ctl;". pag;".dsr;". bas;",cls;". re,3

Cancel

J:!elp

Add As Related Document

When we select the Form we saved previously and click Open, it is


added to the Project. Double-clicking on the Form in the Forms" folder
opens it so we can edit it.

I VB6 Project Structure I

813

Remember, we are not adding


the previous Project, only the
Form (a part of the previous
Project).

Level . . Combo 1
Cell

I.-Co-m-bo-2----------,

Execllle

I:: :

_ _ _ _--.l.

Let's add another Combobox


and Label to the Form and
change the size of the Form.

The first Project we created was named "Project!': This is the name
VB 6 gave it. We want to name this one "Project2". We do this by going
to the VB6 menu and selecting Project> Projectl Properties.

Generall
Startup Object:

IStanchrd cXe
Project Name:
IProject!

~ lii1&mii=iiliiM~
Project Help
Context ID:

Help File Name:

fO

Project Description:

OK

Cancel

Help

Let's talk about this dialog box. We can see the Project Name is being
shown as "Projectl". Let's change this to "Project2".
The Startup Object selection is critical. At this point the setting is "Sub
Main". This means when we run this program either at design time or
runtime, it looks for a procedure named "Main" in a Code Module. For
our example here, we don't want to start with "Sub Main" because we
don't have a "Sub Main". We want to select "Forml" from the list. The
Startup Object ComboBox contains the names of all User Forms that
can be used as Startup Objects. If we had Forms named "Form2" and
"Form3" in this Project they would show up in this list as well.

814

I Chapter 38: Writing VB6 Applications I


Now that these changes have been made, the Project Properties dialog
box should look like this:

General

Project Type:

tStaf1d~~rd EXE

2):artup Object:

-=:J lMSH,!

Project t:/ame:

1Project2

-,::::J

Project Help
Context [D:

t!elp File Name:

10

Eroject Description:

Unatt~nd.~d Execution

P'

U;:-gr,;rJ~ A,liv~X CorM.)!,;

r
r

Require !:,icense Y.6Y

Threading Model "- '-'

Thtead per Cble:;:f:

r.

Tnrea:j PQ.:JI

~ threads

Retained In ~hJmor)l,

~at1cel

Help

When we click OK we can now save our Project. When we click the Save
button, we are asked to name and specify a save location for the Project
file.

Save in:

Ib

V86

i~ Projectl, vbp

File name:

jli1lll1llilliiJ

Save as type:

IProiect Files [", vbp)

Save
Cancel

Help

The name of the .vbp file defaults to the Project Name set in the Project
Properties dialog box. In this case, we will accept the default name of
Project2. vbp. We are not asked for the location or name of the Form
because it had already been saved in our previous Project. The .frm file
is saved with the new controls on it and with its new size properties.
Let's remove Project2 by going to the VB menu and selecting File>
Remove Project2. Now, we will open Projectl again.

I VB6 Project Structure I

815

When we open "Proj ectl" again and look at "Form I " it reflects the
changes made when it was being used in "Project2". This 'linking' of
program design elements into projects is powerful. But as with most
things that are powerful, we should be careful so we don't abuse it either
intentionally or unintentionally. If, for example, we forget that FormI is
being used in two projects, we may make modifications to it in
"Project2" that causes it to stop working properly in "Projectl ".
Now, we compiled Projectl before we began Project2. We made changes
to FormI when we were in Project2. What happens when we execute
Projectl.exe? Do we get the new Form or do we get things as they were
when we compiled Projectl? Answer: when we compile a program,
everything is compiled into the executable as they are at the time the
project is compiled. The executable program does not change to reflect
modifications made to its design elements. If, however, we open and recompile Projectl, the new executable (.exe) reflects the changes that
were made up to the point that we re-compiled the project.
This exercise teaches us a few very important principles.
(E

Multiple VB Projects (.vbp files) can make use of the same


resource file (Jrm, .bas, .ds, etc.) .

(E

Compiled .exe files are static. Changes made to design elements


compiled into .exe files are not reflected in the .exe file unless it
is re-compiled.

(E

By default, compiling an .exe file does not result in saving


source Jrm, .bas, or .ds files. It is possible to create an entire
program using VB6 and compile it without saving any of the
source files to disk. Although potentially dangerous, this can be
useful when we want to make a fairly significant change 'just to
see what happens' and we don't want to lose our 'working' code.
We can make changes, run it in design mode or compile it, and
'see what happens'. Only after we know the code works properly
do we save our project. On the other hand, if we want VB6 to
automatically save our project each time we compile our
project, we can change a Tools> Options> Environment Tab>
When a program starts setting.

Thus far we have created two "Standard EXE" projects. There are two
other note-worthy projects and they are:

816

I Chapter 38: Writin g VB6 Applications I


[B ActiveX DLL -

Allows us to create a 'program' that can NOT


be executed by itself but other programs can 'connect' to it. An
example of an ActiveX DLL would be the "Microsoft ActiveX
Data Objects Library". We cannot 'execute' it so it runs as an
independent program but we can 'connect' to it to allow easy
access of databases.

[B ActiveX Control- Allows us to create our own 'Controls' that

show up in the Toolbox in VBA and VB6. An example of an


ActiveX Control is a ComboBox that automatically displays the
list of Levels in a .dgn file. We would begin with the basic
ComboBox and would write code to populate it. Once
completed and compiled (to an .ocx file) we would be able to
drop it onto a Form in MicroStation's VBA environment and it
would automatically populate with Levels.

CONTROLLING MICROSTATION WITH

VB6

We have created a couple of VB6 projects already. We have placed a few


controls on a Form and have compiled one of the projects. But the
application "Projectl", even though compiled, does nothing for us. We
can click on the ComboBox or on the Button but nothing happens. We
can Minimize, Maximize, and Resize the Form but this does not really
do anything for us. Let's look into the steps necessary for us to begin
controlling MicroStation from our application "Projectl".
The first step to help us to communicate with MicroStation is to add a
Reference. In VBA we do this with the menu items Tools> References.
In VB6 we do this with the menu items Project> References.
The list that appears will likely differ from computer to computer
because each computer has different software installed on it. The list is

I Controll ing MicroStation with VB61

817

in alphabetical order and we want to browse for "Bentley MicroStation


DGN # .# Object Library".
Available References:

OK
Cancel

Browse ...

Priority

Help

Li
r- Bentley MicroStation DGN 8. 9 Object Library .- .......--..- ..- ..- ----..--.--.-- ... -... ..-.......

Location:

C:\Program Files\BentleY\MicroStation\ustation.exe

Lan~~~g~~_=t.~~~~~ _._....___. ____.. _.

_ ..___ . _ .. ____ ...

Once selected we can work with MicroStation much easier than we


could before it was selected.
Now, we want to populate the ComboBoxes in our Form before it is
displayed. We will write some code in the "Form Load" event (in VBA
we would use the Form Initialize Event).
Now for the code in the Form Load event:
Pri vate Sub Fo rm_L oa d()
Di m my MStati on C As Mi cro St at i on DGN. Ap pli cat i onO bje ct Connecto r
Dim myMStation As MicroStationDGN . Applicati on
Dim myLevel As Mi croStat i onDGN . Level
Dim my Cel l As Mi c r 0 Stat ion 0GN. Cel l In for mat i on
Di m myCell InfoEnum As MicroStationDGN . Cel l lnformatio nEnumerator

Set myMStationC = GetObject( , _


"MicroStationDGN . Appl i cationObjectConnector " )
Set my MStation = myMStat i onC . Application
' Popul ate Level s
For Each myLeve l In myMStat i on . ActiveDesignFile . Levels
Combol . AddItem myLevel . Name
Next
' Populate Cells
If myMStation . IsCellLibraryAttached = True Then
Set my Cell In foE num = _
myMStation.GetCellInformationEnumerator(True, True)

818

I Chapter 38: Writing VB6 Applications I


While myCe11 InfoEnum . MoveNext
Set my Cell = my Cell In f 0 En um. Cur r e nt
Combo2. Add Item myCe 11 . Name
myCe 11 I nfoEnum. MoveNext
Wend
End If
IFrame
Level
End Sub
Cell

1_liimMi\il.riMiiiIiI1~
Execute

Let's review the code now. We know we want to connect to


MicroStation. We do this by using GetObj ect and use the
MicroStationDGN.ApplicationObjectConnector as the Class we
'Get: Then we get the Application Object from the
ApplicationObjectConnector. Once we have the Application of the
ApplicationObjectConnector, we are able to work with MicroStation in
the same way we do in VBA.
We place the name of each Level in the ActiveDesignFile into "Combo!".
If a CellLibrary is attached, we add the name of each Cell in the Library
to "Comb02".
And what happens when the user clicks the Button?
Private
Dim
Dim
Dim
Dim
Set

Sub Command l _C1 i ck()


myMStationC As MicroStat i onDGN . App 1icationObjectConnector
myMStation As MicroStationDGN.App1ication
myCe11 As Ce11E1ement
Ce11InsPt As Point3d
my MStat ion C = Get 0 bj e c t (. _
"MicroStationDGN.App1icationObjectCo nnec tor")
Set myMStation = myMStationC.App1ication
Set myCe11 = myMStation.CreateCe11E1ement3(Combo2 .T ext. _
Ce11InsPt. True)
myCe11 .Leve1 = myMStation.ActiveDesignFi1e.Leve1s(Combol.Text)
myMStation.ActiveMode1Reference . AddE1ement myCe11
End Sub

When the CommandButton is clicked, we insert the selected cell at (0, 0,


0) on the selected level.

I Controlling MicroStation with VB61

819

Each time we use GetObj ect, it takes time to connect to MicroStation.


Let's make a few modifications to our code so we don't need to connect
to the MicroStationDGN.ApplicationObjectConnector every time we
click the button.
'G eneral Declarations Area of Form
Dim myMStat i onC As MicroStat i onDGN . App li cationObjectConnector
Dim myMSta t ion As Mi croStationDG N.App l icatio n
Private Sub Commandl _Cl i ck()
Di m myCe ll As Ce ll El ement
Di m Ce llI nsPt As Po i nt3d
Set myCe ll = myMStat i on . CreateCe l lE l ement3(Combo2 . Text,
Cel l InsPt, True)
myCell . Level = myMStation.ActiveDesignFile . Levels ( Combo l . Text )
myMStat ion.A ct iveM odelReference.AddElement myCell
End Sub
Private Sub Form_Load()
Di m myLevel As MicroStationDGN.Level
Dim myCel l As MicroStationDG N.CellInformation
Dim myCell InfoEnum As _
MicroStationDGN . CellInformationEnumerator
Set myMStationC = GetObject(, _
"MicroStationDGN.Applicat i onObjectConnector")
Set myMStation = myMStationC.Application
' Po pul ate Level s
For Each my Level In myMStati on. ActiveDesignFile. Level s
Combol.AddItem myLeve l . Name
Next
' Populate Cells
If myMStation . IsCellLibraryAttached = True Then
Set myCel ll nfoEnum =_
my MStat i on . GetCe l l I nformationEnumerator(True , True)
Whi 1e myCe 11 I nfoEnum. MoveNext
Set myCe l l = myCel l Inf oEnum . Current
Combo2 . AddItem myCell . Na me
my Cel l I nfoE num. M0 ve Next
Wend
End If
End Sub

820

I Chapter 38: Writing VB6 Applications I


By declaring the variables myMStationC and myMStation in the
General Declarations area of the Form, these variables are accessible in
other areas of the Form such as the CommandButton Click Event as
long as they are initially Set in the Form Load event.
We can perform a test run of our Application while still within VB6.
Clicking the Start button in the VB6 toolbar, using the VB6 menu Run>
Start, or pressing the <FS> key executes the program inside VB6. We can
also step through our code by using the <F8> key repeatedly just as we
do in VBA.
Let's compile our program now. File> Make Projectl .exe compiles our
proj ect into the file Projectl.exe. Once compiled, we can execute the
program by double-clicking on it in Windows Explorer or adding a
Shortcut on the Windows Desktop.
Our first VB6 Project was pretty simple. Let's try building a new project
that makes use of some of the Controls not available in the standard
VBA toolbox. We will name this project "Project3".
Here is what the Form looks like when it is executed:

6:l Active>< Control Pad

L.:J BatchProcessing

I2J cd material
CJ docs
EJ Documents
EJ Fonts

CJ from mark

ILl pies
EJ Source Code
EJ USG S
EJ VB6

filel.d
filel 0.dgn
file2.dgn
file3.dgn
file4.dgn
fil e5dgn
file6.dgn
file7.dgn
file8.dgn
file9.dgn
filea.dgn
fileb.dgn
rasterdocs.dgn

Levels in Selected File

Levell
Level 2
Level 3
Level 4
I Level 5
Level6
Level 7
Level 8

,
I

.LeveI9
Level 10

jLeVelll
Level 12

.--~

I Controlling MicroStation with VB61

821

When we select a drive and a folder, the DGN files


in the selected folder display in the File list. When
a file is selected, the Levels in the file are shown in
the Listbox. The Listbox is available in VBA. The
Drive Listbox, Directory Listbox, and File Listbox
are not.
We can see the Drive, Directory, and File Listboxes
in the Toolbox. These are three of the Controls that
are available to us in VB6 but are not available in
the Standard VBA Toolbox.

iiiffil _ _......
1-_

Let's take a look at the code now that we know what the program is
going to do:
'General Declarations Area
Dim myMStationC As MicroStationDGN.ApplicationObjectConnector
Dim myMStation As MicroStationDGN.Application
Private Sub Form_Load()
Set myMStationC = GetObject(.
"MicroStat i onDGN.ApplicationObjectConnector " )
Set myMStation = myMStationC.Application
End Sub
Private Sub Drivel_Change()
Dirl.Path = Drive l .Drive
Listl.Clear
End Sub
Pri vate Sub Di rLChange()
Filel.Path = Dirl.Path
Listl.Clear
End Sub
Private
Dim
Dim
Set

Sub FileLClick()
myLevel As MicroStationDGN. Level
myDF As MicroStationDGN.DesignFile
myDF = myMStation.OpenDesignFileForProgram(
Dirl.Path & " \ " & Filel.FileName, True)
Listl.Clear
For Each myLevel In myDF.Levels

822

I Chapter 38: Writi ng VB6 Applications I


Listl . AddItem myLevel.Name
Next
myDF.Close
End Sub

When the Form is Loaded, we attach to MicroStation. When the Drive


changes, we update the Directory Listbox to reflect the change. When
the Directory Listbox changes, we update the File Listbox to reflect the
change. When the user clicks on a .dgn file in the File Listbox, we open it
'ForProgram' and add its Levels into the Listbox. Opening a file by using
Open Des i gnFi 1eForProgram opens the file in memory without opening it
in MicroStation's editor. Since we are not going to be making any
changes to the .dgn file, we open it as ReadOnly.
Let's name the Form "Form3" and the Project "Project3". After saving
the Project, we can compile it and run it.

Creating an ActiveX Control in VB6


VBA gives us a number of standard Controls. Listbox, Textbox,
Combobox, Checkbox, CommandButton. When we work with
MicroStation, we may see user GUI controls we wish we had in VBA.
One of the benefits ofVB6 is that we can create our own controls in VB6
and then use them in VBA. An entire book could be written on this
topic alone (actually, entire books have been written on this topic). So,
we will create a simple control that can be used in VB6 or in VBA. This
control will display the Models and Levels in the Active Design File.
This will be done with a Tree View control.
Here are the steps to creating this control:

I Controlling MicroStation with VB61


1

823

Create a new '~ctiveX Control" project in VB6.

New

Existing I Recent I

Standard EXE

ActiveX EXE

ActiveX DLL

[II

""~

""

'3\"

~.

VB Wizard
~lanager

17

ActiveX
Activex
Document DII Document Exe

""~

....

fj:'
VB Application
Wizard

Addin

Data Project

v.

~
Open

Cancel

Help

Don't show this dialog in the future

2 Change the Name of the

~~m~m'fiil_lFJ

Control to
"msvba_modeltree".

Imsvba_modeltree User Control


Alphabetic

Categorized

::;]

~~!?~.~~~deltree

~s.~.e.~~.~.~.Y.~

3 Right-click on the Toolbox,

Ali9nable

.: F~I;~

: 1 - 3D
~~p'~"r an~e __
click "Components", and
AutoRedraw
select "Microsoft Windows
Common Controls 6.0" in the list. Then click the OK button.

Controls

Designers Iinsertable Objects

~~icrosoft Windows
L~Iicrosoft Windows
Ct r'Iicrosoft Windows
[J Microsoft Windows

Common Controls 6.0 (SP6) ~ ,


Common Controls-2 5.0 (SP;
Common Controls-2 6.0 (SP,
Common Controls-3 6.0 (SP'

::J Microsoft Winsock Control 6 .0 (SP6)


':::'1 Mitctrl2 ActiveX Control module
[) Morovia Barcode ActiveX Control 3.0 Type Lib"
[} MouseWheelControl

.....

r;]

LJ ~IS Video Control 1.0 Type Library


[] ~ISFlexGrid Wizard

eJ r~sie ActiveX Control module


i..J msinfo32 1.0 Type Library
::::: msrtedit 1.0 Type Library
~,

________l

Browse".

V i

>

Selected Items Only

r /n software - IPWorks I VS File~lailer Control

Location:

-:

C:\WINDOWS\system32\filemISO.ocx

--.---.. -.-----.--.-----.------------------.--~
OK

Cancel

824

I Chapter 38: Writing VB6 Applications I


4

Add a TreeView control to the UserControl area and name it "tvl".

5 Change the "Style" property of the Tree View to "7 tvwTreelinesPlusMinusPictureText".

6 Add a Reference to "Bentley MicroStation DGN 8.9 Object Library"


in the Control Project (Project> References).
We now have the framewo rk for our n ew Control.

So, we have a Control named 'msvba_modeltree' with a Tree View


control on it named 'tvI'. What do we want this control to do?
[8

Display the Models in the Active Design File in the Tree View
under a top Node of "Models".

[8

Display the Levels in the Active Design File in the Tree View
under a top Node of "Levels':

[8

Create an Event named "SelectionChanged" when the user


clicks on a Node in the Tree View and return the Node object
that is selected.

[8

Institute a method named "GetModel" and another named


"GetLevel" that returns the Model or Level Object that is
selected in the Tree View.

[8

Allow the user (actually the programmer) to resize the control


and have the Tree View change size accordingly.

Here is all of the code in the Control:


' General Declarations Area

825

! Controlling MicroStation with VB6!


Di m myMSApp As Mi c roSt ati on DGN.Appli ca t i on
Dim myMSAppCon As Micr oS t at i on DGN. Ap pli ca ti onO bje ctConnec t or
Event SelectionChanged(SelectedNode As Node)
Pr i vate Sub UserControl_Initialize()
Di m myModel As Mi croS t at i on DGN .Model Reference
Dim myN ode As Nod e
Dim myLevel As MicroStationDGN.Level
Set myMSAppCor = GetObject(, _
"Micr oStati onDGN. Appl ic at io nO bje c t Co nnec t or" )
Set myMSApp = my MSAp pCon.Application
' Models
Set myNode = tvl . Nodes . Add( , , "tvlModels ", "Models " )
For Each myMode l I n myMSApp . Act i veDes ignF ile . Mode ls
tvl.Nodes . Add " tvlModels ", tvwChild, "mod_" & myModel.Name,_
myModel . Name

Next
myNode.Sorted = True
' Levels
Set myNode = tvl . Nodes.Add(, , "tvlLeve l s ", " Levels " )
For Each myLeve l In myMSApp.ActiveDes i gnFile.Levels
t vl.N odes . Add " tvlLevels", tvwC h ild, "lvl_" & myLevel.Name,
my Leve l .Na me

Next
myNode.Sorted
End Sub

True

Private Sub UserControl_Resize()


tvl .Wi dth = UserContro l .Width
tvl.Left * 2
tvl . Hei ght = UserContro l . He i ght - tvl . Top * 2
End Sub
Pr i vate Sub tvl_C l ic k()
RaiseEvent SelectionChanged( t vl . Selected I tem)
End Su b
Funct i on GetLeve l () As MicroStat i onDGN . Level
Dim xSplit As Variant
xSpl i t = Split ( tvl . Se l ectedltem . Fu l lPath, "\ " )
Select Case UCase ( xSp l it (O) )
Case "MO DELS "

826

I Chapter 38: Writing VB6 Applications I


Set GetLevel = Nothing
Cas e "LEVELS"
If UBound(xSpl it) > 0 Then
Set Get Level = )
myMSAp p. ActiveDesignFile . Levels(xSplit(l))
Els e
Se t Get Level = Noth i ng
End I f
End Sele ct
End Function
Fun ction Get Mode l () As MicroStat i onDG N.M odelReference
Di m xSp lit As Va r ian t
xSp l it = Sp lit (tvl. Sel ected It em .FullP ath . "\ " )
Se l ect Case UCase(xS pl it(O))
Case "MODE LS "
I f UBou nd( xSp lit ) > 0 Th en
Set Get Mo del =
myMSApp.A ct i veDe si gn Fi le .M ode l s(xSpli t (l))
El se
Se t GetM odel = Not hi ng
End If
Case "LEVEL S"
Se t Get Mode l
No thi ng
End Sele ct
En d Func ti on
Pr ivate Sub UserContro l Ter mi nate()
Set myMSApp = Noth i ng
Set myMSAppCon = Not hing
End Sub

Now, let's take a look at the Events in which we have placed this code to
make sure we are clear on what is happening.

Private Sub UserControl_lnitializeO


When the Control is Initialized, we get the MicroStation Application
Object and the Models and Levels of the ActiveDesignFile. We place the
Model and Level names in the Tree View named 'tvl '.

! Co ntrolling MicroStation with VB6!

827

Private Sub UserControl_ResizeO


When the Control is resized, we change the size of the Tree View based
on the Control's width and height and the Tree View's Left and Top
prop erties.

Private Sub tvl _ClickO


When the user clicks on a Node in the TreeView, we raise the
Se l ecti onChanged Event and provide this event with the Node th at is
selected in the TreeView. Note that the Event Select i onChanged IS
declared in the General Declaration area of the Control.

Function GetLevelO As MicroStationDGN.Level


When the Control is added to a Form in VB6 or in VBA, we can use the
GetLev e1 method of our Control and the Control returns the selected
Level or 'Nothing' if the selected node in the Tree View is not a Level.

Function GetModelO As
MicroStationDGN.ModelReference
Similar to GetLevel, GetModel returns a Model Object if one is selected
in the Tree View.

Debugging ActiveX Control Projects


We have just created the framework for a new ActiveX Control. How
can we test it to see if it works? We can compile it, and then insert it into
a new VB6 or VBA project. But that would force us to re-compile each
time we find a bug or other error. It would also keep us from stepping
through the code to aid in our development.
VB6 gives us the ability to create a Project Group. Project Groups are, as
the name suggests, a group of individual VB6 projects that are opened in
the same session of VB6. Before we create a new Group, we should save
our ActiveX Control Project. And before we save our Project, we need to
give it a name in the Project > Properties dialog. We will name it
"msvbaControls':
When we save an ActiveX Control project, we save the Project (.vbp file)
and the Control in the Project (.ctl file ). Let's save the Project and
Control with the file name "msvba_modeltree':

828

I Chapter 38: Writing VB6 Applications I


Now we are ready to add a new Project to our Control's Project, thus
creating a Project Group.

1 Select File> Add Project. (Don't click "New Project"; we need to Add
a Project.)

Select "Standard EXE".

Go to the VB6 menu and select File> Save Project Group.


We are asked for a Project Group file name.

Save the project as "msvba_modeltree".


We are also asked to give the new Project and Form a file name.

Save the new Project and Form as "testingmodeltree".


1m Ei I ~
1';1" itl

msvbaControls (msvba_mode ltree.vbp)

. to}a

User Controls

: ~ msvba:"'modeltree (msvb,Cmoqeltree .ctl)

;:':] ~ Project! (testingmodeltree.vbp)

EJ.a
L..

Forms
Form! (testingmodeltree .frm)

t:J.

When we look at the Project window now in VB6 we will see that we
have two projects loaded. When we are working in a Project Group, one
of the projects is set as the "Start Up Project". Controls cannot be
'executed' by themselves so we will set our new 'testingmodeltree'
project as the Start Up Project. We do this by right-clicking on the
Project and selecting "Set as Start Up".
Now, in the Project window,
double-click on the Control we
just created so that it displays.
N ext, we are going to close the
Control by clicking the Close
button at the top of the window.

'.

829

! Controll ing MicroStation with VB6!

Why did we do this? Because we cannot add the


Control in the new Project we created when the
Control is open. We do not need to unload the
Project, we only need to close the Control window.
~ 83.iJ~
El:IEl:!

OB..:J~
re,gjJ~

Now, when we select our new Project's Form and look


at the Toolbox we should see our Control in the list.
Clicking on our new control and dragging it into our
new Form initializes the Control.

When we run our new Project, the Form is displayed and the Tree View
is available.

Models
. Building and E"isting Mesh

Buildlng Site Elesign Mesli


, Cut and Fill Volume
Existing Terrain Mesh
Finished Br,ilding Site
Index
EO Levels
Building Site Terrain
BuildingSiteMesh
Default

Y;

At this point, we have created a new ActiveX Control and have added a
new Project that makes use of the Control. Let's add a little bit of code
now to the new Project so we can test the Event and Methods of the
Control. We also need to add a Reference to the "Bentley MicroStation
DGN Object Library".

830

I Chapter 38: Writing VB6 Applications I


Priva te Sub msvba_m odel treel_SelectionChanged( _
SelectedN ode As MSComctlL ib .N ode)
Dim myLevel As MicroStatio~DGN.Level
Dim myModel As MicroStationDGN.ModelReference
MsgBox SelectedNode.Text & vbCr & SelectedNode.FullPath
Set myLevel = msvba_modeltreel.GetLevel
If myLevel Is Nothing = False Then
MsgBox myLevel.Name & vbTab & myLevel.Number, , "LEVEL "
End If
Set myModel = msvba_modeltreel.GetModel
If myModel Is Nothing = False Then
MsgB ox myModel.Name & vbTab & myModel.Description,

. " MODEL "

En d If
End Sub

Here is the "SelectionChanged" event we created in our Control. We


display the selected node's Text property and its FullPath property. Then
we use the GetLevel and GetModel methods.

Compiling our ActiveX Control


Before we compile our ActiveX Control into an .ocx file, we must set the
ActiveX Control Project as the "Start Up" Project. After we do this we
can select File> Make msvba_modeltree.ocx from the menu in VB6.
Let's compile it into the Control Project's folder so we know where it is.
Compiling an ActiveX Control is a little different than compiling a
Standard EXE. Of course, the file extension is different, but the Control
is also "registered" on the development computer. This registration step
must be done manually or with a Setup program on any computer where
the control is to be used. We use the command "RegSvr32.exe" in the
Command Window to register the control.
In Windows, go to the Start button and click the Run shortcut. When
presented with the Run dialog box, type "cmd" and click OK.

Now, type:

831

! Contro lli ng MicroStation wi th VB6 !

regsv r 32 . exe "C: \ Mi cro Sta t i on VBA\ msvba _m ode l tree . ocx "

and press the <Enter> key. The path entered needs to point to the
location of the .ocx file. This .ocx file may be in a different folder so the
path may need to be adjusted.
When we press <Enter>, if everything was entered correctly, we will see
the following dialog box:

."i)

DIiRegisterServer in C: \MicroStation VBA\msvba_modeltree.ocx succeeded.

li::.:::::i:*:::::ij
This registration step is necessary on any computer where we want to
make use of the Control. If this registration step is not performed, the
Control will not be available.
Now that we have compiled our ActiveX Control, let's take a look at it
inside MicroStation's VBA environment.
After inserting a new Form in a MicroStation VBA Project, right-click
in the Toolbox and select Additional Controls.

Available Controls:

o MSCustomLog Control
o MSDTHostCtri Class
o MSDTHostCtrl Class
o MSDVDAdm Class
o MSFlexGridWizard.Sub\llizard
o Msie Control
o MSNCSALog Control
o MsnMusicStatusUi Class
o MSODBCLog Control
o MSRE dit Class
I8J msvbaConl161s 'msvba":rnodeltre.e ~~
o M5\11 ebDVD Class

Show

o Selected Items Only

msvbaControls.msvba_modeltree
Location

C:\MicroStation VBA\msvba_modeltree.ocx

Our new Control should show up in the list. When selected, it displays
in the Toolbox inside VBA.
Drag and drop the Control on to the Form and size it. Then press the
<FS> button to Run the form.

832

I Chapter 38: Writin g VB6 Applications I


A MessageBox may display warning us of the use of this ActiveX
Control. After getting past this dialog box, the Form is displayed and the
Control is populated with the Models and Levels in the Active Model
Reference.
Creating ActiveX Controls is fairly simple and straight forward. We have
created one that interacts with MicroStation. So, the next time we see a
User Interface in MicroStation or another application we wish we could
use, we can create our own control complete with its own Properties,
Methods, and Events. These custom controls can be used from within
VB6 and in VBA.

Creating ActiveX DLLs


We just finished creating an ActiveX Control. ActiveX Controls allow us
to create custom user interface Controls based on existing controls,
some of which are not normally available in VBA. Now we are going to
look at creating an ActiveX DLL.
ActiveX DLLs are compiled .dll files that we can access through VB and
VBA applications. They are often used to 'wrap' functionality into a
single .DLL file and are called a 'wrapper' when this is done. They can be
created to isolate specific logic and calculations from general application
logic.

___"'",",._
New

.;;..;0.=

Let's create a new


ActiveX DLL file
now by starting a
new VB6 Session
and
selecting
''ActiveX DLL" as
the type of project
we want to create.

........_ ..._

IExisting I Aecent I

m
w
VB Wizard
Manager

ActiveX
Act ivex
Document DII Document Exe

~,.
~.

ActiveX
Control

VB Application
Wizard

Addin

Data Project

Open

Cancel "
Help

Don't show this dialog in the future

I'

I Controlling MicroStation with VB61

833

When we create a new "Standard EXE" project, a new Form is created by


default. When we create a new "ActiveX DLL", a new Class Module is
created by default. The names of the Project (set in the Project
Properties window) and the Class Modules are extremely important
when creating an ActiveX DLL.
When we look at the MicroStation Object Model we can see a top-level
Object named MicroStationDGN. Just under this Object is the
Application Object. If we were to create a structure similar to this we
would name our project "MicroStationDGN" and the Class Module
''Application''. Since we don't want to confuse our new DLL's Object
Model with that of MicroStation's, we will use a different naming
convention.
Let's name our Project "msvba_WinAPI" and the Class Module
"System".

General

IMake I Compile I Component I Debugging I

Project I ype:

l!ilm~I!I!IIl-~:J!~mii;i2iilfi.ii!lr!!!r.!: :J: J. .

Project

~tartup Object:

i(None)

~a me:

1msvba_WinAPI
Project Help
Context 10:

tielp File Name:

10
E.roject Description:

r
P

------i
:::J ii
.

odel
Unatt!londed Execution
[ Threading t1
.
IAPartment Threaded
!,!pgrade ActiveX c ontrols
~

.~ ::~:::~:(~i~,~~:~~,:~::

r.

r.

~~~:: ~ :':~~J~~__iB I::~~j


Cancel

Help

834

I Chapter 38: Writing VB6 Applications I


:;7i:'!!t" '"-

,~ .::{ ,,;;:)~~ >:"~W"~('

""J'

~ < .. ,~~~--:r:ry_f ~~~,,,,-

:'..7 .....

...,.::;..~ _.

~"

Project1.:FJ?!!ljl!~t ~~~pei-!i.e.~~,; ~."k":,,::, .;,',,,;,}&.*,~:.!;;.~,~,~;i.~~i,d!8TI


Generall Make

I Compile

~o Compatibility

r.
r

!:roject Compatibility

Component

IDebugging I

!linary Compatibility

OK

f'.- I

Cancel

Help

In addition to naming the Project in the Project Properties dialog box,


the Version Compatibility should be set to "Project Compatibility".
The code we place in the Class Module named "System" is entered
similar to code entered into any Class Module. Here is the code in total:
Pr i vate Declare Funct i on GetSystemMetrics Li b "user32 " (ByVa l nI ndex _

As Long) As Long
Pr i vate Declare Function GetComputerName Li b "kerne132" Alias _
"GetC omp uterN ameA " (ByVal lpBuffer As String.
nSize As Long) As Long
Private Declare Funct i on GetDiskFreeSpace Lib "kerne132 " Alias
"GetD i skFreeSpaceA " (ByVal lpRootPathName As String. _

l pS ec t or s Pe r Cl uste r As Long. _
l pByt esPe r Sec t or As Long ._
l pN umber Of Fr eeC l us t ers As Long.
l pTot alN umb erOfCluste r s As Long) As Long
Private Declare Function Get LogicalDriveStr i ngs Lib "kerne132 " Alias

"Get Log i calDri veS trin gsA " (B yV al nBuffe rL ength As Lo ng. _
ByVa l l pB uffer As String) As Long
Private Declare Function LogonUser Li b "Advapi32 " Alias " LogonUserA "

(ByVa 1 1pszUserna me As Stri ng . _


ByVa l l pszDoma i n As Str i ng . _
By Val lpszPasswo r d As St ri ng. _
ByVal dwLogonType As Long. _
ByVal dwLogonProvider As Long .
phToken As Long) As Long
Private Declare Function ShellExecute Li b "she l 132.dll " Al ias
"Shell ExecuteA"
(ByVal hwnd As Lo ng . ByVal lpOperation As String. _

I Controlling MicroStation with VB61


ByVal
ByVal
ByVal
ByVa!
Const SM CXSCREEN
Const SM CYSC REE N

835

lpFil e As String . _
lpParameters As String. _
lpDirectory As String. _
nShowCmd As Long) As Long
0

Function Version{) As String


Version = App.Major & " " & App.Minor
End Fur.ct ion

Sub ScreenSize(ByRef X As Long, ByRef y As Long)


x = GetSystemMetrics(SM_CXSCREEN)
y = GetSystemMetrics(SM_CYSCREEN)
End Sub

Function ComputerName{) As String


Dim CompName As String
CompName = Space(255)
GetComputerName CompName . Len(CompName)
ComputerName = Left(Co mpNa me. InStr(l. CompName . Chr(O)) - 1)
End Function

Function DiskFreeSpace(DriveLetter As String, DiskTotal As Variant) _ As


Variant
Dim SectorsPerCluster As Variant
Dim BytesPerSector As Variant
Dim FreeClusters As Variant
Dim TotalClusters As Variant
GetDiskFreeSpace DriveLetter. SectorsPerCluster.
BytesPerSector . FreeClusters. TotalClusters
DiskTotal = Byte sPer Sec tor * SectorsPerCluster * TotalClus ters
DiskFreeSpace = BytesPerSector * SectorsPerCluster *
Fr eeClusters
End Function

Function LogicalDrives{) As Variant


Dim LDrives() As Str in g
ReDim LDrives(O ) As String
Dim DriveBuff As String
Di m XSp l it As Variant
DriveBuff = Space ( 255)
GetLogicalDriveStrings Len(DriveBuff). DriveBuff

836

I Chapter 38: Writing VB6 Applications I


XSplit = Split(DriveBuff, Chr(O))
ReDim LDrives(O To UBound(XSplit) - 2)
For I = LBound(LDrives) To UBound(LDrives)
LDrives(l) = XSplit(l)
Next I
Logica l Drives = LDrives
End Function

Function CheckLogon(UserName As String, Password As String) _


As Boolean
CheckLogon
End Function

LogonUser( UserName, .... , Password, 2, 0, 0)

Sub WinOpenFile(FileName As String)

Shel l Execute 0, "OPEN ", Fi l eName ,


End Sub

Testing an ActiveX DLL is similar to testing an ActiveX Control. We


Add a new Project (Add Project, not New Project) to our existing
project, creating a Project Group. Then in the new "Standard EXE"
project, we add a Reference to the ActiveX DLL we just finished
creating:

Available References:

OK

I~: Visual Basic For Applications


I~ Visual Basic runtime objects and procedures

i0 Visual Basic objects and procedures


i0 OLE Automation
l~i

I.

I.::... AS Helper COM Component 1,0 Type Library

'. 1

. Cancel "

Browse .. ,

+1

Once the Reference is added, we use it as we have many other


References in this book. If we add a CommandButton to the form in the
new testing EXE project and go to the Click event of the button we can
enter the following code:
Private Sub Commandl_Click()
Dim myMsVBA As New msvba_WinAPI.System
Dim XS As Long
Dim YS As Long
Dim XDrives As Variant
Dim I As Long
XDrives = myMsVBA.LogicalDrives
MsgBox Join(XDrives, ", "), . "Dr ives On System"

I Compil ing and Distributing Appl ications I

837

For! = LBo und ( XDr i ves) To UBound(XDrives)


MsgBox XDrives(!) & vbCr & _
myMs VB A. Di skFreeSpace(CSt r (XDr i ves(!)), 0), , _
"Drive Free Space"
Next !
MsgBox myMsVBA.ComputerName, , "Computer Name"
myMsVBA.ScreenSize XS, YS
MsgBox XS & " , " & YS, , "Screen Resolution"
MsgBox myMsVBA.CheckLogon( "Administrator", "ad minpwd " ),
"LOGON "
myMsVBA.WinOpenFile "c: \abcdefg . pdf"
End Sub

We have discussed previously the fact that we can place code into Class
Modules and use them in our code. When we compile the Class into an
ActiveX DLL, it makes the code much easier to use. Multiple VB and
VBA projects can now 'attach' to our ActiveX DLL.
Just as with the ActiveX Control, this DLL file must be registered by
using Regsvr32.exe before it can be used on computers other than the
one on which we are developing the DLL. When we compile the DLL file
in VB6, VB6 registers it for us.
This ActiveX DLL can be used by other VB6 Applications as well as
VBA Applications developed in MicroStation VBA.

COMPILING AND DISTRIBUTING ApPLICATIONS


When we are writing code in a MicroStation VBA project (mvba file),
we can simply give our users the file and if we are not using custom
ActiveX Controls, in most circumstances, everything will work well.
When we are writing code in VB6, we need to compile the Application.
And even after we compile the application, there is usually more that we
need to do. Let's first examine compiling VB6 programs and then we
will deal with distributing our applications written in VB6.

838

I Chapter 38: Wri ting VB6 Applications I


Compiling Applications
~ew

Project

~ Qpen Project".

Ctrl+N
Ctrl+O

Agd Project".
Remove Project

IiiI Saye Project Group


Sav!l. Project Group As ...
2ave frmTestSystemDLL.frm

ctrl+S

Save frm TestSystemDLL.frm as ...

5a'./8 CD.anqe Script

~ erint. ..

ctrl+P

III Print Set~".

,MiMM#WWU4i\f*"
~lake Project !aroup...

.,

TestSystemDLL..vbg

~ msvba_WinAPI. vbp
~ msvba_modeltree.vbg

:1 Projectstructured. vbp

When it is time to compile an


Application in VB6, select File> Make ...
The text we see after the word "Make"
differs based on the name of the project
and what type of project we are working
in. In addition to seeing "Make" and a
project name we may also see "Make
Project Group". Make Project Group is
used to compile all Applications in the
current Group .
So, compiling an application is very
simple. We can see the menu items here.
But when should we compile?

is possible to create an entire


application without compiling until the
time comes that we are ready to distribute
it. In the case of an ActiveX Control and an ActiveX DLL, we can Add a
Project to the Project Group and test the Control or DLL without
compiling. If, however, we want to test a Control or DLL in another
application such as MicroStation's VBA environment, we need to
compile it. When it comes to Standard EXE projects, compiling a project
may be useful during the development process to test performance.
When we run a program in Design Mode (when we can break into the
application, step through code, etc.), VB6 applications run much slower
in design mode than when they are compiled. By default, compiling a
VB6 application causes a more thorough check of our code by the
compiler. Errors in Modules, Forms, Procedures, Functions, etc., that
were not identified when we ran the program in Design Mode will
surface when we compile a project.
-----------c-.
E2(it

It

Alt+Q

Compiling a VB6 project usually results in an .exe file (Standard EXE),


an .ocx (ActiveX Control) or a .dll (ActiveX DLL) file. It would appear
as though we could e-mail or distribute this one file to anyone in the
world and our program would work on their computer just as it does on
ours. And this may work from time to time but this is not always the
case.

I Compiling and Distributing Applications I

839

Distributing VB6 Applications


When we speak of distributing VB6 Applications, we mean not only
placing files on another's computer but also registering controls and
DLLs and otherwise 'setting up' their computer.

Standard EXE Applications


When a Standard EXE Application written in VB6 is to be distributed,
there are at least two files that should be distributed: the EXE file and the
VB6 Virtual Machine file (msvbvm60.dll). Without the VB6 Virtual
Machine file, VB6 Applications cannot run. The VB6 Virtual Machine
DLL is installed on a large number of computers but there is no
guarantee that it is installed on every computer where our software is
installed, so it should be distributed with our application.
In addition to these two essential files, other considerations must be
made. For example, let us suppose that we have created an application
that makes use of the Internet Transfer Control for sending files to an
FTP server. This control (an .ocx file) is installed with VB6. It is
extremely useful. With it we can create a powerful internet -enhanced
program. But if we give our program (an EXE file) to someone and they
do not have this ActiveX Control on their computer, our powerful
program will go nowhere. It will do nothing but display error
MessageBoxes.
When we are
looking for files
on which our
application
is
dependent, there
are two places
where we need to
look. The first is
in the References
dialog box.

(ivailable References:

OK

Y.l Visual Basic For Applications


Y.l Visual Basic runtime objects and procedu res
Y.l Visual Basic objects and procedures
Y.l OLE Automation
Y.l msvba WinAPI
Y.l ~licrosoft ActiveX Data Objects 2.8 Library

6.rowse .. ,

..!J

~ Microsoft Scripting Runtime


~ Microsoft Shell Controls And Automation
Y.l Microsoft Excel 11.0 Object Librar~

Priority

Y.l Microsoft ADO Ext . 2.8 for DDL and Security


Y.l Microsoft Speech Object Library

[] lAS Helper COM Component 1.0 Type Library


TAS RAf)TlI'=i Prn~nrnl 1 .n TvnF! I ihrrlrv

~I _c~ __ -"~_~oo_c.J

tielp

Y.l~I~IIU!llll!lml,i!!tlll~

Cancel

,-. Microsoft Message Queue 3.0 Object Libr ary ------..-------.. -----..---.. ----------1

Location:

_w _ _ _ _

Language:
_____ _

C:\WINDOWS\system32V~QOA.DLL
Standard
~

_ __ _ _ , . __ _

_ _ _ _ _ _ _ _

_ _ _ _ _ _ _ _ . _ _ . _ . _ _ _ . _ _ _

i
_

_ . _ _ __ _

.l

840

I Chapter 38: Writing VB6 Applications I


H ere we can see a large number of References. The top four shown are
standard to any VB6 program. We don't need to worry about them. The
other References that are selected are the ones that should concern us.
When an item in the References list is selected, its full path is shown
after the "Location" label.
The other area we sh ould look into is the Toolbox. If, for example, we are
using a Tree View control, we need to make sure that th e file containing
the Tree View control is distributed.

Controls

IDesigners I Insertable 0 biects I

be Microsoft Windows Common Controls 6.0 (SP6) :;-..;;:


(;;;oj Microsoft Winsock Control 6.0 (SP6)

!l.rowse ...

~2elected Items Only

~liCrosoft

Windows Common Controls 6.0 (SP6) - - - - - - - -- -------:

Location:

C:\WINDOWS\system32\MSCOMCTL. OCX

_; ...... _ ..... .. ....... . .. .................. . ........ _ . , ' :, ... ....... . ......... . ... _ _ . _ _ ; _ _ ................. . . h . . ....... _ _ . .. . .. .. . ......... ...................

. 0K

Cancel

_i

,t'.Pp!,'

Right-click on the Toolbox and select "Components". This brings up the


Components dialog box. When we select "Selected Items Only", only
those Controls that have been 'selected' are displayed. The fact that a
Control has been selected does not mean it has been inserted into our
project. If it has not been inserted, the Control does not need to be
distributed with our Application. Right?
If we attempt to 'un-check' an item
that is inserted, we will see this
MessageBox:
Any files that are "un-checkable" are
not actually used in our program so
we do not need to distribute them.

f!~

Can't remove control or reference; in use

OK

_ _H_el p
_--,

I Compi ling and Distributing Applications I

841

OK. We know which files we need from the References and the Controls
area. We know that we need to Register DLLs and ActiveX Controls on
the host computer. How do we best do this?
We have already used the RegSvr32.exe registration process. We could
place a series of these registration commands in a batch file (.bat file).
We could then compress these source files and registration batch file
into a zip file. Although this solution would certainly work, it is far from
elegant and user friendly.
Let's examine the solution that is shipped with VB6: the Microsoft
Package and Deployment Wizard. It is found in the "Microsoft Visual
Basic 6.0" Start menu.
tCm

~licrosoft Visual Studio .NET 2003 ~

,;;m

Mirrn,nft

W~h P"hli,hinn

!~~--------------~-

Before running the Package and Deployment Wizard, it is a good idea to


close down VB6.
1

The first step to using the Package and Deployment Wizard is to


select which Visual Basic Project (.vbp) file we want to distribute.
Then we click on the Package button.
i

"':"Y-;"";-;:---f"1"'

''';.o:, /,

'''-''-

~~p;~~e al!d Oe~l~ymentWjzar~, "

......,.....

w'~~~

.:

,. :" "'': ,::, ... .~,.:JIJ

Select proiect:
Ic :\program

Files\~licrosoft Visual Studio\VB98\Projectl.vbp o::J

I[ :J~i;:;~~:~:::::)1

Bundle this project into a distributable package, such as an


Internet cab or a setup program.

Send one of this project's packages to a distribution site, such as


an Internet server.

~eploy

Rename, duplicate, and delete your packaging and deployment


scripts for this project.
Manage
Scripts
Close

Help

842

I Chapter 38: Writing VB6 Applications I


2

The next dialog presents the choice between a "Standard Setup


Package" and a "Dependency File': We will select a Standard Setup
Package.

Choose the type of package you want to create.

f'.ackage type:

r",rll\UI"i!lfll!ll!ft@l" .
D;ndency File

Q.escription:
Use to create a package that will be installed by a
setup. exe program.

Help

Cancel :

We are next asked to select a fo lder in which to create the Setup.exe


file.

f'.ackage folder:
IC:\Program

Files\~licrosoft Visual Studio\VB98\Package

~C :\

ell

Ell Program Files

,. =11
:. ...1

aMicrosoft Visual Studio

Q] MSXM Addln

G;:3 RainyDayCode Addin


Help

Cancel

<!!ack

:3

j9c:
Network...
Ne~Folder ...

Einish

After selecting a folder, we are asked to verify which files we are to


include in the Setup file. This step takes care of determining which
files need to be distributed with our Application. So, if we are going
to use the Package and Deployment Wizard, we do not need to
worry about looking at the References and Controls we have added.

I Compiling and Distributing Appli cations I

843

This step is also important because we can Add files to our Setup
package that may not be added automatically. For example, if we
have written a User Manual and created a PDF file of it, we can Add
the file in this step and it will be compressed with the other proj ect
files and 'installed' on the user's computer.
"

<.~ ~

.,

-~'

IT

, ' ' : """,-~ .~"'.,

t"',

~ ~ j'"

"~r""." ~ ""'r> ~J:"If'I'}'.)t":' i'""'~"'iii:1!:'>1<'f'~_~T'

~ P~kage ilnd Dep(oym~Ilt.Wi~a!d ,-)!!c(u~ed"Fi!~~""

";:{",L;",, ~tdil

The files in the list below will be included in your package, Click
Add to Include additional files , Clear the checkbox to the left of
the file name to remove a file from the package,

FUes:
Name
Source
""
c:\Progr am Files\Microsoft Vis
0 SETUP 1.EXE
SHELLLNK.TLB
C:\WINDOWS\system32
ST6UNST.EXE
C:\Program Files\~licr osoft Vis
V86 Runtime and OLE Automation

o
o
o
o V86STKIT.DLL
Help

B.dd" .

C:\WINDOWS\system32

Cancel

< !l,ack

After verifying which files are to be included in the Setup file, we are
asked whether we will be distributing the setup as a single file or if it
is to be placed on multiple floppy disks (outdated but still an
option) .

You can create one large cab file or multiple cab files for your
package. If you are going to distribute your application on
floppy disks, you must create multiple cabs and specify a cab
size no larger than the disks you plan to use , Choose the
appropriate option below.

Cab options

i r.

:2ingle cab

I (' Multiple cabs


iI

~;t, "lC'"

I ,;: :"
..:J
I
I____,_ _ ___~
i

Help

Cancel

< !l,ack

844

I Chapter 38: Writing VB6 Applications I


6

Next, we supply the title for the Setup package:

Enter the title to be displayed when the setup program is run.

Installation title:
!MicroStation VBA Sample

Cancel

!l.ack

Shortcuts can be placed in the Start Menu or in other places.

Start menu items:

."

a ...g Start Menu


Ela

New ~rou p

Programs
MicroStation VBA Sample
i.. ... ... MicroStation VBA Sample

8"a

New ltem...

I'
I

--"'----'

e,roperties ...
e,emove

Help

Cancel

<!l.ack

t~.in i:;h

The files that have been included in the setup package must be
placed somewhere. By default, DLLs and OCX files are placed in the

I Compil ing and Distributing Applications I

845

System32 folder. We can, however, change the lo cation of these files


in the next dialog box:

You can modify the install location for each of the files listed
below by changing the macro assigned to the file in the table. If
desired, you can add subfolder information to the end of a
macro, as in $(ProgramFiles)\CVlySubFolder.
Choose the file you want to modify, then change the
information in the Install Location column.

Flies:
Name

Install Location

Source

MSCOfV1CTL .OCX

' ~ :\VVIN~()VVS\systeIl13~ ....

msstkpr~: ~~

i C: \VV I~J~()"V\I?\syst~Il1~~

~r~je.c~.l :",.~_e __.


scrrun.dll

$(WinSysPath)
.... ... .. .. ..
.
,

"

' ~(Win?ysPath)

... C:\~r_~~r.':ll1~iI",s.lCV1ic'-(Js~!t ~~sl:'a!_?t~~i(J\VB9~


C:\WINDOWS\system32

$(~p'pPath~
$(WinSv.sPath)

~E_~~~NK. T~.tJ. ___ . TC :~~~J~~0~~y~~~3i.__ $~VV!n?ys~~th)


Help

Cancel

< !l,ack

H::::: tl~~t:?:: tP

We are not going to specify our project's testing executable as a


'Shared File' so we will continue through the next dialog box
without selecting our project's executable file.

When this package is installed, the follOWing file(s) can be


installed as shared files. Shared files may be used by more than
one program. They are only removed if every program which
uses them is removed. Check the files you want to install as
shared.

Shared files :
Name

o Projectl.exe

<
Help

Cancel

Source
C:\Program FilesV1icrosoft Visual Studio\VB91

I.

846

I Chapter 38: Writing VB6 Applications I


10 The last dialog box we have to deal with allows us to specify a Script
name.
~.-~~';"'"'"',.....~~

~:""~~~,,,!,~

~~~........ ~~ ~

;i.:J~~!!C~e and Dep[oyrnent,~iz~rd: ~ ~~~ih,e~l "

'" ,."",~",.,," "",:, ,; . ~'f.', ' ~

The wizard has finished collecting information needed to build


this package, Enter the name under which to save the settings
for this session, then click Finish to create the package,

After we click the Finish


button, our setup file is
created.

.. ;;;;p.f ........h~~. .""~;;-< ;;,v~':Jt

.~"'. "f'r~' ""-~"':;'''';.

,"""11-';",""""',,\

~ '<~~"">' 1'''\/,t."-",,,,!'7~''''''''''

;Wi?,t~.~&~::~,'; 1,;~i~~:~~::~i~b~A~~:; ~;~~~tii~~~~~~~<':/~~~~t~,~~~r : '~A'~ ~

After the Setup program


Creating cab file(s) .. ,
is complete, a report is
generated and we are
given the option of
saving the report or closing the wizard.
The cab file for your application has been built as 'C:\Program
Files\~licrosoft Visual Studio\VB9B\Package\Project1.CA8',
There is also a batch file in the support directory (C:\Program
Visual Studio\V898\Package\Support\Project 1.8AT) that
will allow you to recreate the cab file in case you make changes to some
of the files,

Files\~1icrosoft

:l.dve Report

Close

I Review I

File

Edit

View

Back

: Add, ess

847

Favorites

Tools

Help

t:' t ./'J Search re~ Folders r m:n.

1-"'---"'--' ...---.-...- - - - - ----.--.---.... ~g_~jr.~~r am File.~~~rosoft Visual Studio\VB98\Package

Folders

.- ...--. -'-i'bPa~k;;;;-- .. - ---- ~.


:

( f

"'I,

10 RainyDayCode Addin
Ib Setup
10 Summary Inform ation
,I

4 objects (Disk free space: 5.35 GB)

Name '"
e;i.~~p'p'~;t:J

qJ Project 1. CAB

'"

~setup . exe
V; [hl SETUP. LST

'

2 '_-_ _ _ _ _

So, we have completed the


Package and Deployment
Wizard. And what do we
have to show for it? Early
on in the packaging
process, we were asked for
a location to place our
deployment files. When
we browse to the location
we selected, we see the
files created by the
Wizard.

We are given a Project7.CAB file, a setup.exe file, and a SETUP.LST file.


The .CAB file contains all of the files that are to be distributed and they
(all of the files) are compressed into a single file. The .LST file specifies
where each of these files are to be saved. It can be modified after the
CAB and setup.exe file is created. As for the setup.exe file, its only
purpose is to extract the files in the .CAB file and place them in the
folder specified in the .LST file. It also registers components as
instructed in the .LST file . The Support folder contains all of the files in
their un-compressed state.
The Microsoft Package and Deployment Wizard does a fairly good job at
packaging up programs developed in VB6. There are, of course, other
products that create installation programs as well. One of these is
Macrovision's InstallShield Express.

REVIEW
VB6 has an environment that looks and feels a lot like VBA. In addition
to creating stand-alone programs, we can create our own custom
ActiveX Controls as well as ActiveX DLLs. When we finish our
programming and compiling, we can distribute our application by using
the Microsoft Package and Deployment Wizard which ships with VB6.
Once we 'attach' to MicroStation, developing in VB6 is nearly identical
to developing inside of MicroStation's VBA environment. All of the code
we have created and worked with inside of MicroStation VBA can be
'ported' to VB6 with very little difficulty.

848

I Chapter 38: Wri tin g VB6 Applications I


There is one thing we need to be careful about when 'porting' a VBA
program to VB6. When we are working in VBA, several Objects are
naturally exposed for our use, the Application object, for example. In
MicroStation's VBA environment, we can type
Appl i cation . Act i veDes i gnF i le . . . . .

and the code works. When we are working in VB6, every MicroStation
Object must be implicitly set. If we declare a variable to represent the
MicroStation Application as Public in a Code Module, however, we only
need to set it once and we can use it thereafter without the need to 'set' it
again.

NOTE: Microsoft Visual Studio 6 has been replaced with Visual


Studio .NET 2003 and Vis ual Studio .NET 2005. The .NET family of
development products can produce standalone .EXE files and can
create DLL files that are accessible through COM. However, .NET
cannot be used to create ActiveX Controls. So, if a new Control is
needed in MicroStation's VBA environment, it should be created in
VB 6.

39

Using VB.NET
On the surface, it appears as though VBA, VB6, and VB. NET are
identical. They each bear, in part, the title "Visual Basic" and make use
of the same "Visual Basic" basics. And yes, they are very similar. But
there are also differences between them. VBA and VB6 have much more
in common than VB.NET. The differences between them (VBA and
VB6) and VB.NET can be quite significant, depending on which 'area'
we are considering. When Microsoft introduced the VB.NET
environment in 2003, one of the aims was to make Visual Basic much
more Object-Oriented. This is one of the primary differences between
VB.NET and VBA or VB6.
This chapter deals with not only controlling MicroStation in VB.NET
applications but also concentrates on the differences between these
environments so we can become more proficient in the VB.NET
environment and language. We will be using Visual Studio 2005 for our
discussion.

In this chapter:
[B An Introduction to the VB.NET Environment
[B You can do this in VB.NET!
[B VBA / VB.NET Cross Reference
[B Distributing VB. NET Applications

849

I Chapter 39: Using VB.NET I

850

[B The Future of VB. NET with MicroStation

VB.NET INTRODUCTION
Let's take a look at the VB.NET environment. The first thing we should
get out into the open is the fact that whereas VB6 is an Application and
VC++ is its own Application, the .NET environment is used for
developing Applications in VB, C# (pronounced C Sharp), J#, etc.

Adding 810g9in9 to Your Apps with My.Blogs and Visual Basic .

JoJ,;

Fri, 27 Jan 2006 05: 56:02 GMT My,Blogs is a collection of sample code
that shows how to easily provide programmatic access to blogs in your
applications. Chris Mayo shows how easy it is to reed and pubUsh blog
entries within Visual Basic 200S using My ,Blogs.

' !l

Visual Basic: Navigate The .NET Framewo rk And Your Projects ".

Tue, 24 Jan 2006. 16:58:48 GMT One of the biggest issues that
Open:
Create:

Project .. ,
Project...

IWeb Site.,

IWeb Site, ,,

r~1n::ee~se~~~I~:~ ;~rt~ep~~~~~I~~~~~~hq~~t~NJ~~~i~~ ~~~c~~ke


Mackenzie shows how the My namespace in Visual Basic 2005 will make
Conver ting a Dfl ta -Ol'icnted Application from Visual Basic 6 to ...

Tue, 24 Jan 2006 18:24:04 GMT Ken Getz and Paul Sheriff show you
how to migrate your data-oriented application from Visual Basic 6,0 to
Visual Basic 2005 in Part 3 of this series.

HowDoI ... ?
What's nell'} in Visual Bastc 2005?
Create 'four First Application

Use a Starter Kit


le,3rn Visual Basic
Connect With the Comrnurlity
Download Additional Content

Cha nnel 9: M]X with aill Gates in Vegas

r.1,:.;'
f

i
ri
~ ,[
.!

~ 11

tii{jjj

f ;li

F{[

'l!

Mon, 23 Jan 2006 23: 11:59 GMT Watch the MIX content team in action
as they talk about their plans for this new conference happening at the
Venetian hotel in Vegas.
User Interface Contro ls in Visual 8asic 6 a nd Visual Oasic 2005
Fri, 20 Jan 2006 20:08:28 GMT When upgrading Visual Basic 6
applications, Uttle causes more concern than controls that just wont
convert -in-project controls, custom ActiveX controls, built-in controls,
etc. B~I Sempf shows you how to ease this process,
Press Release: lnb'oducing tlelpStudio lite tlelp Authoring Tool
Wed, 18 Jan 2006 19:08 :34 GMT The Visual Studio 2005 SOK ships with
a free tool for authoring Help content and integrating it with Visual Studio
2005. HelpStudio lite, built by Innovasys, Ltd" Is a lightweight version of
HelpStudio, optimized for Visual StudiO Industry Partners.

Give us feedback , suggestions, or


tell us about bugs in Visual Studio
2005

Register Now for the MIX '06 Conference in Las Ve gas

Fri, 13 Jan 2006 t 7:59:25 GMT - Attend the MIX conference, a LIVE !
conversation between web developers, designers and business leaders,
being held in las Vegas from March 20 - 22 at the Venetian hotel with Bill
Gates and Tim O'ReiUy ,

The Start Page of the Visual Studio 2005 environment

From the Start Page of Visual Studio 2005, we can create or open new
Projects. Tn addition to the ability to create new projects and open
existing ones, we can see the RSS fe ed from Microsoft's MSDN Visual
Basic homepage. (We discussed RSS technology in an earlier chapter.)

I VB.NET Introduction I

851

Creating a new Project

Project types:

Templates:

_._--_._-----------_._------_...

8 Visual Basic

Visual Studio installed

~emplates

Windaws

II Smart Device
Starter Kits

II Other Languages
II Other Project Types

~
..vB

Windows
Application

Class library

"

Consale
Applicatian

Jill

Windaws
Web Control
Library
Cantrollibrary

Empty Project

-1... ............................................_..... _....... .

I A project f or creating an application with a Windows user interface


Name:

i ~licraStatian Contral A

I
~[

Cancel

We will create a new Project named "MicroStation Control N. It will be


a Windows Application. As with VB6, a new project is created with a
single Form added.
We know we want to control MicroStation. What is the next step?
Adding a Reference, of course.

From the Project menu, select Add Reference.

I.
2

OK

Cancel

Select the COM tab and scroll down to "Bentley MicroStation DGN
# .# Object Library". Clicking the OK button adds the Reference and
we are ready to continue.

I Chapter 39: Using VB.NET I

852

3 Double-click on our Proj ect Name in the "Solution Explorer" and


then select the References tab to display all the references of the
current project.
File

Edit

View

Projett

: ~ '~ r3 0 " fZil "

Build

Debug

Data

Tools

I h G'J'1;',' ~ ! 3!
.

'.

-.

Window

' ~1

Community

r" ., .

IJ

4 . . . . . --

Help

~]i ,"".~
:!]! '~ ~ ~

<>]!

-. -

~ Drj~,....-.._~
"_ __

)('

,-

11!~~:_S tati o n :~~~~~~~~=~~~-====~==~=====~-==~=_::..=,_ XIjr~~:~e:i:~~'" ~ X


y

g-

Ii

Application

'I

Compile

ReFerences:

x II

1
.

Debug
!
___,____...___J

II

Unused References".

i I i3I MicroSta tlon Control A

"

ReferenceP :CJ

1j

'I
~ __ ~I

Ty~ ~:r~,~

S.opY" l ocal

P"'th. _

COM

8.0.0, 0

True

C: \Documents and sett,'

,NET

2,0,0,0

False

C:\WINDOWS\Microsof

,,1

References

I ..
i
!

C: \ WINDOWS\Microsof

Resources

,NET
,NET

2,0,0,0
2, 0,0, 0

False
False

C:\WINDOWS\Micro,of
C: \WINDOWS\Microsof

Settings

,NET

2, 0,0, 0

Fal,e

C:\WINDOWS\Microsof

My Project

~ Form1. vb

i c-I~-----,;=,~==-=====I! --< Solution Expl~~Oata Sources

(:\WINDOWS\Microsof --

l5iI

i
;1,'

I!~,~~,~~,,",,[W=~,,,,;=C~I~,,,--,-,-,=-,,,,,--,

;11

~~~f--

"

~================================-=--=,--=---~--It-===--=--=======I
Ready

From this window, we can see a reference named System, one


named System.Data, another named System.Drawing and others.
They have a Type of ".NET". We can see that the Bentley
MicroStation reference has been added. Its Type is "COM".
Let's write some code now.

4 Switch over to the Forml.vb tab and pin the Toolbox by clicking on
the pushpin icon at the top of the toolbox.
5

Now, drag and drop a Button from the Toolbox to the Form.
~

.iI:W' ..... r:;'f'

..... :- . "...'w,

"-~

Toolbox , ~ ___' ... Q.

SttJ}}IWi!lItQIY~J:9r~ ""'.
f;l h91lJfIlOll Qmtrpls '" _, ' __

11\

Pointer

(ill

Button

rn]

CheckedListBox

-"'1"W*_'"

'Vi'

','

.. ..

".

o;~

. ... 0

w "."

x ' i /--MiZrOstatkJn controfA;;; -ftorm-i~vb [Design

I!
;

o CheckBox

~ ComboBox

Next, double-click on the Button to enter the Click event of the


Button.

Nm.'l that we are in the Click event of the Button, we are going to
enter some very simple code that 'attaches' to MicroStation and
displays the Application's Caption in a MessageBox. Here is the
entire listing of code that includes this basic fun ctionality.

I VB.NET Introduction I

853

Publ i c Cl ass Forml


Private Sub Suttonl Click(
SyVal sender As System.Object. _
SyVal e As System.EventArgs) Handles Suttonl.Click
Dim myM SAppCon As MicroStationDGN.ApplicationObjectConnector
Dim myMSApp As MicroStationDGN.Application
myMSAppCon = GetObject(. _
"MicroStationDGN.ApplicationObje ctConnector " )
myMSApp = myMSAppCon.Appli cat i on
MsgBox(myMSApp.Caption)
myMSApp = Nothing
myMSAppCon = Nothing
End Sub
End Cl ass

The code should look very familiar. It resembles the code we created
in VB6 as well as code created for Microsoft Excel. One of the main
differences between what we see here and what we used before is the
absence of the "Set" statement when we are working with Objects.
Why is this? Because all variables are Objects in VB.NET. Strings are
Objects. Integers are Objects. So, we don't need to use "Set" when we
are assigning variables their values or objects.
If we look at the MessageBox statement, we see that we are using
parenthesis in VB. NET where we do not do this in VB6 except when
we are getting a return value. Any time we use a Function or
Procedure in VB.NET that uses Parameters, we surround the
Parameters with parenthesis.

8 If we try running our code, we will find that the code seems to run
fine. But before doing anything else, we should save our project.
Selecting File> Save All displays the Save Project dialog box.

~I

Cancel

With the settings as they are shown, a new directory is created


named "MicroStation Control 1\:

854

I Chapter 39: Using VB.NET I


Compiling our application
1

We begin compiling our application by going to the menu Build>


Build MicroStation Control A.
Build

1 Debug

ffIl

Build MicroStatlon Control A

Data

Tools

Window

. d MicroStation Control A

Clean MicroStation Control A


Publish MicroStation Control A

When we "Build" our application, an .exe file is generated. Let's take


a look at this next graphic and then we will discuss it.
File

Edit

View

Favorites

Tools

Help
H

?====~;" " ""

"

"

""

" "

""

"" """

......................................................... ....

""""" " "...... . .. . . ..

..... __..._..... ;=
......=.. ~=~=""'~=~k===~==~==~. ~=_~=~~=
Ad9~~~ ro C:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\Projects\MicroStation Control A\~'icroSt

Folders
X
I--El-:ro:--V-isu- a-I S-tu-d'-,o 20-05-----\=~!

Efi ro Backup Files


iI IQ Code Snippets
El IQ Projects
El ro ~1icroStation Control A
El 10 MicroStation Control A
El Cll bin
10 Debug
Release

Name ""

"

Size

Type

'~[i6:t:;;,~~~::0.i~i~~~~~i~6~:~:~li::i''-1;208KBDUF~~~-~~~",--.~.-.=.

.-

D~1icroStation Control A,exe


~ MicroStation Control A,pdb
~ ~'icroStation Control A, xml

28 KB
60 KB
1 KB

Application
Program Debug Database
X~'L Document

[JI

When we compile applications in VB.NET, by default, they are


compiled into one of two folders: Debug or Release. "Debug" and
"Release" are compiler configurations we can use as we develop and
compile our applications. These configurations have different
settings pertaining to debugging, performance options, and
processor preferences. We specify which configuration we want to
use in the "Configuration Manager" (Build> Configuration
Manager).

NOTE: If we are attempting to debug an application in VB.NET and


we seem to be unable to step through our code using <P8>, we should
look at the Configuration Manager to check the active Configuration
settings. Chances are, we are attempting to debug an application with
the Release Configuration selected (which disables much. of the
debugging capabilities of VB. NET).

I VB.NET Introduction I
2

8S S

If the Configuration Manager menu item does not appear in the


menu, you can turn it on by setting the option in Tools> Options .

lI Environment
8 Projects and Solutions
General
VB Defaults
:J Text Editor
:! Windows Forms Designer
:! Device Tools

Visual Studio projects location:

1ml.ftiititit\t:nttiitift.tt1W

.1.t.i!"t4.tm\ml.im;ft.mn#1~

[J

Visual Studio user project templates location:

fu~ments and Set.t.i~.?~~~~istrato~~~ly D'::.uments\~:,:~_~~io: 1[J


Visual Studio user item templates location :

[CJO~~;;;~t-;~~g~\Ad;;;~-~MYDo~~-;';t~Vi~~~iSt;lo;l

-.

[J

~ Always show Error List if build finishes with errors

~ Track Active Item in solution Explorer


8l.show advanced build configurations
~Iways show solution

o Save new projects when created


~ Warn user when the project location is not trusted
o Show Output window when build starts
o Prompt for symbolic renaming when renaming files
o Show all settings
3

_ ..J11 Cancel

ti.-......;;.
O;.;.
K

Select "Show advanced build configurations" to turn on


Configuration Manager in the Build menu group.
Now, let's take a look at the Configuration Manager.
Active solution configuration:

Active solution platform:

[ ~.:.I~~~~ __________. . ____._____ .____ ____ .___ ._ . __;~J

L ..._________________ __. ___ . .______ .

_':''J

Project contexts (check the project configurations to build or deploy):

I Project

,. ~1i~;oStation

C~nt;~1 P-

confi~ur a~~n

Release

, Platform
~'! Any CPU

Build

When we Build our applications, they are placed in the folder


specified for the active configuration. By default, "Release"
compilations are compiled in the Release folder and "Debug"
compilations are compiled in the Debug folder. We use the
Configuration Manager to specify the Active Configuration. When
we Build our applications, the compilation is based on the active
configuration.
This concludes our brief introduction to the VB.NET environment
Microsoft Visual Studio 2005 .

III

856

I Chapter 39: Using VB.NET I

You CAN DO THIS IN VB.NET!


We just created a very simple, very small application in VB. NET. There
are some differences between the IDE (Integrated Development
Environment) of VB.NET and that of VB A and VB6. Once we learn how
to use the IDE in VB.NET, we can begin looking around a little at what
else we can do in VB.NET. If we explore a little bit, we will find a few
'tools' that are not readily available in VBA. Let's create a few
applications that make use of VB. NET-specific functionality.
The next project we will create is a "Windows Application" project
named "MicroStation Control B". The source files are on the CD
accompanying this book.
Let's take a look at the application when it is being run. We will discuss
the functionality and then look at the code.
r--------------!~;

11'

. Crealed By: Benlley Syslems

Creale 0 ale: 1/1/2006


~ :1: WebsileAddress: http://www.benlley.com

t~;
~:

.'}

$ 0 00000

HEIGHT

C)

II'
J

NORTH

[l?QJ__~~:

L___

'To- W
- e-bsile- - - - - - - - - - - - - - - - - '

The concept for this application is fairly simple. We see a list of cells that
can be inserted into MicroStation with a thumbnail image of the Cell
accompanying the Cell name. We also see information about the
selected cell in a text box on the right. This additional 'information' is
stored in an .info file with the same file name as the preview image. If a
line beginning with the text "Website Address:" is found in the .info file,
a hyperlink 'To Website' is shown which, when clicked, opens a new web
browser window and opens the website address in the .info file. If a
website address is not in the .info file or if an .info file is not available,

I You can do this in VB.NET! I

857

the "To Website" link is not displayed. When the user double-clicks on
an item in the List View control, the 'double-clicked' cell is inserted into
MicroStation. To keep things simple, we will insert the Cell at the center
of the current view.
Rather than hard-code cell names and force the user to create thumbnail
images of a particular size, we base the contents of the entire list on the
availability of bitmap (.bmp) files in the Application's folder. The name
of each bitmap file corresponds with a cell name. Information Files
(.info) match the file name of the bitmap files and contain reference
information about the cell. A thumbnail is automatically created in
memory for 'use in the ListView based on each bitmap file. Each
dynamically created thumbnail is 64 pixels wide and 64 pixels high. The
source bitmap files can be any size but will ideally be square in shape
since the thumbnail image that is created does not compensate for
differences in aspect ratios.
Here is the code for this project. Keep in mind that we already have a
reference added to the Bentley MicroStation DGN # .# Object Library.
Public Class For m1
Pub l ic ExePath As Stri ng
Pu bl ic FixedHeight As Long
Pri vate Sub Form1_L oad(ByVal sender As System.Object ,
ByVal e As System .E ventArgs) Handles MyBase.Load
Dim myFile As System . IO.F i leIn f o
Dim myFo l der As New _
Sy stem. IO . Dire c to r yIn f 0 (Ap P1 i cat ion. Ex e cut a b1 ePa t h)
Dim myLstVI As ListViewItem
Dim myImg As Image
Dim myThumb As Image
ExePath = myFolder.Parent.FullName
For Each myFile In myFolder . Parent.GetFiles("*.bmp " )
myImg = Image.FromFile(myFile.FullName)
myThumb = _
myImg.GetThumbnail Image( _
ImageListl.ImageSize.Width, _
ImageList1.ImageSize.Height, _
Nothing , Nothing)
myThumb.Tag = myFile . Name . ToUpper . Replace( ". BMP ", "H )
ImageList1.Images.Add( _
myFile.Name.Replace( ". bmp " , "H ) , myThumb
myLstVI = lstvCells.Items.Add(myThumb.Tag, _
ImageList1.Images.Count - 1)
Next
End Sub

858

I Chapter 39: Using VB.NET I


Pr i vate Sub l stvCells_ I temSe l ect i onChanged(ByVa l _
sender As Object. ByVal e As _
System . Windows . Forms . ListViewItemSelectionChangedEventArgs) _
Handles ls t vCells. I temSelectionChanged
Dim myFi l eInfo As IO.FileInfo
Dim myFileReader As IO.StreamReader
TextBoxl.Text = ""
LinkLabell.Visible = Fa l se
myFileInfo = New IO.FileInfo(ExePath & "'" &
e. I tem . Text & " . inf o" )
If myFileInfo.Exists = True Then
my Fi leReader = my Fi le In fo .O penText
TextBoxl.Text = myF i leReader . Read ToEnd
myFileReader.Close()
If InStr(TextBoxl.Text. "Website Address: " ) > 0 Then
Li nk Labe l l.V i sib l e = True
End If
End I f
En d Sub
Pr i vate Sub l stvCel l s_Do ubl eClic k(ByVa l sender As Obje ct. _
By Val e As System . EventArgs) Hand l es
1stvCe ll s . Doub l eC l i ck
Di m myMSApp Con As _
MicroStationDG N.Appl i cationObjectC onnector
Dim my MSApp As MicroStat i onDGN . Application
Di m myCe llEl em As Mi c r oStat i onDG N.Cel lEl ement
Di m Ce llO r i gin As Mi c r oStat i on DGN . Poi nt3d
Pr ogressBar l.V is ibl e = True
Prog r essBa r l .V al ue = 10
myMSA ppCon = GetObject(. _
"Mi cr oStati on DGN. Ap pli ca tionO bj ect Con nec t or " )
ProgressBarl.Value = 20
myMSApp = myMSAppCon.Application
ProgressBarl.Value = 40
CellOrigin = myMSApp . CommandState.LastView.Center
Prog r essBarl. Valu e = 60
myCe l lE l em = myMSApp.CreateCe ll Element3( _
l stvCe ll s.SelectedItems(O) .T ext. Cel l Origin. True)
ProgressBarl.Value = 80
myMSApp.ActiveModelReference.AddElement(myCellElem)
ProgressBarl.Va l ue = 100
ProgressBarl . Visible = False
End Sub
Private Sub Li nkLabe ll _LinkCl i cked(ByVal sender _
As System.Object. ByVal e As _
System.Windows.Forms.LinkLabelLinkClickedEventArgs) _
Handles LinkLabell . LinkClicked
Dim xSplit As String()
Dim strWebsite As String

I You can do this in VB.NET! I

859

xSp lit = Spl it(Text Boxl.Text , vbCrLf)


For Each strWebsite In xSpl it
If strW ebsite.StartsW i th( "Websit e Address: " ) = True Then
Dim a As ProcessStartInfo = New ProcessStartInfo(
Mid(strWebsite, InStr(strWebsite, ": ") + 2))
Process.Start(a)
End If
Next
End Sub
Pr i vate Sub Forml_Resize(ByVa l sender As Object,
ByVal e As System . EventArgs) Handles Me. Resize
I f Fi xedH ei ght = a Th en
FixedHeight = Me.Height
Else
Me.Height = FixedHeight
End If
End Sub
End Class

Code is placed into five events which are triggered by either the
application starting or by user interaction. Let's discuss each of these
events and what they are accomplishing.

Private Sub Forml_Load


This application makes use of a ListView control. This control allows us
to display images and descriptions in a list. ListView controls use
ImageList controls to hold the images that are to be displayed inside the
ListView control. We look in the folder in which the Application resides
(the .exe file) for any Windows Bitmap (.bmp) files. Each Bitmap file
represents a Cell in MicroStation. These Bitmap files can be 'CAD
drawn' or can be actual photographs or illustrations of the object the
Cell represents. We create a thumbnail in memory for each bitmap by
using the GetThumbNai 1Image method of the Image object. Although this
same functionality could be duplicated in VBA, VB.NET makes this step
very easy. After adding each thumbnail to the ImageList, we add an item
to the ListView using the thumbnail image just 'created' and the
description of the Cell.

Private Sub IstvCells_ltemSelectionChanged


Each time the user changes the selection in the ListView, we look to see
if an .info file is available for the selected bitmap. If we find an .info file,
we populate the TextBox with the contents of the .info file. If we find a

860

I Chapter 39: Using VB.NET I


line in the .info file that contains "Website Address:", we make the
LinkLabel control visible so it can be clicked.

Private Sub IstvCells_DoubleClick


When the user double-clicks on the ListView, we insert the doubleclicked cell into MicroStation. There are numerous ways we can
determine the cell's origin. In our example, we place the cell in the center
of the current view in MicroStation. A ProgressBar displays the progress
of the code as it runs.

Private Sub linkLabell linkClicked


The only way to click on the LinkLabel is for it to be visible. It is only
visible if "Website Address:" is in the .info file of the selected cell. When
the LinkLabel is clicked, we get the "Website Address" in the .info file
and start a new process using the website address. Pro ceS S . Start begins
a new process (starts an application) and opens the provided file in the
application. Process .St art is equivalent to the She llE xecute Windows
API call we discussed in a previous chapter.

Private Sub Form 1 Resize


User Forms in VB6 and VB. NET can be resized. In our example we only
want the form to be able to be resized in its width. So, we use a Variable
named FixedHeight when the form is initially created (the Resize event
is triggered as well as the Load event when a Form is displayed) and
continue to use this variable for the Form's Height any time the Form is
resized.
As we review the code shown above or if this project is opened in Visual
Studio, we will find that something is missing. What is it? We do not
have any error handling. As a standalone .EXE file, it is possible
someone could open the program without MicroStation running first.
As we look through the code we can see that the only place where this
may be a problem is in the lstvCells_DoubleClick event. Let's add a little
error handling the VB.NET way. It is a little different than what we have
used in VB6 and VBA.
Private Sub lstvCells_Doub l eClick(ByVal sender As Object. _
SyVa l e As System . EventArgs) Handles lstvCells . DoubleClick
Di m myMSAppCon As MicroStat i onDG N. Appl i cationObjectConnector
Dim myMSApp As Mi croStationDGN.Application

I You can do this in VB.NET! I

861

Dim myCe l lElem As MicroStationDGN . CellE l ement


Di m Cel l Origin As Mi croStationDGN . Po i nt3d
ProgressBar1 . Visible = True
ProgressBar1.Value = 10
Try
myMSAppCon = GetObject(, _
"MicroStationDGN . ApplicationObjectConnector")
ProgressBar1.Value = 20
myMSApp = myMSAppCon.Application
Pr ogress Ba r1.Value = 40
Cel l Origin = myMSApp . Co mm andState . LastV i ew . Center
Prog ress Bar 1 .Va lu e = 60
myCellElem = myMSApp . CreateCellElement3( _
lstvCells.Se l ectedltems(O).Text, CellOr i gi n , Tr ue)
ProgressBar1.Va l ue = 80
myMSApp.ActiveModelReference.AddElement(myCellElem)
ProgressBar1.Value = 100
Catch ex As Exception
Se l ect Case Er r . Number
Case 429 'MicroStation not started
MsgBox("MicroStation is not sta rt ed." & vb Cr &
"Please s tart Mi c roSta t i on and try aga i n ." )
ProgressBar1.Vi s i bl e = Fal se
Ex it Sub
Case Else
MsgBox(ex.Message & vb Cr & eX.S t ac kTra ce, , "E RROR " )
Pr ogressBarl.Visibl e = Fa l se
Ex it Sub
End Se l ect
End Try
ProgressBarl.Visible
En d Sub

False

When we perform error checking in VB.NET, we anticipate that a line of


code may cause an error. We Try the line or lines of code after which we
Catch the error or errors that may arise. We will see a few additional
examples of this as we continue.

862

I Chapter 39: Using VB.NET I


A

DGN BROWSER ApPLICATION

Our next application is also a


Windows Application. It is
titled DGN Browser. It makes
use of a Tree View control, a
CommandButton, a TextBox, a
Folder Browser Dialog, a
NotifyIcon, an Image List, and
a ToolTip Control. Let's take a
look at the GUI when it is
being run.

IC'Documents and SettingslAll UserslApplication D I~

JI4.

BSI3OUu,El Ol-Plan dgn

EJ~ Levels

,,0

AF6G-BldgExti
A-F8-G-BldgMisc
:0 A-G251-G-WaIlExtl
, 0 A-ZOOO-D-Dim
A-2000-G-Anno
A-ZOO1-G-lden
A-2011-G-TitI
A-2013-G -Legn
Default
. 0
Frame
j ... r::;;> links
' ...e> Object
Models
:
i(il Composite Cut Ground Floor Plan
:
cO Ground Floor Plan
$ . pt BSI300AE201-Elevations.dgn
EJ @ Levels
j .. ..
A-F44-G-SubsNich
.

t,?

.. =
=

i am
Users browse to a folder they
want to view. Each .dgn file is
loaded into the TreeView
=
control and each .dgn file is
opened in MicroStation "For
Program". Levels and Models are extracted and displayed under the
"Levels" and "Models" icons under each design file. The 3D and 2D
models are distinguished by their icons.

Once again, MicroStation must be running for this program to work


correctly. If it is not running, we don't want our program to blow up, so
we simply show the .dgn files in the Tree View but Levels and Models are
not shown.
L~~~~~~~~~.._~~~~~~_!~J~
,14 BSI300AE 10l-Plan.dgn
:14 BSI300AE201 -Elevations.dgn
Here's what it looks '
like when
MicroStation is not
running:

. .p.
:. 14

....~

'...p.
'. p.
, .~
,. ~

: 14
, .. ~

: It

, 14
' 14
. . It

BSI300AE301-S ections.dgn
BSI300AE501-Details.dgn
BSI300AE701-RCPlan.dgn
BSI300AE9-Atrium.dgn
BSI300AE9-Core.dgn
BSI300AE9-Shell.dgn
BSI300C-9-Site.dgn
BS1300G1001-CoveLdgn
BSl300Gl9-3DMasteLdgn
B513001-9-1 nteriOL dgn
BSI300S-9-Atrium.dgn
BSI300S-9-Structural.dgn
BS!30(!><-9-Sign.dgn

I A DGN Browser Application I

863

Let's imagine that we have built this application


and we are so pleased with it that we are sure
users will want it available with the quick click
of a mouse. How can we make our program this
accessible? One way is to place it in the
Windows Status Notification Area of the Task
Bar.
How difficult is this to accomplish in VB.NET? Simply add a
"NotifyIcon" Control to the Form and it displays in the TaskBar. Of
course, we could not let this wonderful program show up with a
standard VB.NET icon so we use the MicroStation V8 XM icon.
So, to review:
1

The program is launched by double-clicking on the icon in the


Windows Status Notification area.

We select a folder by clicking the Browse button. The program then


finds all .dgn files in the selected folder and displays the file name in
a TreeView.

If MicroStation is open, we use GetObject to attach to MicroStation


and open each file in the list "For Program" and add the Levels and
Models to the Tree View control.

Simple enough? It truly is when working in VB.NET.


Here's the code:
Publ i c Cl ass Forml
Private LastPath As String
Private myMSApp As MicroStationDGN.Application
Private myMSAppCon As MicroStationDGN . ApplicationObjectConnector
Private MicroStationError As Boolean
Private MicroStationOpen As Boolean
Private Sub Notifylconl_MouseDoubleClick(ByVal
sender As System.Object. _
ByVal e As System.Windows . Forms.MouseEventArgs) _
Handles Notifylconl.MouseDo ubleCl ick
Me.WindowState
End Sub

FormWindowState.Normal

Private Sub Buttonl_Click(ByVal sender As System.Object. _


ByVal e As System . EventArgs) Handles Buttonl.Click
FBDl .S electedPath

LastPath

864

I Chapter 39: Using VB.N ET I


FB D1.Des cr ipt io n = "DG N Explo r er Pat h: "
FBD1 .S howD i al og()
TextBoxl. Text = FBD1.SelectedPath
DisplayDGNs(FBD1.SelectedPath)
End Sub
Private Sub DisplayDGNs(ByVal PathIn As String)
Dim myDI As IO.DirectoryInfo
Dim myFI As IO.F i leInfo
Dim myNode As TreeNode
Dim FileCounter As Long
tvl.Nodes.Clear()
myD I = New IO. DirectoryInfo(Path I n)
If Not my DI . Ex i s t s The n
MsgBox( "The path " & PathIn & " does not ex i st. " , _
MsgBoxStyle.Critical)
Tex tBox l. Tex t
Exit Sub
End If
LastPath = PathIn
ToolTipl.SetToolTip(TextBoxl, PathIn)
FileCounter = 0
ProgressBar1.Visible = True
For Each myFI In myDI.GetFiles
FileCounter = FileCo unter + 1
ProgressBarl.Value = FileCounter * 10
ProgressBarl.Refresh()
Select Case myFI.Extensio n.ToUpper
Case ". DGN "
myNode = tvl.Nodes.Add( my FI.Name)
myNode.ImageIndex = 0
myNode.SelectedImageIndex = 0
If MicroSta t i onErr or = Fa l se Then
GetFileComps(myFI.FullName, myNode)
End If
End Se l ect
If Fi l eCounter = 10 Then FileCounter
0
Next
ProgressBarl.Visible = False
tvl.Sort()
End Sub
Private Sub Forml_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
LastPath
End Sub

"C: \MicroStat i on VBA "

Private Sub GetFileComps(ByVal FileIn As String , _


ByVal FileNode As TreeNode)
Dim myLevel As MicroStationDGN.Level
Dim myModel As MicroStationDGN.ModelRefere nce

I A DGN Browser Application I

865

Dim LevelN ode As Tree Node


Dim ModelN ode As Tree Node
Dim tmpNode As TreeNode
If Mi cr oStat i onOpen = Fa se Then
Try
~yMSAppCon = GetObject(. _
"MicroStationDGN.ApplicationObjectConnector")
myMSApp = myMSAppCon.Application
Catch
MicroStationError = True
Exit Sub
End Try
Dim DGNFile As MicroStationOGN.DesignFile
Try
OGNF il e = myMSApp . OpenOesignFi l eForProgram( _
File In. True)
Catch
FileNode.F oreCo l or
Color . Red
Exit Sub
End Try
Leve lN ode = Fil eNode.Nodes.Add( "Leve l s " )
Leve lN ode .ImageI ndex = 1
Leve l Node.Se l ected Im age I ndex = 1
Mode l Node = Fi l eNode.Nodes.Add( "Models " )
Mode lN ode.I mage I ndex = 3
ModelNode . SelectedImageIndex = 3
For Eac h myLevel I n OGNF il e.Leve l s
tmpNode = Level Node . Nodes . Add( my Level.Name )
tmpNode.ImageIndex = 2
t mp Node . Se l ectedImage I ndex = 2
Next
For Eac h myModel In DGNFile. Models
tm pNode = Model Node. Nodes . Add(myModel . Name)
If myModel. Is3 0 Then
tmpNode . ImageIndex = 4
tmpNode . SelectedImageIndex
4
El se
tm pNode.ImageIndex = 5
5
tmpNode.Se lec tedImageIndex
End If
Next
OGNFi l e
Nothing
End If
End Sub
End Class

The ImageList Icons are located on the CD that accompanies this book.
Are there things we could do to make this program even more
powerful? Of course. We could automatically open a file when it is
double-clicked in the Tree. We could allow the user to drag and drop

866

I Chapter 39: Using VB.NET I


folders from Windows Explorer into our DGN Explorer. We could store
the most recently selected folders in a ComboBox instead of using a
TextBox to display the selected path. We could have the option of
including subfolders when a folder is selected. There are many things we
could do to this project but we are not going to do them here. We will
leave embellishments up to the reader.

VBA TO VB.NET REFERENCE


One question that is often asked is, "What programming language
should I be learning? VBA? VB6? VB.NET?" That is a good question.
And if you ask this question to 100 different experts, you will hear a 100
different answers. We discussed some of the reasons why you would
want to develop in VBA versus VB6 or VB.NET previously. If there are
clear distinct benefits to developing in VBA, then of course, specializing
in VBA makes sense. And if VBA is the primary development
environment, then VB6 is a much more natural transition than VB. NET.
However, as they say, "The writing is on the wall': VB6 is going away and
VB.NET is the future, so, it would be a good idea for us to get familiar
with the .NET environment and how things are done in VB.NET. And as
for VBA, Microsoft has developed a .NET-like VBA environment for
applications, so it will likely be adopted in the coming years.
This section of this chapter is not meant to be a comprehensive listing of
VBA to VB. NET calls. Rather it aims to provide information on how
some of the common things we have explained in this book are being
done in VB.NET. See the Other References section in this book for more
information on the VB.NET environment.

Everything is an Object
When developing in VBA or VB6, we can declare a variable as a String.
In VBA and VB6, a String is a data type, not an Object. In VB.NET, a
String is an Object with its own properties and methods. For example,

IVBA to VB.NET Reference I

867

when we type the name of a variable declared as a String and press the
period key, we see:
..

to.

~=

r-=

,,-.,.."'~=.~.-.."'-.-, .... Substring


... ToChar Array

F;;"'1.~b[DeSignl; ''1 ToLower


-

. -.~-.-------".-

i~

ser

ToLowerInvariant

.=_=== v ToStr ing


: lass Formi

v i

Public Function ToUpperO As String (+ 1 overloads)


Returns a copy of this System ,String conver ted to uppercase, using the casing rules of the current culture .

'\.j

.., ToUpperlnvariant

~lat.e Sub But ' ~ T r im

'ci nder

ByVal e .~ TrimEnd
Dim myName
''I TrimStart
myN ame =

PI

Common

: ..i

../

s)

As System. Object,
Handles B u ttoni. Click

All

IIsaBox (mvName .

As we can see here, we can use the ToU ppe r method of a String Object
instead of using UCas e (see below).
Ucase( my Name) ' VB6 and VBA
myString.ToUpper VB . NET

To Upper is a method of the String Object. And how about the String

Object's properties? Yes, there are Properties as well. In VB.NET, we use


the Length property of a String Object instead of using the Le n function
(as we do in VB6 and VBA).

Overloaded
That word brings vivid pictures to mind. In VB.NET it means that a
single procedure, method, function , etc., can have more than one
implementation, each with its own set of unique Parameters. Here's an
example using the FileInfo Object's Open method:
... 1 of 3 ~ Open (mode As 5ystem.IO.FileMode) As System.IO,FileStream
: mode: A System,IO.Filer~ode constant specifying the mode (for example, Open or Append ) in which to open the file,

.., 2 of 3:r Open (mode As System.IO,Filer'iode, access As System ,IO,FileAccess) As System,IO.FileStream


: Opens a file in the specified mode with read, write, or read/write access .

... 3 of 3 .... Open (mode As System.IO,File~iode, access As System, IO, FileAccess, share As System.IO,FileShare) As System,IO, FileStream
: Opens a file in the specified mode with read, write, or read/write access and the specified sharing option,

When we attempt to use the Open method of the FileInfo object, we


initially see the Intellisense hint as being "1 of 3". We can step through
each of the overloaded methods by using the arrow keys or by clicking
on the Up and Down arrows shown in the tooltip. These methods make

868

I Chapter 39: Using VB.NET I


use of varying numbers and types of parameters but all accomplish the
same thing - they open a file as a FileStream.
Not only are standard VB.NET functions, procedures, etc., overloaded,
but we can create our own overloaded functions as well. This comes in
very handy because if we need the same function to be used with
different parameters, we can overload it instead of adding a constantly
growing number of optional parameters to the function.

Procedures and Functions


In VB.NET, parenthesis are placed around Procedure calls as well as
Function calls. One example of this is the MessageBox.
In VBA and VB6:
MsgBox "This is a test."

In VB.NET:
MsgBox("This is a test.")

Accessing ASCII Files


Reading and Writing ASCII files is easy to do in VBA and VB6. It is also
easy to do in VB.NET.

Reading Files in VBA


Sub ReadFileVBA()
Di m FF ile As Long
Dim strL in e As String
FFile = FreeFile
Open "C:\MicroStation VBA\points.txt" For Input As #FFile
While EOF(FFile) = False
Line Input #FFile, strLine
Debug.Print strLine
Wend
Close #FFile
End Sub

Reading Files in VB.NET


Sub ReadFi 1eA()
Dim myFS As New IO.StreamReader(
"C:\MicroStation VBA\points.txt " )
Whi le myFS.EndOfStream = False
Debug.Print(myFS.ReadLine)
End While

I VBA to VB.NET Reference I

869

my FS . Clo s e ( )
End Sub

Sub ReadFi 1 eB()


Dim myFS As New IO.StreamReader(
"C:\MicroStation VBA\points.txt")
Debug.Print(myFS.ReadToEnd)
my FS . Cl os e ( )
End Sub

In VB.NET we can read a file one line at a time just as we do in VBA. We


also have the option to ReadToEnd. When we use the ReadToEnd method,
we are returned the entire file at once in one String.

Writing Files in VBA


Sub WriteFileVBA ()
Dim FFile As Long
Dim strLine As String
FFile = Fre eFi l e
Open "C: \MicroStat i on VBA\points2.txt " _
For Output As #FFile
Pr i nt #FFi l e, " 1,1,0,1"
Print #FFile, "2 , 2 , 0 , 2"
Print #FFile, "3 , 3 , 0,3 "
Print #FFile, "4, 4,0 ,4"
Cl ose #FFile
End Sub

Writing Files in VB.NET


Sub Wri teF i 1 eA ()
Dim myFW As New IO.StreamWriter (
"C: \M icroStation VBA \ points2.txt")
my FW. Writ eLi ne ( " 1 , 1 , 0 , 1 " )
myFW.WriteLine("2,2,O,2")
my FW.WriteLine("3,3,O,3")
myFW.WriteLine ( "4,4,O,4")
myFW . Close()
End Sub

Writing files in VB.NET is very similar to writing files in VBA. We write


one line at a time.

E-mailinginVB.NET
For an example of e-mailing using VBA, refer to previous chapters
where this was discussed. As for VB.NET, here are a couple of examples:
Sub SendMa i 1A( )

870

I Chapter 39: Using VB.NET I


Dim mySMTP As New System . Net.Mail . SmtpClient
mySMTP.Host

"yoursmtpserver.com "
"you@bentley.com", _
"Mi croStat i on VBA" , "Email i ng using . NET is easy ." )
End Sub
=

mySMTP.Send( " ~e@bentley . com " ,

Sub SendMai 1B()


Dim mySMTP As New Sy ste m.Net.Mail. Smt pCli ent
Dim myMessage As New System.Net.Mail .MailMessage
Dim myAttachment As New Syste m.Net.Mail.Attachment( _
"C: \ Mi c r oSt at i on VB A\po int s . tx t" )
mySMTP.Host = "yoursmtpserver . com "
myMessage .To . Add( "you@bentley.com " )
myMessage.From = New _
Sy stem. Net. Ma i 1 . Mai l Ad d res s ( "me@be nt 1ey . com " )
myMessage.Subject = "Emailing an Attachment"
myMessage . Body = "( b>HTML( / b> Ema il i s also easy ."
my Mes sa ge.IsBody Ht ml = True
myMes sage . Atta chme nt s . Add(myAt ta chm en t )
myS MTP. Se nd(my Message)
End Sub

The first example, SendMa i 1A shows us that with only three lines of code,
we can send an e-mail. Se ndM ai 1B does things a little differently.
Se ndMa i 1B adds an Attachment to the e-mail that is being sent. Of
course, the SMTP.Host, From, and To fields in each of these examples
need to be modified to reflect legitimate e-mail server and mailbox
settings.

Traversing a Folder and its Subfolders


We have seen already how this can be done using the File System Object
in VBA. Let's take a look at how this is accomplished in VB.NET. The
first example gets all of the subdirectories of a specified directory:
Sub TraverseFolders ()
Dim myDI As New IO.Directo ryI nfo( "C: \M i croStation VBA")
Dim mySubD I As IO.DirectoryInfo
For Each mySubDI In myDI.GetDirectories( "*.* ", _
IO.SearchOption.AllDirectories)
Deb u9 . Pr i nt( my Sub DI . Fu1 1 Name )
Next
End Sub

GetDi rectori es has three different implementations. The one shown


here allows us to specify a pattern to look for as well as the ability to
specify ''AllDirectories'' or "TopDirectoryOnly':

I VBA to VB.N ET Reference I

871

In VBA, we use a recursive procedure to get to the subfolders. W hen we


use VB.NET, we can get all folders with a single line of code.

c: \lIicroStation
C: \IUcroStation
:\Hi croStation
:\lIicroStacion
: \ Ui croScation
:\MicroStation
:\Micr oStat ion
: \ Micr oStati on

VBA\cd material
VE A\ docs
VBA\Documents
VBA\Fonts
VBi\. \ from mark
VBA \ FTP i n VB Doc Net
VBA\pics
VBA\Source Code

:\HicroStation
: \lIicroStation
:\HicroStation
: \liicroStation
:\Hicr oS tation
: \HicroStation
: \HicroStation
:\HicroStation
:\HicroStation
C: \HicroStation

VBA\USGS
VBA \ VB6
VBA\FTP in
VBA \ PTP in
VBA \ FTP in
VBA \ FTP in
VBA\FTP in
VBA\FTP in
VBA\FTP in
VBA\FTP in

VB Doe ?-let\CS
VB Dot Net \ VB

VB
VB
VB
VB
VB
VB

Dot
Dot
Dot
Dot
Dot
Dot

Net\VB\FtpSample
Net \ VB\ FtpSample\bin
l~et\VB\FtpS ....ple\obj
Net\VB\FtpSample\obj\Debuq
Net\VB\FtpSample\obj\Debuq\TempPE
Ilet\CS\FTPSample

Getting All Files in a path


Just as VB. NET makes it easy to traverse the folders under a specified
folder, traversing files under a specified folder (and its subfolders) is also
done with only a few lines of code:
Sub FindFilesA ()
Dim myDI As New IO .Di rectoryIn f o( "C: \ MicroStati on VBA " )
Di m my FI As IO. Fil eInfo
For Each myF I In myDI.G etFiles( "* . DGN " .
IO.SearchOption.AllDirectories)
Debug.Print(myFI.FullName)
Next
End Sub

872

I Chapter 39: Using VB.NET I


In this example, we get all .dgn files beginning in the C: \M icroStation
VEA path and its subdirectories.
C:\HicroStation
C:\HicroStation
C:\HicroStation
C: \MicroStation
C:\MicroStation
C:\MicroStation
C:\MicroStation
C:\HicroStation
C:\MicroStation
C:\MicroStation
C:\MicroStation
C:\HicroStation
c: \llicroStstion
c: \IIicroStation
C: \HicroStation
C:\HicroStation
C:\HicroStation
C:\MicroStation
C:\MicroStation
C:\HicroStation

VBA\BatchProcessinq\File R.dgn
VBA\BatchProcessinq\BatchD\File W.dgn
VBA\ BatchProcessing\BatchD \ File X.dgn
VBA\BatchProcessing\BatchD\File Y.dgn
VBA\BatchProcessing\BatchD\File Z.dgn
VBA\BatchProcessing\BatchC\File U.dgn
VBA\BatchProcessing\BatchC\File V.dgn
VBA\BatchProcessing\BatchB\File R.dgn
VBA\BatchProcessing\BatchB\File S.dgn
VBA\BatchProcessing\BatchB\File T.dgn
VBA\BatchProcessing\BatchA\Pile F.dgn
VBA\BstchProcessing\BatchA\File G.dgn
VBA\BatchProcessing\Batc:hA,\File H. dgn
VBA \BatchProcessing\BatchA\ File J. dgn
VBA\BatchProcessing\BatchA\Batc:hA-3\File
VBA\BatchProcessing\BatchA\BatchA-3\File
VBA\BatchProcessinq\Batc:hA\BatchA-2\File
VBA\ BatchPr ocess ing\BatchA\BatchA- 2\ File
VBA\BatchProcessing\ BatchA\BatchA-l\File
VBA\BatchProcess i nq\Bat chA\Batc:hA- l \Fi l e

P.dqn
Q.dgn
M.dgn
N.d~l

K . dgn
L.dgn

!f
tYl
c>}

Returning Function Values


VB.NET requires us to use the 'Return' statement when a function needs
to return a value. This is different than VBA and VB6 when we use the
name of the function, then the equal sign, then the value or object that is
to be returned.

Windows API Calls


The .NET Framework exposes many obj ects with methods and
properties that previously had been the exclusive realm of Windows API
calls. For example, accessing the entire Windows Registry was a
somewhat tedious process. Of course, we could create our own classes
that wrapped up the code for accessing the Registry. In VB.NET,
accessing the Registry is much easier, though. But not all Windows API
calls are placed at our fingertips as we will see in our next example.
When we look at Windows Explorer, files are shown with their names
and then with an icon. The icon normally reflects the application that is
used to open or edit the file. There are several places in the Registry
where the "DefaultIcon" property of a specific file type is found. In our
next example, we are going to look at each file extension in a folder,
attempt to find the default icon that is used for each file extension in the
Registry, and then save the icon out as a Bitmap (.bmp) file. To

I VBA to VB.NET Reference I

873

accomplish this we will use two Windows API calls as well as the built-in
Registry access objects in .NET.
First, here are the Windows API calls declared just below the "Public
Class" statement in VB.NET:
Declare Function ExtractIcon Li b "shel13Z . dll " Alias
"ExtractIconA " (ByVal hInst As Integer . _
ByVal lps zExe Fi leName As Str i ng . _
ByVal nI con I nd ex As Integer) As I nteger
Pub l ic Declare Function FindExecutable Lib "shel13Z.dll " Alias
"FindExecutab l eA" ( ByVal lpF i le As Stri ng. _
By Val l pD ir ecto ry As String . ByVa l l pRes ult As Str in g) As Long

The first call, Extr ac tI con extracts a specific icon from an .exe or .dll
file. Fin dEx ecutab le allows us to specify a file name and it returns the
path to the program registered to open the file. We will see these used in
a little while.
Let's look at the main Procedure that kicks things off.
1

We begin by getting a Directorylnfo Object that is pointing to the


path C:\MicroStation VEA.

2 When we look at each file in this folder, we check to see if we have


already created a Bitmap file for the file extension of the file we are
looking at. If we have not already created a Bitmap file for the file
extension, we begin the process of identifying the icon file to use to
create the Bitmap.

3 Our first attempt at identifying the icon is looking in the Windows


Registry based on the File Extension. If this fails, we get the Icon
from the registered application of the file. If this fails, we get the
default Windows icon (which comes from the shell32.dll file).
4

When we successfully obtain an Icon, we save the file as a Bitmap.


Then, later in the code, we place the Icon in a PictureBox control
and then save it out to a file . The reason we use the PictureBox
control is to maintain some of the transparency information found
in the icon file.

Sub SaveFi l elcons ()


Di m myD I As New IO . Di r ectoryInfo( "C: \M i croStat i on VBA " )
Dim myFI As IO. Filelnfo
Dim IconFile As IO. Fi le l nf o
Dim myl con As Bitma p

874

I Chapter 39: Using VB.NET I


Dim tmp lmage As Image
Dim myP i ctu reBox As New PictureB ox
For Each myFI In myDI.GetFiles
IconFile = New IO.Filelnfo(myDI.FullName & .\. &
myFI . Extension . Replace( . . ) & .bmp )
If IconFi l e . Exists = Fa l se Then
' First look at extension
my l con = IconF rom Ext ensi on(my FI .E xte nsion)
I f my l con Is Not hin g = Fal se Th en
myl con.Save ( c: \ MicroStation VBA\ & _
myF I .Extension.Rep l ace( . . ) & .bm p. _
Sys t em. Drawing. lm agi ng.lmageFormat . Bmp)
Else
'N ow look at fi l e
mylcon = IconFromFile(myFI.FullName)
If mylcon Is Noth in g = False Then
mylcon . Save( c : \MicroStat i on VBA\ &
myFI.Extension.Replace(. . ) & . bmp . _
System . Draw i ng .lmag i ng.I mage Format . Bmp )
Else
'Us e D ~ f au l t Win dows Icon
my l con = GetDe fWinl con()
my l con . Save( c : \Mic roStat i on VBA\ &
myFI.Extens i on.Rep l ace( . . ) & . bmp . _
System.Drawing.lmaging . lmage Format . Bmp)
End If
End I f
If myIcon I s Nothi ng = Fal se Then
myPic t ureBox .l mage = myIcon
tm plm age = myP i ct ureBox .l mage
tmp Im age . Save( C: \MicroStation VBA\ &
my FI.E xte ns i on .Rep lace( . . ) & .a bm p )
End If
End If
Next
End Sub

Now, for the Functions I con FromExtens i on, I conFromFi 1e, and
GetDefWi nI can. Each of these functions use another function,
I ca nFromV a1ue. which parses the results of the Def au It I con value when it
is retrieved from the Registry.
Function IconFromExtension(ByVal FileExtension As String)_
As Bitmap
Dim MyKey As Microsoft .W in32 . RegistryKey
Dim MyDefaultKey As Microsoft.Win32.RegistryKey
Dim myCR As Microsoft.Win32.RegistryKey
Dim myDefaultlcon As Microsoft.Win32.RegistryKey
Dim DefVal ue As String
myCR = Microsoft .Wi n32 . Regis t ry . ClassesRoot
MyKey = myCR.OpenSub Key(Fi l eExtension)

I VBA to VB.NET Reference I

875

If MyKey Is Nothing Then


Return Nothing
Exit Function
End If
'First look for Va l ue
DefValue = MyKey.GetValue("Defaultlcon")
If DefValue Is Nothing = False Then
Return IconFromValue (DefValue)
Exit Func t ion
End If
'Next look for Key
DefValue = MyKey.GetValue("")
If DefValue Is Nothing = False Then
MyDefaultKey = myCR.OpenSubKey(DefValue)
If My Defau ltKey Is Nothing = Fa lse Then
' Look for Key
myDefault lcon = MyDefaultKey.OpenSubKey("Defaultlcon")
If myDefault lco n Is Nothing = False Then
Return
IconFromValue(myDefault l con.GetValue(""))
Exit Funct ion
El se
'Lo ok for Value
Retur n
IconFromValue (MyDefaultKey.GetValue("Defaultlcon"))
Exit Function
End If
End If
End If
Return Nothing
End Function
Function IconFromFil e(ByVa l Fi leln As String) As Bitmap
Dim myDI As 10.Directorylnfo
Dim my FI As New 10.Filelnfo(Fileln)
Dim myExe As String
Dim nlcon As Long
Dim t mp Bitmap As Bitmap
myDI = New 10.Directorylnfo(myFI.Direc toryNa me)
myExe = Space(Z55)
FindExecutab l e(myFI.Name. myDI.FullName. myExe)
nIcon = Extractlcon(O. myExe.Substring(O, _
myExe.IndexOf(Chr(O))), 0)
If nlcon > 0 Then
tmpBitmap = Bitmap.FromHicon(nlcon)
Return tmpB i tmap
E1s e
Return Nothing
End I f
End Fu nction
Function GetDefWinlcon() As Bitmap

876

I Chapter 39: Usi ng VB.N ET I


Di m nIcon As Long
Dim t mpBitma p As Bit map
nIcon = ExtractIcon(O, "C:\Windows\System3Z\shel 13Z . dll " , 0)
tmpBitmap = Bitmap.FromHicor(nIcon)
Return tmpBitmap
End Function
Function IconFro mVal ue(ByVal ValueIn As String) As Bitmap
Dim xSpl it() As String
Dim tmpBitmap As Bitmap
Dim nIcon As Long
tmpBitmap = Nothing
xSplit = Split(ValueIn, ",H)
I f xSp li t(O) = " " Then
Ret urn Nothing
Exit Fu ncti on
En d If
Select Case xSp l it.Le ngt h
Case 1 '.ico fi l e
If xSplit(O).Contains( "%" ) = False Then
If xSplit(O) <> "" Then
tmpB i tmap = Bitmap.FromFile(xSpli t (O) )
End If
End If
Case Z '.exe or .dl l file
nIcon = ExtractIcon(O, xSp l it(O), xSplit ( l))
If nIcon > 0 Then
tmpBitmap = Bitmap.FromHicon(nIcon)
End If
Case Else
Return Nothing
End Select
Return tmpBitmap
En d Functio n

dgn ,bmp

dgna ,bmp

doc.bmp

doca,bmp

gz ,bmp

gza,bmp

htm,bmp

htma ,bmp

When this program is run , Bitmap files are created for each unique file
extension found in the specified folder.

I Distributing VB.NET Applications I


DISTRIBUTING

877

VB.NET ApPLICATIONS

Distributing a VB.NET Application is a matter of a few mouse clicks


because it is built into VB.NET.
Build

1 Debug

Build VBA To VB.NET

Data

Tools

Win

Rebuild VBA To VB. NET

11 Clean VBA To VB. NET


1.

Publ~~ V.B~TOVS.NET ~

~ Configuration Manager ...

Use Build > Publish XXXXXXX to begin the 'publishing' process.

Where do you want to publish the application?

Specify the location to publish this application:

~icroStation VBA\VBA To VB.NET

_______

-.-1 [ Browse ...

You may publish the application to a web site, FTP server, or file path.
Examples :
Disk path:

c:\deploy\myapplication

File share:
FTP server:

\\server\myapplication
ftp:/Iftp .microsoft.comimyapplication

Web site:

http://www.microsoft .comimyapplication

Next

> .~ [

Finish

II

Cancel

878

I Chapter 39: Using VB.NET I


We are asked where to publish our application. In addition to
publishing to the hard drive, we can publish to an FTP server or
Web server.
.. r:c'

-<"'~g""

3"~'~

Publish Wizard

,,''''''>~''~.

'

",

__ ,

"""'''~.~'''',_,v->"".~

"

'

"

-'

_~_~"'~'<_"'''T'

, ' , :"

__

~.;Y''fi'Y7

~~

"

How will users install the application?

o From a Web site


Specify the URL<

' ,--lh-:-ttP_~f_ll;_)C_dn_o_$t_l"_'BA_T_Q_\I_B'_Nf_'T~l~_ _ _ _ _~_ _ _...,-_--,--:--:---,I. [~~~~::~]


qFrom a UNCp~th orfile sh~re;

In our example, we will create a Setup that is to be run from CDROM or DVD-ROM.

Where will the application check for updates?

,0

The application will check for ~pdates fromthe following.location:

e'rhe application will not check fo~ ~pdates

< Previous

Ii

Next>

Finish

I[

Cancel

I Distributing VB.NET Applications I

879

To simplify things, we will not use the Update feature.

Ready to Publish!
The wizard will now publish the application based on your choices .

~he application will be published to:


file :/I/C: /MicroStation%20VBANBA %20T0%20VB .NET /
When this application is installed on the client machine, a shortcut will be added to the Start Menu, and the
application can be uninstalled via Add/Remove Programs.

-----_._----_.

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

< Previous

Cancel

3 Clicking Finish causes the Publish Wizard to create the setup file.
lt takes a few moments to create the Setup file. "Publish building" is
shown in the status bar of the VB.NET IDE with an animated icon.

Here are the files generated by the Publishing Wizard.


Name ..
DVBA To

VB. ~jET ~I~~O_O

~~:~:~:~:p.:;:~:~:~:::1

UVBA To VB. NET. application


UVBA To VB.NET _1_O_O_O.application

Si.2: _ ..:ype_ ..
File Folder
422 KB Application
6 KB Application Manifest
6 KB Application Manifest

880

I Chapter 39: Using VB.NET I


REVIEW
The specific API calls used to control MicroStation using VB.NET are
not different in any way when compared with Microsoft Excel or VB6.
We still add a Reference to the "Bentley MicroStation DGN #.# Library".
We still use GetObj ect. The are other differences between VB.NET and
VB6/VBA. These differences often result in greatly simplifying file and
folder access as well as other areas of programming that had previously
been difficult and tedious. There is little question that VB.NET is the
future of VB programming. Although we can continue to develop
powerful applications in MicroStation's VBA environment, it is a good
idea to become familiar with the .NET world.

Additional Sources
GENERAL VBA RESOURCES
http://msdn.microsoft.com/vba
VBA Overview, Whitepapers, etc.
http://bentleyinstitute.bentley.com/catalog.aspx?discipline= 10
Programming Classes offered by Bentley
Mastering Microsoft VBA; ISBN: 0782144365
VBA Developer's Handbook; ISBN: 0782129781
Google, Yahoo, or other Internet Search for "VBA" or "Visual Basic for
Applications"
http://discussion.bentley.com
Look for the bentley.microstation.v8xm.vba discussion group.
VB and VBA in a Nutshell; ISBN: 1565923588

SQL STATEMENTS
http://msdn.microsoft.com/library/en-us/tsqlreflts sases 9sfo.asp?frame=true
SQL Statement Explanations and Examples from Microsoft

881

882

I Additional Sources I

VB.NET
http://msdn.microsoft.com/vbasicl
Visual Basic 2005 Programmer's
Programmer); ISBN: 0764571982

Reference

MATHCAD
www.mathcad.com

XML
XML in a Nutshell, Third Edition, ISBN: 0596007647
XML Programming Bible, ISBN: 0764538292

(Programmer

to

Index
& (ampersand), 100
, (apostrophe), 6
* (asterisk symbol), 105-106,736
\ backslash symbol, 106
1\ (carat symbol), 106
: (colon symbol), 291
I (forward slash), 106
- minus symbol, 105
I (pipe symbol), 729
+ (plus symbol), 104
.II (selectNodes), 592

A
About Microsoft Visual Basic, 21
Abs function, 110-1 11
Absolute Value, 110-111
Accept event, 399
Access, Microsoft, 554-557
accessors, 193
Action parameter, 548
active design file, 95
Acti veModelReference, 417
ActiveSheet, 692-694
ActiveX Automation, 769, 784, 787,
808

ActiveX Controls, 816


compiling, 830-832
creating, in VB6, 822-823
debugging, 827-830
registering, 669
using, 667-670

ActiveX Data Objects, 556, 714716, 740


ActiveX DLLs, 816
ActualSpacing function, 779
Add Note method, 736-737
Add Reference dialog, 785
Add-In file, 803-805
Add-In Manager, 20
adding
files to Setup package, 843
item pairs, 678

add-ins menu, 20-21


AddItem method, 161, 168-169
addition, 104-105
Additional Controls, 20
AddNew method, 729-730
addresses, cell and range, 697-698
ADO constants, 746-749
ADOX reference, 555
After Detach event, 524
AfterActivate event, 527-529
AfterAttach event, 520-524

883

884

I Index I
Alias, the, 650
alignment, 362
buttons, 367-368
creating, 735
horizontal, 365-368
vertical, 368-370

AlignSelected method, 366-368


alphabetizing, 87
ampersand (&),100
API Calls, 649-651
API Types, 650-651
apostrophe C), 6
Append procedure, 131, 483
Application, MicroStation, 73
Application Object, 42-44,188-189,
191, 193,784
list of properties and methods in, 194222
in Mathcad, 769-770

ApplicationObjectConnector, 222
applications
building in VB.NET, 854-855
communicating with other, 784
compiling in VB6, 838
compiling Microsoft Visual Studio
2005,854
compiling VB6, 837-838
distributing VB6, 839
distributing VBA, 501-503
distributing VB.NET, 877

ArcElement, 75-76
arcs, creating, 301
ArcTangent, 110
arrays, 60, 77
changing the size of, 416
dynamic, 371-372, 447
returning, in a function code, 60
storing file extensions to, 446

ASC function, 96-98


ASCII Text files, 19, 96
accessing, in VB.NET, 868
batch processing in, 599-603
exporting tags using, 580-584
reading, using File System Object, 676677
reading and writing to, 130-131
tab-delimited, 481-483

asterisk symbol (*), 105-106


use in SQL, 736

Atn (ArcTangent) function, 110


AttachmentLog.txt, 523
AttachmentModified event, 525
attachments, 519-526
detaching of, 524
displaying, 381-382, 386
e-mail, 620-621, 680-681
list of object properties and methods
for, 521-522
list of properties and methods in, 222227

Auto List Members, 190


autocomplete, 154
Auto-Load, 11,494,647
Auto-Run, 494-495

B
backslash symbol (\), 106, 190-191
.bas (Module) files, 16
BaseElement, 573-574
BasePoint click, 363- 364
batch processing, 599-621
Beep function, 128,650
Before Detach event, 524, 526
BeforeActivate event, 527-529
BeforeAttach event, 525
BeginUndoRedo Event, 537-538
"Bentley MicroStation DON #.#
Object Library, 788, 795, 817,
851
Bentley website help files, 41-42, 44
binding, 690-691, 785-787
Bitmap files, 859, 873-876
Bookmarks, 18
Boolean data type, 72
Boolean value, 113-114
BorderStyle property, of Image
Control, 167
Break,20
Break point, 357, 465
Browse button, 384
bubble sorting, 372-373, 377
bubble sorting function, 88-90
"B uilding" project, 571, 854-855

I Index I
buttons
alignment, 366- 368
Browse, 384
Cancel, 17 1, 443
Change Current Selection, 357-358
Close, 358-359
Command,164
creating, 798-802
Draw In MicroStation, 737-738
Pick, 172, 173
Run Macro, 4
seed file selection, 442
Select, 356-3 57
Spin, 167
Step Into, 12
Toggle, 163
unassigned cursor, 337

ByRef (By Reference), 64, 68


ByVal (By Value), 64, 65, 68

C
.CAB files, 847
Cad Input Queue, 334-340, 364
CadlnputMessage, 334
list of properties and methods in, 227228

calendar, 669
Call Stack window, 18, 27
calling, 57-58
class modules, 394-395
PointString element, 417
variables, 421

Cancel button, 171, 443


"CantBeUndone" parameter, 540
capitalization, 80
of text elements, 400-402

capturing events, 287, 493


of external applications, 793

carat symbol (A), 106


Carriage Return constant, 100
case sensitivity, 80
cataloging files, 679
Catch function, 861
CDbl function, 111-1 13
CDO (Collaborative Data Objects),
620,680
cell addresses, 697-698

885
cells
adding to library, 306-307
changing column designation in Excel,
695
changing fo rm ula in, 701
changing values in, 701
creating, 304-307
declarations, 305
finding specific Excel, 694-695
selecting, 346-347

Cells Collection, 694- 695


CGPlace Line Constrained
command, 349
Change Current Selection Button,
357- 358
Change event, 166, 183
ChangelnX property, 465
Changeln Y property, 465
ChangeinZ property, 465
ChangeTag procedure, 578-580
ChangeTrackEvents Interface, 549550
ChangeType parameters, 531-532
channel levels of RSS files, 741
characters, counting, 90-91
CheckBoxes, 162
CheckLevel function, 740
choosing an application, 793-794
Chr function, 96
Clnt function, 111-114
Circle element, 738-739
circles, 75, 297- 300
drawing, 410- 41 2
selecting center point procedure for,
298-299
selecting two points procedure fo r,
299-300
test procedure code for, 298-299

class files, 16
Class Module windows, 19, 30
class modules, 54, 394, 445
adding, 52-54
calling up, 394-395
clsTimeTrack, 491 - 493
interfaces and, 392
lifecycle, 395- 396
MDL Applications and, 387-388

886

I Index I
Class_Initialize event, 493
Class_Terminate event, 493
ClassComplete property, 424-425
classes, 52, 67, 784. see also objects
adding, 524
default file name of, 394
implementing, 52-53
terminating declared, 396

Cleanup event, 399


Clear, 17, 161, 732
click events, 29
CommandButton, 836-837

CLng function, 111-114


Close and return to MicroStation
function, 17
Close Button, 358-359
CloseMode parameter, 360-361
.cls files, 16
clslmagelnsertion,389-390
clsLineElem, 474-477
clsModelEvents code, 527-529
clsStandCheckA code, 626-627
adding to Standards Checker, 627-628

clsTimeTrack,491-493
cmdCancel_Click, 171
code, 18,52-54,516
controlling execution of, 26, l35
repeating, l37-l38
use of, within VBA Project Manager, 4,
6
viewing, 29- 30

Code Modules, 30,45,51,516


collection methods, 316-324
Collections, 470-477
colon symbol (:), 291
color
changing active, 333
changing line, 293-295

Color Table, 294, 318-319


ColumnCount property, 160
ColumnCount property, for ListBox
control, 161
columns, 555,695
COM interfaces, 784
ComboBoxes, 29, 32,160-161,817818
command area, 330
Command Buttons, 164
CommandButton,29

commands, 22, 348


captu ring, 352
input initiated as, 337

CommandState.CommandN arne
property, 541, 548
comments, 6, 47
compatibility, setting, 834
compiling
ActiveX Controls, 830-832
applications in VB6, 838
applications in VB.NET, 854

Complete Word, 18
complexity, degrees of, 368
Compressed (zipped) Folders, 507
Computer name function, 613-615
Configuration Manager, 854-855
Connection Object, 715, 719-722
Connection Strings, 563, 716, 722723
constants, 78, 100
ADO, 746-749
cursor type, 725
List, 17
lock type, 725-726
message boxes, 118-120
QueryClose,360-361
vbProperCase, 85

contents tab, 35
control events, 154- 155
control names, 377
control placement, 363
control properties, 377
controls, 20,29, 151,353-355
ActiveX, 669-670
adding, 168
creating, 822-826
initializing, 829
properties for ComboBoxes, 160-161
properties for ListBox, 161
properties for TextBox, 159
registering, 830-831
standard, 27
in Toolbox, 152,668
TreeView,822-826
use offrame with, 164

ControlTipText property, 158, 360


converting
strings to numbers, I II
supplied parameters to doubles, 11 2

I Index I
coordinates, 35 1
CopyFileToZipFile procedure, 508509
Cos (cosine) function, 107-108
counting
characters, 90-91
elements, 374

Create button, 13
Create3dLines, 291-294
CreateArcElement, 301-303
CreateCellElement, 305-307
CreateCircle, 298-300
CreateDatabaseLink, 564-565
CreateDesignFile,307-309
CreateDia1og, 459
CreateEllipseElement, 297-298,
300-301
CreateLineElement, 289-295
CreateObject, 691 , 789, 790
CreatePolygon, 296-297
CreateShapeElement, 295- 296
CreateTextElement, 303-304
cursor buttons, unassigned, 337
cursor movement, 426
cursor type constants, 725
Custom Class Modules , 474-478
Custom file Properties, 686-687
Custom tab, 685
Custom types, 282-283
Cut, 17

o
data, security issues with creating,
309
DataBaseLink object, 552
databases
connectingt~557-561, 7 19

connecting to records in, 724


creating, 563-564
creating a user interface, 566-567
creating in-memory, 740-741
creating new, 554
creating records with SQL, 565-566
examining schema of, 744-749
linking elements to, 552-554

DataPoint event, 407-408


date and time functions, 122- 124,
287
date data type, 72

887
DateAdd function, 122-123
DateDif function, 123
DDE (Dynamic Date Exchange),
784
Debug folder, 854
debug menu, 19
debug mode, 12
debug toolbar, 22
debug window. see immediate
window
De-bug.print, 85
declaration, 189
of API calls, 649-650
area,66,282-283,355
default setting, 67
DIM,66
form functions, 431
private, 66
public, 66-67
of types, 650- 651
of variables, 66, 70, 76
of variables in Mathcad, 762

default file name, 436


DefaultFileN arne property, 453
"define" region, 773
Definition, 18
DeleteSetting function, 129
description area, 12-13
design files
attachments, 381 - 382
creating new, 307-309
exporting to, 377
saving changes to, 333

Design Mode, 20
Design Time, setting properties at,
152
DesignFile Object, 74, 228-230
DesignFile property, 522-523
DON Browser application, 862-865
DON files, 74, 446
compressing into Zip file, 507
creating levels in, 796-802
numbering within zip files, 510
opening multiple, 484-485
searching for, 382

dialog boxes, 12,353, 443


Information, 443-444
MDL Applications and, 387-388
OpenAlert, 432

888

I Index I
dialog functions, 432-444
DialogOpenfile command, 352-353
Dictionary Object, 676-677
Digital Signature, 20
DIM statements, 6, 66
Dir function, 125-127, 603
directories, 124-125
displaying, 23
attachments, 381-382
Excel files, 437
models and levels in Active Design
File, 822-826
more than one file type, 438
one file type at a time, 437
RSS files, 743-744
text as code executes, 26

distribution, 374-375
division, 106
division points, 422-424
DivPts property, 422-424
DLLs, 20, 431, 670
creating Active X, 832

Do ... Loop statement, 138-140, 364


DoCellInsertion, 173
dockingtab, 27-28
documents, creating, 307-309
DoFilesinFolder procedure, 484-485
double precision floating point
number, 71
doubles, 48, 58, 71-72
Draw In MicroStation button, 737738
DrawCirc1e, 420, 467-468
drawing
a 2D plate, 771-772
circles, 75, 410-412
in Mathcad, 775-781
rectangles, 409-410

DrawLine method, 466


DrawLinePerp method, 466-467
DrawPart procedure, 776-778
DriveLetters variable, 652
drives
determining type of, 652-653
getting all network, 676
using File System Object to get
information, 675-676

DSO OLE Document Properties


Reader 2.0, 681-682

DSOFile Reference, 68 1-682


DTR (Degrees to Radians), 59
Dynamic Data Exchange (DDE),
784
Dynamics event, 399-404, 407-408
optimizing, 426-427

E
early binding, 690, 691, 785-787
edit button, 12
edit menu, 17
edit toolbar, 22
Element objects, 230-236
ElementChanged event, 539-540
ElementEnumerator, 236
elements, 369-370
deleting, 547-548
displaying DatabaseLink in message
box, 565
finding specific, 404-406
ID property of, 577-578
linking to database records, 564
modi~ng,539-542

saving changes to design file, 333


selecting with two point method, 341342
user selection of, 332

ElementScanCriteria object, 316318,379


list of methods, 325-326
list of properties and methods in, 236237

EllipseElement,75
ellipses, creating, 300-301, 410-412
e-mail, sending, 619-620, 680-681
in VB.NET, 870

Empty String, 435


Enabled property, 157
EndPoint properties, 463-465
Entity property, 565
EntityNumber property, 552

I Index I
enumerations, 243-245
ErrorType, 639
for horizontal alignment of text, 366
list of, 245-277
MCRegionType,765-766
MsdACSType, 245
MsdAddAttachmentFlags, 245
MsdAngleAccuracy, 245
MsdAngleFormat, 245
MsdAngleMode, 246
MsdAttachMode, 246
MsdBsplineCurveOffsetCuspType,
246
MsdBsplineCurveType, 246
MsdBsplineParametrizationType, 246
MsdBsplineSurfaceDirection, 246
MsdBsplineSurfaceType, 246- 247
MsdCadlnputType, 247
MsdCellType, 247
MsdChangePropagation, 247
MsdChangeTrackAction, 538-539
MsdCommanResult, 248
MsdConversionMode, 248
MsdCoordinateAccuracy, 248-249
MsdCoordinateFormat, 249
MsdCopyContextLevelOption, 249
MsdCopyViewPort, 249
MsdDatabaseLinkage, 249
MsdDataEntryRegionJ ustification, 249
MsdDesignFile Format, 250
MsdDevelopableElementOutputType,
250
MsdDialogBoxResult, 250
MsdDimAccuracy, 250- 251
MsdDimAlignment, 251
MsdDimAlternateThresholdCompani
on, 251
MsdDimAngleMeasure,251
MsdDimBallAndChainAlignment, 251
MsdDimBallAndChainChainType,
252
MsdDimCustomSymbol, 252
MsdDimDMSPrecisionMode, 252
MsdDimLabelLineFormat, 252
MsdDimMLNoteFrameType,252
MsdDimMLNoteJustification,252
MsdDimNoteHorizontalAttachment,
252-253
MsdDimNoteLeaderType, 253

889
MsdDimNoteTextRotation, 253
MsdDimNote VerticalAttachment, 253
MsdDimNote VerticalJ ustification, 253
MsdDimPlacementTextPosition, 253
MsdDimRadialMode, 253
MsdD imS tackedF ractio nalAlignm en t,
254
MsdDimStackedFractionType, 254
MsdDimStyleProp, 254-260
MsdDimSuperscriptMode, 260
MsdDimSymbolType, 260
MsdDimTerminatorArrowhead,260
MsdDimTerminatorMode, 260
MsdDimTerminatorType, 260-261
MsdDimTextField,261
MsdDimTextFormat, 261
MsdDimTextFrameType,261
MsdDimTextJustification,261
MsdDimTextLocation,261
MsdDimTextOrientation, 262
MsdDimThousandOpts,262
MsdDimToleranceType, 262
MsdDimType, 262-263
MsdDim ValueAngleFormat, 263
MsdDim ValueAnglePrecision, 263
MsdDim VerticalTextOptions, 263
MsdDrawingMode, 263
MsdElementCachePurpose,263
MsdElementClass,263-264
MsdElementSubtype, 264
MsdElementType, 264-265
msdElementType,312-313
MsdError,265-27 1
MsdFileAccessMode,271
MsdFillMode,2 71
MsdFontType,271
MsdGeoReferenceSisterFileType, 271
MsdGlobalLineStyleScale, 27 1
MsdLevelChangeType, 271- 272
MsdLevelElementAccess,272
MsdLimits,272
MsdMeasurementBase, 272
MsdMeasurementSystem, 272
MsdMemberTraverseType, 272
MsdMessageCenterPriority,273
MsdModelChangeType, 273
MsdModelType, 273
MsdNestOverrides, 273
MsdNewLevelDisplay, 274

890

I Index I
MsdRasterBlockType,274
MsdRasterDisplayOrderCommand,
274
MsdRasterDisplayPriorityPlane,274
MsdRasterModificationType,274
MsdRasterWorldFile,274
MsdReferenceSystem, 274-275
MsdRenderingMode,275
MsdStandardsChecker ReplaceChoice,
275
MsdStandardsCheckerReplaceOptions,
275
MsdStatusBarArea,275
MsdTagType, 275
MsdTangentElementOutputType,276
MsdTangentInterpolationType, 276
MsdTextDirection,276
MsdTextJustification, 276
MsdTextNodeLineSpacingType,276
MsdV7 Action, 276
MsdViews,277
MsdXDatumType,277

enumerators and scan criteria


objects, 321-322
Err object, 147
error handling, 142-148,639, 791
default path validation, 451-452
of error 52, 452
in VB.NET, 860-861

"ErrorType" enumeration, 639


"eval" region, 773
Event Handler, 549-550
events, 6,29,285,468-470, 519
Accept, 399
accept, 399
After Detach, 524
AfterActivate,527-529
AfterAttach, 520-524
application object, 189
AttachmentModified,525
BeforeActivate,527-529
BeforeAttach, 525
BeginUndoRedo,537-538
capturing time, 493
Change, 166, 183
Class_Initialize, 493
Class_Terminate, 493
Cleanup, 399
click, 29,161 - 164,836

CommandButton click events, 836837


control, 154-155
DataPoint,407-408
Before Detach, 524, 526
Dynamics, 399-404, 407-408, 426-427
Elem entCh anged, 539-540
Form Load, 8 17
IAttachmentEvents Interface, 519
ILevelChangeEvents Interface, 53 1532
ILocateCommandEvents Interface,
396-397
IModeiActivate, 527-528
IModeiChange,527-530
Initialize, 183-184
IPrimitiveCommandEvents, 392-393,
406-407,420
ISaveAsEvents Interface, 510
ISaveAsEvents_BeforeRemap, 510511
IView UpdateEven ts_AfterRedraw,
493
KeyDown, 160
Keyin,416
KeyPress, 96, 155, 160, 169-170,364365
KeyUp,160
LineAdded, 468-469
LocateFailed, 399
LocateFilter, 398,400
LocateReset, 399
ModeIChange,527- 530
MouseDown, 155
MouseMove, 359
OnDesignFileClosed, 286,493,507,
509-510
OnDesignFileOpened, 286, 493, 506
QueryClose,360-361
RoomLabelCheck, 635- 638
RunCheck,634
Scroll, 166
Show, 287
Start, 399
UserFormlnitialize, 168- 169,360

I Index I
examples folder, 39
Excel, 437, 793-794
Add-In fil es, 803-805
connecting to, 689-690
customizing, 798
as databases, 749-754
MicroStation, 793-795
opening, 753
starting or creating new instance of,
691
XML file exported from, 593-594

Excel Function RAND, 704


Excel.Application Object, 692
.exe files, 815
EXE files, 839
Execute method, 719
execution of code
breaking, 91
immediate, 26-27

Exit Do statement, 139,364


Exit function statement, 139
Exit Sub statement, 139
exponents, 106
export file, 16
exporting
field names, 735
tag information, 580-584
user forms to a design file, 377-379

ExtCount, 447-448
"Extended Property", 749-754
Extensible MarkupLanguage. see
XML
ExtractIcon, 873

F
favori tes tab, 37-38
feedback,329-331,375-376
capturing user, 359

field names, exporting, 735


fields, 746-749
Fields Collection of the Recordset
Object, 735
File, 19
file extensions, 445-453, 458, 460,
508
file headers, 508
file menu, 16
file names, 510
File Open dialog box, 436

891
File System Obj ect, 484, 576,
675-678
FileCreate function, 458-459
Fi1eDateTime function, 124
FileLen function, 124
Fi1eOpen function, 432
files
adding to Setup package, 843
ASCII Text, 19
batch processing of all folder, 603-604
cataloging, using Dictionary Object,
679
filtering, 31 7-318
finding, 3
identifying, 665- 666
loading, 2
processing, in folders and subfolders,
607
reading in VBA, 868
reading in VB.NET, 868-869
registering, 837, 841
saving as Bitmap, 873-874
scanning, 311 - 314
searching, 311
sending, to CD writer, 512-513
types of, 604
user interface for, 608-613
writing in VBA, 869
writing in VB.NET, 869
zip, 507-508

FilesInFoldersmethod, 385-386
FileSystemObject, 385
FillMode parameter, 296
filtering, 3l7-318, 404-406
Find, 17
Find method, 727-728
FindExecutable, 656-657, 873
Fix function, 112
floating point, 71
fmStyleDropDownList, 169
FolderIn parameter, 607
fo lders, 23
processing, 606-608
root, 382, 675
selecting, 382
traversing, 870-871

folder-sharing, handling, 495-496


font size, 20
footers, 179

892

I Index I
For ... Next statement, 135-137
For Append, 131 , 483
For Each ... Next statement, 141 , 472
form files , 16
Form Load event, 817
Form1 (VB6), 810
Format function, 100
format menu, 19
FormatCurrency function, 98
FormatDateTime function, 99
FormatNumber function , 98-99
forms, 49, 353
creating with VB6, 809-816
displaying as modeless, 361
public, 67
window, 28-29
forward slash (I) , 106

frames, 164, 168


FreeFile, 131-13 2
.frm files, 16
frmAlign.Text.frm, 362
frmDFAY.frm, 381
frmExportElements.frm, 377
frrnMatchProperties.frm, 353
FSOC, 675-676
Function (keyword) , 55, 58-63
functionality, 457-458, 517
encapsulating similar, 445

functions , 6, 55, 650


DLLs and, 431
VBA string functions, 84

G
geometry, creating MicroStation,
771
GetAllRegions function, 773- 774
GetAllSettings function, 129- 130
GetComputerName, 613-614, 653
GetDiskFreeSpace, 657
GetDriveType, 652-653
GetEvals, 774-775
GetExts, 447, 450
GetInput method, 335
GetLogical Drives, 652
GetObject, 689- 690, 788- 789, 818819
GetSelectedCount, 374
GetSetting function, 128
GetString method, 728

GetSystemMetrics, 658-659
GetTags, 573
GetThreeVals, 65
GetTickCount, 659- 660
Getting All Files, 871-872
GetType functionality, 544-547
GetUserName, 660
GetValue, 759-760, 772-773
GetVersionEx, 653-655
GetWindows Directory, 660-66 1
graphical elements, 289
Graphical User Interfaces (GUIs), 7,
49, 96
grid, 20
group collection method, 324- 325
grouping, 474-478
groups, creating named, 323

H
hardcoding, 47, 351
headers , 178
height property, 157
help files
Bentley website, 41-42, 44
Object Browser, 190- 191
Visual Basic, 21, 34

help menus, 21, 30, 34


High Security Mode, 502-503
.htm files, creating, 582-584
Hungarian notation, 79
IAttachmentEvents Interface, 519
ID property, 577- 578
IDE (Integrated Development
Environment), 856
If ... Else If statements, 178-182
If ...Then statements, 141-142
ignored problems, 645-647
ILevelChangeEvents Interface, 531532
ILocateCommandEvents Interface,
396-397
Image control, 167
images, inserting, 387- 389
IMathcadApplication2, 770
IMathcadRegion20bject, 767
Immediate Window, 18,26-27, 742
IModelActivate events, 527- 528

I Index I
IModelChange events, 527-530
Implements (keyword), 392
import file, 16
Import Image MDL application,
387-388
Inc1udeOnlyWithin Range method,
326
Indent, 17
Index attribute, 595-596
index tab, 36
information
retrieving from a web site, 617-618,
741-742
storing, 615-616

Initialize event, 183-184


initializing, U serForm, 360
InputBox, 120-122,605
Input Queue, 174
input types, 335
inputs, capturing user, 338-341
"Insert Into" SQL statement, 737
Insert menu, 19
InStr function , 92- 94
InStrRev function, 94-95
Integer, 70
integers, converting, 111-113
Intellisense, 786-787
Internet, logging activities over,
616- 617
Internet Explorer, 785- 787
Internet help, 40-41
IPrimitiveCommandEvents, 392393,406-407,420
ISaveAsEvents Interface, 510
IsNumeric function, 113-114
IsOleFile, 686
IStandardsChecker, 624-625
Item child in RSS files, 741
item index, 474
item levels in RSS files, 741
IView UpdateEvents_AfterRedraw,
493

J
Join function, 95

K
Key Ascii parameter, 170, 365
KeyDown event, 160

893
Key-In dialog box, 352
Keyin event, 4 16
Keyin window, 337
key-ins, 417
KeyPress Event, 96, 155, 160, 169170, 364-365
Keys, using, 473-474
KeyUp event, 160
keywords, 78
Kill function, 127

L
Label control, 158
late binding, 690, 785-787
Lcase function, 84-85
LCE_DistanceText, 402-404
LCE_Text, 397-398
Left function, 90
Left property, 156
Len (length) function, 90
Level Mapping file, 488-489
levels, 74-75 , 377, 531
adding to a collection, 473
batch processing, 600
CheckLevei function for, 704-706
creating in .dgn file, 796-802
equalizing, 738-740
list of properties and methods in, 237238
modifying, 548-549

Levels Collections, 140-141


LevelSpecA procedure, 485-487
libraries, 24
LineAdded event, 468-469
LineAngleDegs, 465-466
LineAngleRads, 465
LineElement,75
lines, 178-179
3d,291-294
creating, 289-293
drawing with cursor, 407-409

linking elements, 552-554


LinkLabel, 860
List Constants, 17
List PropertieslMethods, 17
ListBoxes, 161
ListView controls, 859
lngDivisions property, 424
load behavior, 21

894

I Index I
load project button, 3
Locals Window, 18, 26
LocateCriteria object, 404-406
LocateFailed event, 399
LocateFilter event, 398, 400
LocateReset event, 399
Lock proj ect property, 500, 797
lock type constants, 725-726
log files, 613- 614
LogonUser, 661
LogToWeb, 617
Long number, 70- 71,111 - 112
lower case function, 84-85

M
macros, 3- 5, 20
recording, 348- 351
running, 793-794
running a MVBA, 5
running Excel, 797-805
using Mathcad, 757- 759

Macros dialog box, 12


Make Project dialog box (V6), 811
Make Project Group, 837- 838
mapping standards, 485-488
Match Properties Form, 362
Mathcad
advantages of using, 772
communicating with, 763-766
overview, 755-756

Mathcad Object Model, 769- 770


MathInterface property, 767
MathRegion, 766
mcMathRegion, 765- 766
MDL Applications, 387
mdlDialog_fileCreate Function,
439-440
mdlDialog_fileCreateFromSeed
Function, 441-442
mdlDialog_fileOpen function, 432434,453
mdlDialog_openAlert Function, 443
mdlDialog_openInfoBox Function,
443-444
members list, 24, 189
memory, opening files in, 496-497
menus, 15-21
message boxes, 117-120
MessageBeep function, 662-663

methods
Add Note, 736-737
AddItem, 161, 168-169
AddNew,729-730
AlignSelected,366-368
application obj ect, 189
collection, 316-324
creating new in Class m odule, 459
DrawLine, 466
DrawLinePerp, 466-467
Execute, 719
FileslnFolders, 385-386
Find, 727-728
GetInput, 335
GetString, 728
group collection, 324-325
IncludeOnlyWithin Range, 326
interfaces and, 392
MoveFirst,727
multi-criteria collection, 321 - 324
New, 692
OpenDialog, 446
OpenSchema, 746, 751
overloaded, 867-868
Pages.Add,165
Pages. Count, 165
ScanCriteria collection, 316-322
Show, 183-184
ShowError, 332
ToUpper,867
Update, 729-730

Microsoft Access , 556-557


Microsoft ADO Ext. 2.8 for DDL
and Security Reference, 554
Microsoft CDO for Windows 2000
Library, 680-681
Microsoft Excel #.# Object Library,
805
Microsoft Internet Controls
Reference, 617, 787
Microsoft OLE DB Simple Provider,
740
Microsoft Package and Deployment
Wizard, 841-847
Microsoft Scripting Runtime, 385,
484, 576,606,674
Microsoft Shell Controls and
Automation, 605, 606, 671
Microsoft Visual Basic Help, 21 , 34

I Index I
MicrosoftSpeechObjectLibrary, 679
MicrQStation, 18
MicroStation
attaching to project in Visual Studio
2005,852-853
controlling with VB6, 81 6
creating new instances of, 790-791
Excel versus, 793-795

MicroStation DGN file, 74


MicroStation VBA Help file, 39
MicroStationDGN .ApplicationObj ec
tConnector, 818- 819
MicroStation-Specific variable
types, 73
Mid function, 91 - 92
Midpoint property, 463-466
minimum and maximum points of
selected text, 369-370
minus symbol, 104
MkDir function, 124-125
Mod function , 114
ModelChange event, 527-530
modeless dialog boxes, 353
mode1ess form display, 173, 361
ModelReference Object, 74
list of properties and methods in, 238241

module files, 16
modules , 45-46, 67
insert, 19
opening, 30

MonthN arne function, 86


MouseDown event, 155
MouseMove event, 359
MoveFirst method, 727
MS_STANDARDSCHECKERAPP
S,647
MS_VBA_OPEN_IN_MEMORY,
495-497
MSCatalog table, 552, 561 , 565
MsdDesignFile Format, 243
MsLink property, 552, 565, 588- 589
multi-criteria collection method,
32 1- 324
Multipage control, 165
multiple users, log files for, 61 3-615
multiplication, 105- 106
.mvba extension, 3, 61 3-614
myExcel, 690-691

895

N
Name property, 189
named group search, 323
naming
controls, 155
conventions, 79, 156,355
variables, 78-79

.NET Framework, 872


New (keyword), 789-790
New method, 692
"NotifyIcon" Control, 863
Now function , 122, 287, 660
numeric functions, 103
NumericValue, 760
NumericValueObject, 760-761

o
Object, view menu, 18
Object Browser, 18,24,42-44, 149,
757
description of, 188- 190
displaying Members of the Object,
671-672
opening Microsoft VBA Help in, 40

object data type, 72-73


Object Model, 42, 784, 786
Object Names, 784
objects, 29, 469
accessing, in a collection, 471-473
application type of, 506
creating, 462 - 469
DataBaseLink, 552
removing, from a collection, 474
returning, 63
setting, 76- 77
in VB.NET, 866- 867

OLE Documents, 681- 682


OLEDB connection, 554
OnDesignFileClosed event, 286,
493, 507, 509-510
OnDesignFileOpened event, 286,
493,506
OnProjectLoad procedure, 495 , 647
OpenAlertDialog Box, 432
OpenDesignFileforProgram, 386387
OpenDesignFileOpened, 506
OpenDialog method, 446

896

I Index I
opening files, 386, 438
OpenSchema method, 746, 751
operating system, determining, 653655
OptionButtons, 162-163
option explicit, 67, 80-81
Options, 20
Order By statement (SQL), 732-733
Order of Operations, 116
Outdent, 17
Output, 483
overloaded methods, 867-868

p
Pages.Add method, 165
Pages.Count method, 165
Pan operations, 183
ParamArray, 57-58
Parameter Info, 17
parameters, 47-49,57,65,68
"parcel" table, 553
parenthesis, using, 853
in VB.NET, 868

passwords
protecting, 498-500
requesting from user, 661-662

Paste, 17
Path property, 189
paths, 451-452,656-657
Pause Recording Macro, 11
PCE_CircTest, 410--412
PCE_LineTest, 407-408, 417-420,
424-426
PCE_PointStringTest, 414-416
PCE_PolyTest, 412-414
PCE_RecTest, 409
PCE_TestLine, 420--424
personalizing help fi les, 38
pFileExts, 447-450
Pi function, 59
Pick button, 172, 173, 363
PictureBox control, 873
pipe symbol (I), 729
Place Block, 341-343, 350
Place Line command, 341-343
PlaySound function, 662-663
plus symbol (+), 104
Point List Reader, 174-177

Point3d type, 279, 283, 463


PointElems array, 292
points
capturing, 337
user selection of, 363-364, 414-416

PointsByLine function, 341-344


PointsByRectangle function, 345346
PointString, 416, 417
PolarPoint function, 62-63
polygons, creating, 296, 412-414
PolyTest, 412-414
PopulateFileList, 384-385
populating
a collection with levels, 474
ComboBoxes, 817-818

porting, 847- 848


Preserve (keyword), 416
Print, 17
Private (keyword), 66
Private Sub FormLLoad, 859
Private Sub FormLResize, 860
Private Sub LinklabeLLinkClicked,
860
Private Sub IstvCells_DoubleClick,
860
Private Sub
IstvCells_ItemSelectionChange
d,859-860
problem report, 645- 647
Procedure, 19
procedures,6,55,57-58,66
processing, 606-608
processor time, maximizing, 426429
project, creating a VBA, 46-47
Project Explorer, 18
Project Explorer, 23
Project Groups in VB6, 827-828,
837-838
project manager, 3
projects
creating in Microsoft Visual Studio,
851
distributing, 500
protecting, 498-499, 811
un-signed,502-503

prompt area, 330

I Index I
properties, 20, 152
application object, 189
Custom file, 686-687
implementing, for classes, 52
viewing, of variables, 192
writing, 682-683

Properties Window, 18,25,152-153


"Property Get" statement, 449
"Property Let" statement, 449
provider component of UDL file,
563
Public (keyword), 66-67
publishing process, 877-878
Q
Query Builder, 557, 562
QueryClose event, 360-361
Quick Info, 17

R
R1C1 reference style, 696-697
radians, 107-108
"Radius" parameter, 56
RaiseEvent statement, 469
RAND function, 704-705
random number, function to
generate, 115
range addresses, 697-698
Range Collection, 695
reading
ASCII Text files, 130-131, 132-135,
676-677
in VB A, 868
in VB.NET, 868-869
from the Windows Registry, 616
XML files, 587-594

Read-Only property, 449, 496-498


Record Macro, 11
RecordCount property, 726
records, adding new, 737
records, database, 565-566
Recordsets, 724, 731
rectangles, drawing, 410-412, 523
recursive execution, 606-608, 675
Redo, 17

897
References, 20, 500-501, 785, 840
adding, 484
adding, to DLLs, 670
adding, to Microsoft Excel Object
Library, 690
"Bentley MicroStation DGN #.# Object
Library, 788, 795,817,851
DSOFile, 681-682
Mathcad, 756
Microsoft ADO Ext. 2.8 for DDL and
Security, 554
Microsoft Internet Controls, 617, 787

Region Objects, 766-767, 773


Regions, in Mathcad, 765-767
registry editor, 382
RegSvr32.exe, 830-831
re1ati ve coordinates, 351
Release folder, 854
remainder val ue, 114
Remove, 17
removing objects from a collection,
474
removing projects from VB6, 812
Replace, 17
Replace function, 92
ReportError procedure, 639-640,
644-645
Reset, 20
Reset Input, 338
resetting Level names procedure,
487-488
Return statement, 872
return value array, 373
return values, 434
setting, 373
of user forms, 441

rewriting element to model, 314, 325


right function , 90-91
RmDir function, 125
Rnd function, 115
RoomLabelChecks event, 635-638
Root Folders, 382, 605, 672-673
Round function, 114
rows and columns in Excel, 695
RSS files, 741, 743-744
RTD (Radians to Degrees), 59
Run Macro, 4-5, 11,20
run menu, 20
RunCheck event, 634

898

I Index I
running a project automatically,
494-495
run-time, setting properties at, 152154

S
Save button, 22
SaveAs functionality, 510- 517
SaveSetting function, 128
savmg
class modules into VBA Project, 461 462
projects with CreateDialog method,
459

ScanCriteria collection method,


316-322
scope, 66-68
Scroll Bars, 166
Scroll event, 166
search and replace function, 92-94
search tab, 37
searching, 311
folders for .dgn files, 382
sub folders for .dgn files, 382

security issues, 309, 502-503


Seed file, 441-442
Select All, 17
select button, 356
Select case statement, 118-119, 314
error handling, 142-147

Select statement (SQL), 731- 732


selecting folders, 382-384
selection sets, working with, 332
selectNodes, 591-592
SendCommand,348
SendDragPoints, 341
sending files to CD writer, 512-513
settings
retrieving, 382, 383
saving to Registry, 383
storing, 382

Settings dialog box, 630


SetUp program, 841-846
SetValue call, 764
Sgn function, 115
shapes, creating, 295
sharing, handling folder, 495-496
She1l32 BrowseForFolder, 672-674
ShellExecute, 664-665

SHGetFileInfo, 665
shortcuts, 844
Show method, 183- 184
ShowCommand,339-340
Show Error method, 332
ShowEvents procedure, 287
ShowPrompt, 339-340, 359- 360
ShowTempMessage, 331
Sin (sine) function, 107-108
Sleep function, 656
SND_ASYNC flag, 663-664
sorting
bubble, 88-90, 372-373, 377
in SQL, 732-733
text, 87-90

sounds, 663-664
Space function, 435
spaces, removing, 86
spacing, text, 368, 375- 376
SpacingXIn formulas, 779
SpacingYIn formulas, 779
speech capabilities, 679
Spin Buttons, 166-167
Split function, 95
SQL Query Builder, 561, 565-566
SQL Select Statements, 562, 730731
Sqr (square root) function, 107
squares function, 106
Standard EXE, 810, 839
Standard toolbar, 22
Standard VBA variable types, 70
standards
checking for, 634
cross-company, 485-487
maintaining, 479

Standards Checker Interface, 480,


624-625
Start event, 399
Start Up project, 828, 830
StartPoint properties, 463-465
Startup Objects, 813
State property, 722, 731-732
status area, 330
Step Into buttons, 12
Stop, 465
StopRecording Macro, 11

I Index I
storing
confidential information, 449
information, 615-616
point selections, 366
settings, 387, 632

StrComp function, 87-88


StrConv function, 85
string data type, 72, 83
string type variables, 32-33, 70
strings
buffered, 652, 653
concatenate strings, 100
counting character, 90-91
in VB.NET, 866-867

StringValue in Mathcad, 761


"Structured Storage" Documents,
681-682
Style property, 169
SUB (keyword), 55
subfolders, 870-871
subtraction, 105
Summary Properties, 682-684

T
Tab Order, 18
Tab strips, 164
tab width, 20
tab-delimited files, 483
Tablndex property, 157
tablename field, 552
tables, creating, 555-563
TabStop property, 157
Tag property, 158
tags, 571-578
extracting information, 707- 711

TagSetName, 573
Tagsets, 575-576
TakeFocusOn Click property, 164
Tan (tangent) function, 107-110
temporary message, 331
temporary storage location, 513
terminology, 32
"Test Connection" message box,
560-561
TestCadlnputN, 352

899
testing
ActiveX DLL, 836
clsTimeTrack, 493
default path, 451-452
DrawLinePerp method, 467
file extensions, 447-450
startpoint and endpoint, 464

TestMatchProperties, 361
text alignment, 362
text boxes, 37-38,364
text elements
capitalizing, 397-398, 400-401
comparing, 87
creating, 303
determining number of, 369-370

text files , 95-96


TextBox control, 158-159
TextElement Object, 76
"TheAttachment" parameter, 521
Thumb,166
time, tracking, 490-493
Timer function, 124
Title parameter, 120
Toggle button, 163
ToMiles function, 107-108
Tool Tip, 32
Toolbars, 18
toolbars, 22-23, 798
Toolbox, 18, 840
controls in, 27, 152,668

Tools Menu, 20
Top property, 156
ToUpper method, 867
tracking, 615
transaction logs, 619-620
translating standards, 485-487
traversing a folder and subfolder,
870-871
Trim function, 86
Try function, 861
.txt files, 445
Type Libraries, 20, 785
types, 279-283
API, 650-651
available in MicroStation VBA, 281 282
MicroStation-specific variable, 73
returning, 62-63
standard VBA variable, 70-73

900

I Index I
variables, 6, 66, 68

U
UCase function , 84
UDL files, 557-560, 563,715-719
connecting to Excel file, 750-751
opening a connection using, 722-723
RSS and, 741

UnassignedCB input, 337


underscore character C), 60
Undo, 17
Unload Me, 171
un-signed projects, 502-503
unsupported levels procedures,
finding, 480-482
Update method, 729-730
Upper case function, 84
user feedback, 375-376
providing, 379-380

User forms, 49,860


exporting to a new design file, 377 -379
VB6,812

user input, getting, 334-337


user interface exercises, 167- 174
user interfaces, 566-567, 608-613
U serForm, 19
code, 286
Initialize event, 168-169,360
toolbar,23

users, capturing current, 287

V
Val function, 113
validating path, default, 451-452
Value property, 162, 163, 165, 166,
167
values, 63
assigning, 76-77
constant, 78
retrieving cell, in Excel, 694-695
retrieving in Mathcad, 773
returning function, 872
tag, 572

adding a watch to, 192


calling, 421, 469
configuration, 496-497
defined,70
as dynamic arrays, 371-372
MicroStation -Specific, 73
naming conventions for, 78-79
as new class type, 395
as private, 66
as Public, 450
setting, to an object, 786
as a string, 435
types of, 70
using, 81

variants, 61, 67, 73


VB6
differences between VBA and, 807 -808
MicroStation, 816
structure of projects, 809

VB6 Virtual Machine file, 839


VBA
differences between VB6 and, 807-808
overview, 1-5
string functions in, 84
variable types in, 70-73
windows, 23

VBA editor, 12
VBA Files From Levels form , 380381
VBA IDE, 15-30
VBA Project Manager, 3-5, 7, 9-13
elements of, 10

VBAPM,3-5
vbCr constant, 100
VB.NET,850
vbProperCase, 85
vbTab constant, 101
vertical order, 369-371
View Code, 29-30
View menu, 18
visible property, 157, 164
Visual SQL Query Builder, 557, 562
Visual Studio 2005, 850
voice capabilities, 679

I Index I
W
Watch Window, 18
watch window, 25, 130
watches, adding, 192
WeekDayName function, 85-86
WeekDayNumber function, 85-86
Where statement (SQL), 732
While ... Wend statement, 137-138
Width property, 157
window menu, 21
Windows API Calls, 872
Windows API functions, 45, 382384,649-650
Windows Registry, 615-616, 632633,872
Windows Status Notification Area,
863
WithEvents (keyword), 286, 506,
792-793
WithE vents form, 469
WordWrap property, 159
workbooks, 692-693
worksheets, 692-694, 701- 702
wrappers, 832
Write Out File, 177-178
Write-Only property, 449
writing files
ASCII, 130-132
using File System Object, 676-677
from the Windows Registry, 615-616

x
x and y text boxes, 364
.xla files, 803-805
.x1s files, 445
XML
button, 741
overview, 585-587

XML files
reading, 587-594
structure of, 586
tracking row and column of celis, 595597

Xpath statement, 591

Z
zip files, 507, 508
Zoom operations, 182-184

901

902

I Index I

END USER LICENSE AGREEMENT FOR BENTLEY SOFTWARE


IMPORTANT - READ CAREFULLY: This End- User License Agreement ("EULA:') is a legal agreement between yo u (either
an individual or a single entity) and Bentley Systems, Incorporated ("Bentley") fo r the Bentley software and associated
documentation that accompanies this EULA, which includes the associated media and Bentley internet-based services
("Software").
YOU AGREE TO BE BOUND BY THE TERMS OF THIS EULA BY DOWNLOADING, INSTALLING, COPYING, OR
OTHERWISE ACCESSING OR USING TH E SOFTWARE. YOUR ACCEPTANCE OF ALL OF THE TERMS AND
CONDITIONS OF THIS EULA IS A CONDITION TO THE GRANT OF LICENSE BELOW. THIS EULA, AS MAY BE
MODIFIED BY ANY APPLICABLE SIGNED WRITTEN AGREEMENT BETWEEN YOU AND BENTLEY, REPRESENTS THE ENTIRE SET OF TERMS AND CONDITIONS GOVERNING YOUR USE OF THE SOFTWARE AND
SUPERSEDES ALL PRIOR OR CONTEMPORANEOUS ORAL OR WRITTEN COMMUNICATIONS, PROPOSALS
AND PRESENTATIONS W ITH RESPECT TO THE SOFTWARE OR THE SUBJECT MATTER OF THE EULA.
If this agreement is translated into a language other than English and there is a conflict of terms between the English and the
other language, the English version will control. You should keep a copy of this EULA for your records. The latest version of
this EULA appears in its entirety on http://www.bentley.com/legal/eula_en.txt. Bentley may update or amend the EULA at
any time without notice to you; however, the form of EULA in effect at the time of the Software acquisition will apply.
1.

CERTAIN DEFINITIONS.
1.1. "Academic Related Use" means the use of designated Software in obj ect code form solely for internal classroom
instruction or research of your teaching staff and! or students matriculated in a degree program and not to
include student use in a paid employment setting or any other use prohibited under this EULA.
1.2. ''Academic Software" means Software that is identified as "Academic Edition" or "Academic License" (or words of
similar meaning).
1.3. "CAe' means client access license.
1.4. "Device" means a single personal computer, workstation, terminal, hand held computer, pager, telephone,
personal digital assistant, Server or other electronic device used by a User.
1.5. "External User" means any individual (not an organization) who is not: (i) one of your full-time, part-time or
temporary employees; or (ii) agency temporary personnel or an independent contractor on assignment at
your place of business or work-site.
1.6. "License Key" means the document furnished to you by Bentley in electronic or such other format, as
determined in Bentley's sole discretion, that sets forth a unique serial number for the Software and authorizes
use of the Software.
1.7. "Production Use" means use of the Software in object code form by a single User or a Device, as applicable, solely
for internal production purposes in support of one Site.
1.8. "Site" means the discrete geographic location where you first install or use the Software.
1.9. "Time Clocks" means any time clocks, copy-protection mechanisms, or other security devices embedded in the
Software which may deactivate the Software after expiration of any applicable subscription or termed license
period.
1.10. "User" means any individual or entity that is not an External User.

2.

GRANT OF LICENSE. As and for so long as you comply with all of the terms of this EULA, Bentley grants you the
right to (a) install and use one copy of the Software for Production Use in the country where the Software is first
obtained and (b) use the documentation that accompanies the Software for internal, non-commercial reference
purposes only.

3.

RESERVED RIGHTS. You acknowledge and agree that the Software is a proprietary product of Bentley or its
suppliers, distributors and unrelated third parties ("Suppliers") protected by copyright and other applicable intellectual
property laws and treaty provisions. You further acknowledge and agree that the entire right, title and interest in and to

the Software including associated intellectual property rights, shall remain with Bentley or its Suppliers. This license
grant may be made by Bentley on behalf of Suppliers as third party beneficiaries of the license rights provided herein.
Bentley retains all rights not expressly granted to you in this EULA. THE SOFTWARE IS LICENSED NOT SOLD.
4.

REGISTRATION. You acknowledge that registration or ac tivation may be required in order for you to utilize the full
benefits of the Software.

5.

NO RENTAL OR COMMERCIAL HOSTING. Software is licensed for Production Use only. You may not rent, lease,
lend or provide commercial hosting services with the Software. You may also not use the Software to provide fee or
transaction based services. Contact Bentley for the availability of alternate pricing if you desire to use the Software in
such fashion.

6.

NO "MULTIPLEXING" OR POOLING. Use of software or hardware that reduces the number of electronic devices
directly monitored or managed by the Software or directly accessing or utilizing the Software (sometimes called
"multiplexing" or "pooling" software or hardware) does not reduce the number of licenses required; the number of
licenses required would equal the number of distinct inputs to the multiplexing or pooling hardware/software "front
end:'

7.

LIMITATIONS ON REVERSE ENGINEERING. You may not decode, reverse engineer, reverse assemble, reverse
compile, or otherwise translate the Software except and only to the extent that such activity is expressly permitted by
applicable law notwithstanding this limitation. To the extent that you are expressly permitted by law to undertake any of
the activities listed in the previous sentence, you will not exercise those rights until you have provided Bentley with
thirty (30) days prior written notice of your intent to exercise such rights.

8.

DATA CAPTURE AND USE. You agree that Bentley may collect and utilize technical information gathered as part of
Software support services that may be provided to you. Data capture in this form will only be used to improve Bentley's
products and/or provide customized services to you and will not be disclosed or disseminated to third parties except in
an aggregated form.

9.

ARCHIVAL COPY. You may make one copy of the Software on media appropriate for your single Device for the
express purpose of backup in the event that the Software media is damaged or destroyed, provided that you reproduce
and include on the backup copy all the information appearing on the original labels.

10.

RESTRICTIONS ON CERTAIN SOFTWARE. Software identified as demo, evaluation, BDN, Beta or "NFR" (or "Not
for Resale" or with words of similar meaning) may not be sold, bartered or otherwise transferred. Such Software may
not be used for any purpose other than your testing or evaluation unless specified otherwise pursuant to a separate
agreement signed by both you and Bentley.

11.

ACADEMIC SOFTWARE. For Academic Software, Bentley hereby grants you a non-exclusive right and license to use
in object code form such Academic Software for Academic Related Use only. You may not sell, barter or otherwise
transfer Academic Software. Special Note Applicable to Academic Software: If you have covered the Academic
Software subject to this EULA pursuant to a valid BEN SELECT Agreement (or successor agreement) with Bentley then
you may be entitled to additional and incremental licensing benefits to those set forth in this EULA by virtue of that
relationship. In the event that Academic Software is no longer covered by a valid BEN SELECT agreement due to
termination of the BEN SELECT agreement or any other reason, then you will lose those incremental benefits, and
your license rights will only be as set forth in this EULA.

12.

TIME CLOCKS. Bentley's default licensing term is perpetual unless otherwise specifically identified for the Software
licensed. If you have licensed the Software subject to this EULA for a term shorter than a perpetual license, you
acknowledge that the Software may be delivered to you with embedded Time Clocks. You agree that Time Clocks are
not considered a defect of the Software and you release Bentley from any and all claims, however characterized, arising
from or related to Time Clocks or their operation.

13.

TRANSFER. Internal. You may transfer the Software and the EULA to a different Device at the same Site, provided you
completely remove the Software from all prior Devices. You may also make a one-time transfer of a CAL to another of
your Users or Devices located at the same Site. In order to accomplish these transfers you may need to contact Bentley.
External. You may not transfer the Software and license granted under this EULA, or a CAL, to a third party without
Bentley's prior written consent. If such consent is obtained, you may permanently transfer the Software and the license
granted under this EULA, or the CAL, provided you transfer the Software and all and media to such third party, and
you do not retain any copies. The recipient of such transfer must agree to all terms and conditions of the EULA. Any
purported sublicense, assignment, transfer or encumbrance is void without Bentley's prior consent.

14.

UPGRADES. You may not use any Software identified as an up grade unless you are properly licensed to use Software
which Bentley has identified as being eligible for an upgrade. After installing an upgrade, you may use the original
Software product that was eligible for an upgrade provided that at anyone time yo u use only the upgraded Softw are or
the prior Software version subject to the upgrade.

15.

NO EXTENSION OF CAPABILITIES. You may develop your own applications that interoperate or integrate with the
Software. Bentley prices its Software, among other factors, based on capabilities that we expose to you. You may not
extend the Software to enable or unlock capabilities of the Software not specifically identified by Bentley as forming
part of the specified end user functionality.

16.

SEPARATION OF COMPONENTS. The Software is licensed as a single product. Component parts of the Software
may not be separated and installed or used on multiple Devices.

17.

TERMINATION. If you breach the terms and conditions of this EULA, Bentley may terminate this EULA without
prejudicing any of its other rights. In such event you must destroy and remove all copies of the Software fr om your
Device(s). Sections 1,3, 13,20,21,23,25,26,27,28 and 29 specifically survive termination.

18.

NO AUTOMATED USE. A license for the Software may not be shared or used concurrently on different Devices, nor
to support multiple User or operational requests as indicated above. As a result, you may not use the Software in an
automated, unattended, non-interactive server application or component (including ASP) where: (i) multiple User
requests from different Users are queued for processing; or (ii) multiple requests from one User are queued for
processing but acting against content created or edited by other Users. Examples which would violate this Section 18
include but are not limited to use as a plot server, fi le translator, print server or other applications using or employing
similar methods.

19.

LIMITED WARRANTY. Except for Software which is identified as no-charge, free, demo, evaluation, BDN, Beta or
NFR, which is provided to you "AS-IS" and specifically without warranty of any kind, for sixty (60) days from the date
of first installation (the "Warranty Period"), Bentley warrants that (i) the Software will perform substantially in
accordance with the functional specifications in the documentation which accompanies the Software; and (ii) the
media on which the Software is distributed meets generally accepted industry standards. It is understood that neither
Bentley nor its Suppliers are responsible for your use of the Software or the results from such use. It is furthe r
understood that there may be errors or omissions in the information contained in the Software, that the information
contained in the Software may not be current or complete and that defects in hardware or software may prevent you
from gaining access to the Software. This limited warranty is offered by Bentley alone, and is not extended to any
software code that may be contributed to the Software by our Suppliers. Any supplements or updates to the Software
(including but not limited to fixes, work in progress builds, or subsequent updates) provided to you after the expiration
of the Limited Warranty period above are not covered by any warranty or condition, express, implied or statutory.

20.

DISCLAIMER. THE FOREGOING LIMITED WARRANTY STATES THE SOLE AND EXCLUSIVE REMEDIES
FOR BENTLEY'S OR ITS SUPPLIER'S BREACH OF WARRANTY. EXCEPT FOR THE LIMITED WARRANTY AND
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, BENTLEY AND ITS SUPPLIERS PROVIDE
THE SOFTWARE AS IS AND WITH ALL FAULTS, AND TO THE MAXIMUM EXTENT PERMITTED BY
APPLICABLE LAW IN YOUR JURISDICTION, BENTLEY AND ITS SUPPLIERS DISCLAIM ANY AND ALL
OTHER WARRANTIES, FOR ITSELF AND FOR ALL SUPPLIERS, EITHER STATUTORY, EXPRESSED OR
IMPLIED, INCLUDING, W ITHOUT LIMITATION, WARRANTIES OF GOOD TITLE, WARRANTIES AGAINST
INFRINGEMENT, AND THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE. THIS LIMITED WARRANTY GIVES YOU SPECIFIC RIGHTS; YOU MAY HAVE
OTHER RIGHTS, WHICH VARY AMONG JURISDICTIONS.

21.

HIGH RISK ACTIVITIES. The Software is not fault tolerant and is not designed, manufactured or intended for use or
resale as control equipment in hazardous environments requiring fail-safe performance, such as in the operation of
nuclear facilities, aircraft navigation or communication systems, air traffic control, direct life support machines, or
weapons systems, in which the failure of the Software could lead directly to death, personal injury, or severe physical or
environmental damage ("High Risk Activities"). Accordingly, Bentley and its Suppliers specifically disclaim any express
or implied warranty of fitness for High Risk Activities.

22.

END USER REMEDIES. If a defect in the Software appears that constitutes a breach of the above Limited Warranty,
Bentley shall, at its sole option, repair the Software, refund the price you paid for the Software or replace the defective
item(s), provided that: (i) you notify Bentley of the defect during the Warranty Period; (ii) the Software is not modified,
changed, or altered by anyone other than Bentley, unless authorized by Bentley in writing; (iii) your computer
equipment is in good operating order and the Software is installed in an officially supported environment; and (iv) the

non-conformity is not caused by a third party or by you, your agents, employees or contractors. Repaired, corrected, or
replaced Software shall be covered by this limited warranty for the period remaining under the warranty covered by the
original Software, or if longer, for thir ty (30) days afte r the date: (a) of installation by you of the repaired or replaced
Software, or (b) Bentley advised you how to operate the Software so as to achieve the functionality described in the
documentation. YOU AGREE T HAT THE FOREGOING CONSTITUTES YOUR SOLE AND EXCLUSIVE REMEDY
FOR BREACH BY BENTLEY OF THE LIMITED WARRANTY MADE IN THIS EULA. Outside the United States,
neither these remedies nor any product support services offered by Bentley are available without proof that you
acquired the accompanying copy of the Software from an authorized source outside the United States.
23.

LIMITATION OF LIABILITY. Regardless of whether any remedy set forth herein fails of its essential purpose by law,
in no event will Bentley or its Suppliers be liable fo r indirect, special, incidental, economic or consequential damages,
regardless of the nature of the claim, including without limitation lost profits, costs of delay, interruption of business,
loss of use, costs of lost or damaged data or documentation or liabilities to third parties arising from any source, even if
Bentley has been advised of the possibility of such damages. In no event shall the liability of Bentley or its Suppliers
exceed the amount paid by you (in the currency used to purchase) for the Software. Some jurisdictions do not allow the
exclusion or limitation of implied warranties or limitation of liability for incidental or consequential damages, so the
above limitation or exclusion may not apply to you. THE PROVISIONS OF THIS EULA ALLOCATE THE RISKS
BETWEEN BENTLEY AND YOu. BENTLEY'S PRICING REFLECTS THIS ALLOCATION OF RISK AND THE
LIMITATION OF LIABILITY SPECIFIED HEREIN .

24.

STATUTORY CONSUMER RIGHTS. Nothing in this EULA is meant to contravene statutory rights that consumers
may have pursuant to local law.

25 .

EXPORT CONTROLS. The Software has been manufactured or developed in the United States of America and
accordingly may be subject to U.S. export control laws, regulations and requirements. Regardless of any disclosure
made by you to Bentley of an ultimate destination of the Software, you must not export or transfer, whether directly or
indirectly, the Software, or any portion thereof, or any system containing such Software or portion thereof, to anyone
outside the United States (including further export if you took delivery of the Software outside the United States)
without first complying strictly and fully with all export controls that may be imposed on the Software by the United
States Government or any country or organization of nations within whose jurisdiction you use the Software. The
countries subject to restriction by action of the United States Government are subject to change, and it is your
responsibility to comply with the United States Government requirements as they may be amended from time to time.
You shall indemnify, defend and hold Bentley harmless for any breach of your obligations pursuant to this Section.

26.

U.S. GOVERNMENT RESTRICTED RIGHTS. If the Software is acquired for or on behalf of the United States of
America, its agencies and/or instrumentalities ("U.S. Government"), it is provided with restricted rights. The Software
and accompanying documentation are "commercial computer software" and "commercial computer software
documentation;' respectively, pursuant to 48 C.ER. 12.212 and 227.7202, and "restricted computer software" pursuant
to 48 C.ER. 52.227-19(a), as applicable. Use, modification, reproduction, release, performance, display or disclosure of
the Software and accompanying documentation by the U.S. Government are subject to restrictions as set forth in this
Agreement and pursuant to 48 C.ER. 12.212,52.227-19, 227.7202, and 1852.227-86, as applicable. Contractor!
Manufacturer is Bentley Systems, Incorporated, 685 Stockton Drive, Exton, PA 19341-0678.

27.

GOVERNING LAW. This EULA will be governed by and construed in accordance with the substantive laws in force in
the Commonwealth of Pennsylvania. The state courts located in Chester County, Pennsylvania and the federal courts
located in Philadelphia, Pennsylvania shall have exclusive jurisdiction over all disputes relating to this Agreement. To
the maximum extent permitted by applicable law, the parties agree that the provisions of the United Nations
Convention on Contracts for the International Sale of Goods, as amended, and of the Uniform Computer Information
Transactions Act, as it may have been or hereafter may be in effect in any jurisdiction shall not apply to this Agreement.

28.

SEVERABILITY. The provisions of this EULA shall be deemed to be separable and the invalidity of any provision
hereof shall not affect the validity of the remainder of this Agreement.

29.

NOTICES. Please send all notices under this EULA to Bentley Systems, Incorporated, Attn: General Counsel, 685
Stockton Drive, Exton, PA 19341 -0678.

30.

QUESTIONS. Should you have any questions regarding this EULA, please contact the Bentley subsidiary serving your
country, or write to: Bentley Systems, Incorporated, Legal Department, 685 Stockton Drive, Exton, PA 1934 1-0678 .

31.

RE-DISTRIBUTION OF BENTLEY VIEW- . If you are interested in re-distributing Bentley View either internally or
externally to your organization, please complete the online Bentley View Distribution Agreement fou nd at: http://
www.bentley.com/ bentleyview/ redistribute.html.

You might also like