Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Microstation VBA
Jerry Winters
LEARNINGMICROSTATION
VBA
First Edition
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 does 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.
Published by:
Bentley Institute Press
Bentley Systems, Incorporated
685 Stockton Drive
Exton, PA 19341
yww.bentlev.com
Function Object
F u n c t i onIndex Variable
Keyboard key
xvii
xviii I Introduction I
ACCOMPANYINGCD-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.
MYSELECT CD
Bentley SELECT Subscribers can order the supporting files through the
MySELECT CD program. MySELECT CD allows you to select the
Bentley software or documents you need and have a CD delivered to
your door.
To become a Bentley SELECT Subscriber, go to http://1Yww.selectservices.
bentley.com. Bentley SELECT is a subscription program that features
product upgrades and updates.
ABOUTTHE 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 web-
based development.
I Acknowledgments I xix
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 b o x , Maureen Rhoads, and Christopher Rogers, without whom
this book would have never gotten off the ground.
Furthermore, I would like to thank the Bentley Institute for affording
me the opportunity to write about Microstations implementation of
VBA. I hope the lessons learned in this book will be as rewarding to the
reader as they have been for me.
Contents
iii
iv I Contents I
Help Menu ............................................... 3.21
Toolbars ..................................................... 3.22
Standard toolbar .......................................... 3-22
Edit toolbar .............................................. 3.22
Debug toolbar ............................................ 3.22
UserForm 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
Variables ............................................
Standard VBA Variable Types ..................................
6-69
6.70
Integer ................................................... 6-70
Long .................................................... 6-70
Double .................................................. 6.71
Boolean .................................................. 6.72
Date ..................................................... 6-72
String .................................................... 6.72
Object ................................................... 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
WHATIS 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.
1
2 I Chapter 1 : Introducing VBA I
WHY LEARNVBA?
Learn VBA to rapidly develop programs that meet your individual
needs. Much of what you learn in MicroStations 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. A finishes
up the final third of VBA. The X 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.
How Do WE USEVBA?
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, lets create a new
I How Do We Use VBA? I 3
We use the Project Manager to begin new VBA projects and open
existing VBA projects.
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
4 I Chapter 1 : Introducing VBA I
you will find a file named Introductionmvba. Select this file and click
the OK button.
This loads the .mvba file into Microstation and displays it in the VBA
Project Manager.
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. Lets 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
1ntroduction.rnvba file. In the VBAPM, select the VBA Project
1ntroduction.rnvba. 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 5
When you click it, the Macros dialog box opens, which allows you to
select which macro (procedure or function) you want to run.
Select P r o c e d u r e A 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. P r o c e d u r e A draws a line from (0, 0,O) to (10, 10,O) 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 <FS> key.
Its 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+FS> keyboard shortcut. Hold down the <ALT> key and then
press the <FS> key to display the Macros dialog box. Select P r o c e d u r e C
from the list and click the Run button. P r o c e d u r e C draws a square using
6 I Chapter 1 : Introducing VBA I
lines from (0, 0,O) to (10,0,0) to (10,10,0) to (0,10,0) and finally back
to (0, 0,O).
That's all there is to running a Microstation VBA macro. Load it and
run it.
Sub P r o c e d u r e A o
I ********
'* T h i s P r o c e d u r e d r a w s a l i n e f r o m (10, 1 0 , 0) t o ( 3 0 , 1 0 , 0)
I ********
D i m S t a r t p o i n t As P o i n t 3 d
D i m E n d p o i n t As P o i n t 3 d
D i m M y L i n e As L i n e E l e m e n t
StartP0int.X = 0
S t a r t P 0 i n t . Y = 0: S t a r t P 0 i n t . Z = 0
EndP0int.X = 10: EndP0int.Y = 10: E n d P 0 i n t . Z = 0
S e t M y L i n e = CreateLineElementZ(Nothing, ~
S t a r t p o i n t , Endpoint)
A c t i v e M o d e l Reference.AddE1 ement MyLine
End Sub
Some of the applications we write will have no GUI, but we will also
explore the visual side of Visual Basic.
VBA projects are contained in .mvba files. Each file contains code and
can also contain graphical user interfaces. Load and unload VBA
projects using the VBA Project Manager. After the code is written, You
run VBA projects and the code they contain by using the Microstation
menu Utilities > Macro > Macros... or by pressing <ALT+FS> 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.
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. Lets take a more
comprehensive look at what the Project Manager can do for us.
The Project Manager gives us the ability to load existing VBA
projects.
The Project Manager allows us to run the procedures and
functions of projects that have already been loaded.
Start new VBA projects using the Project Manager.
Unload VBA Projects that are already loaded.
Save loaded VBA Projects to a new location and/or a different
file name.
Enter the Visual Basic Editor from the Project Manager.
Record macros from the Project Manager.
Auto-Load VBA Projects so projects are loaded each time
Microstation is started.
9
10 I Chapter 2: The VBA Project Manager I
We can use the Microstation menu to display the VBA Project Manager
or we can hold down the <ALT> key and press the <F8> key to display
the macros that are loaded and are ready to be run.
We need to be careful when discussing the VBA Project Manager. The
term Project Manager is so generic it could be confused with other
products or functionality. For brevity we will refer to the VBA Project
Manager as the VBAPM from time to time throughout this book.
Now that we have identified the VBAPMs functionality in general it is
time to examine it in greater detail.
You are prompted for the location of the new .MVBA file
and for its name.
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 Box
DIALOG
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.
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-Li n e ~ F r o r n ~ O ~ O ~ O ~ t o We
~ l 0 ~can
0 ~ 0name
. it P r o c e d u r e A 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. Its simple. Heres 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-1 into the TextBox and click the
Create button, a new Procedure named ProcedureA-1 is created. Of
course, no code is entered into the procedure after it is created. That is
our job. We can now select ProcedureA-1 from the ListBox, click the Edit
button, and go into the new procedure in the VBA Editor to begin
writing code.
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:
Menus
Toolbars
El Windows
15
16 I Chapter 3: The VBA IDE I
File Menu
Import File imports existing
Ctrl+M form (.frm), module (.bas), and
class (.cls) files into our project.
Export File exports forms,
modules, and classes from our
project to their own .frm, .bas,
and .cls files. After these files have been exported, they can be
imported into another project.
Remove 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.
I Menus I 17
Edit Menu
Undo and Redo are standard
Windows menu items.
Cut, Copy, and Paste perform
standard Windows Clipboard
operations.
Clear deletes selected text or
objects.
Select All selects all text when in a
Code window or all controls when
in a User Form.
The Find, Find Next, and Replace
menu items perform standard Find
and Replace functions.
Indent indents the selected code by *
one tab to the right. Outdent (is
Outdent a real word?) shifts the selected code by one tab to the
left.
List Properties/Methods displays the Properties/Methods list.
List Constants works with an API call that utilizes constants. A
list of the applicable constants is shown.
To use Quick Info, set the cursor on a variable, an object, or an
objects property or method and then click Quick Info to display
the type of object or variable on which the cursor was placed.
Parameter Info displays information about the Method the
cursor is over.
Complete Word shows the list of Constants and Methods in
VBA so we can select something from the list.
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 quicklyjump to a line of code. Bookmarks are not saved
with .mvba projects.
View Menu
El When looking at a user form,
click Code to jump to the code
behind the form.
El Object displays the form
associated with the code we are
looking at.
El 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.
ElLast Position moves the cursor
to the previous line of code the
cursor was in.
El Object Browser, Immediate Window, Locals Window, Watch
Window, Call Stack, Project Explorer, PropertiesWindow, and
Toolbox display a window with the same name.
Insert Menu
Procedure displays the Add Procedure
dialog box to begin new procedures. This
dialog box is most useful for creating new
Properties for Class Modules.
UserForm, Module, and Class Module
inserts these new objects into our project.
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
Debug Menu
The Debug menu allows us to
perform debugging operations
on our code. We will cover this
functionality in Chapter 9
"Standard VBA Calls".
20 I Chapter 3: The VBA IDE I
Run Menu
Run Macro, Break into, and Reset
code execution by using these menu
items in the Run menu.
Design Mode is a standard VBA
*
button that does nothing substantive in
the Microstation implementation of VBA.
Tools Menu
References allows us to add a
1
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.
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.
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 manager with an add-in that has been loaded. Add-
ins can be loaded based on the Load Behavior settings.
Window Menu
Help Menu
We will cover Help issues in the
next chapter. One way to get there
is by clicking the Microsoft Visual
Basic Help menu item.
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 MivoStatmn
Notice how holding your cursor over an icon displays the icons tool tip.
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.
Debug toolbar
The Debug toolbar displays the functionality found in the Debug menu.
I Windows I 23
WINDOWS
Use the toolbars and menu items to display and hide VBA windows.
Lets 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.
In this project we have a form named
UserForm1, a module named Module1 and
a class module named Class1 . The view
shown uses folders to group the common
types of objects.
Object Browser
Properties Window
Watch Window
Locals Window
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 variables 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
D i m MyApp As A p p l i c a t i o n
S e t MyApp = Application
D e b u g . P r i n t MyApp.Caption
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.
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.
OK
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.
I Windows I 29
This is the C l i c k E v e n t of
CommandButtonl. View
Code takes us to the default
event of the control we Private Sub CommandButtonl-Click()
Dim MyApp As Application
right-click over. So, here we Set MyApp = Application
Debug.Print MyApp.Caption
can see the C1 i c k Event. Are DrawL ine
there other events we can End Sub
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. Thats easy. When
learning VBA, however, you dont 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
dont know the correct terminology, you wont 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:
Terminology
Help Files
TheNet
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?
Lets begin by looking in VBA.
Holding your cursor
over a variety of objects
displays a Tool Tip.
There is a ComboBox
m icon in the Toolbox. If
we ask about a
ComboBox we are
more likely to find answers than if we ask about a DropDown. Both
combinations of words may make sense to us, but using the correct
name for the control gets us closer to finding answers to our questions
than using terminology that, although descriptive, is not correct.
Since the terminology used in VBA may be foreign to you when getting
started, it is a good idea to make notes or highlight areas of this book
and other resources when you come across a word or phrase you want to
remember or that you may want to be able to find quickly at a later date.
For example, if you are asked to provide a string, you may produce a
piece of flexible material useful for restricting blood circulation in ones
index finger with the intent of reminding you of something. As for me,
Im 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 fingerless. Or is it finger-free?Digitless?
I Terminology I 33
HELPFILES
One way to display the VBA Help File is to go to the Help menu in VBA
and select Microsoft Visual Basic Help.
Visual Basic
See i\k Spea!!c>
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? Lets begin in the Contents
tab and drill down to the ComboBox starting with the Microsoft Forms
Reference.
I Help Files I 35
Contents tab
ComboBox Control
See Also Example Properties Methods
Events 4pftiimsr
Combines t h e features of a ListBon and a TextBox.
The u s e r can e n t e r a new value, as with a TentBon, o r
t h e user can select a n existing value as with a ListBon.
Remarks
If a ComboBox is bound t o a data source, then t h e
ComboBon inserts t h e value the user enters o r selects
into t h a t data source, If a multicolumn combo b o x is
bound, then the BoundColumn property determines
which value is stored in t h e bound data source,
The list in a ComboBon consists of rows of data. Each
r o w can h a v e one o r m o r e columns, which can appear
with o r without headings. Some applications do not
support column headings, others provide only limited
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.
I Chapter 4: Finding Help I
Index tab
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 dont have the exact terminology, getting close to the correct word
may link you to the correct topic.
I Help Files I 37
Search tab
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 Toolbox Visual Basic ... 2
Unable to unload withi... Visual Basic ... 3
you enter combobox, you are UnderstandingObject... Visual Basic ... 4
MatchFound, MatchR... Microsoft Fo... 5
returned a listing of 67 topics. Layout Event, OldLeft... Microsoft Fo... 6
Style Property Microsoft Fo... 7
Some topics are properties such as DblClick Event, CanP... Microsoft Fo... 8
ComboBox Control, A,.. Microsoft Fo... 9
List Property. Other topics are Ways to put data in a ... Microsoft Fo... 10
List Property Microsoft Fo... 11
instructional, such as Ways to Thingsyou can do wit ... Microsoft Fo... 12
Style Property Example Microsoft Fo... 13
put data in a ListBox or MatchEntryProperty, ... Microsoft Fo... 14
AutoTab Property Microsoft Fo... 15
ComboBox. Keep in mind that BoundColumnProperty Microsoft Fo... 16
Text Property Microsoft Fo... 17
most help topics are linked to CurX Property Microsoft Fo... 18
TextColumn Property Microsoft Fo... 19
related topics. So, we could begin Value Property Microsoft Fo... 20
Locked, DropButtonS... Microsoft Fo... 21
with a search for combobox and DropDown Method Ex... Microsoft Fo... 22
DragBehavior Property Microsoft Fo... 23
read far more than 67 topics by Listwidth Property Ex... Microsoft Fo... 24
ListRowsProperty Ex... Microsoft Fo... 25
jumping to other help topics. Linecount Property Microsoft Fo... 26
Add items to a list usin... Microsoft Fo... 27
Listlndex Property Microsoft Fo... 28
Liststyle Property Microsoft Fo... 29
Favorites tab
t data in a Listsox or
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:Finding 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 bookmarkedtopics 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 of VBA. What about the Y?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 Files I 39
Microstation V8
Visual Basic for
Applications
p Loading and runninga VBA macro Visual Basic and
VBA are thoroughly
Frequentlyasked MicroStationVBA Questions modern object-
oriented
Recording and RevisingMacros programming
environments used
Automatingcommon MicroStationtasks by both professional
Working with MicroStationObjects application
Working with MicroStationEvents developers and
Customizing MicroStationwithvisual Basic casual
programmers.
Convertingfrom MicroStationBasic toVBA Visual Basic is the
primary
Changes for MicroStationVBXMEdition development
platform f o r a large
number of
commercial
products, some of
which you may use
on a daily basis.
Visual Basic f o r
Applications shares
most of Visual
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 Chapter 4:Finding Help 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 <F1> key on the keyboard.
THENET
Got Net? http://msdn.microsoft.com/isv/technology/vba/default.aspx
gets us to Microsofts 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
Microstations VBA-specific implementation here, you will find many
other examples on how to accomplish specific tasks in the VBA
environment.
I The Net I 41
Google, Yahoo, and other search engines can unlock the rest of the
Internets 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?
~ ~ L ~ C ~ ~Laz;?s
________________ ~ _________
r u- C
i ~~ en s~ i ~W ~~ rr i~ ~tinbiMl ~~i ~~~ ~ . u V~8 t ~ ~ ~ ~ R
Th!s technical Dociiment provides s description #f the variables in MicroStation V8 !Ziiii? Editiun, V8 0
and di3.t)
..............................................................
.............................................................
You can access any of these groups via must papular discussion gruup neimre
from a Web browser-, visit htip:i/discus5ion,ien:lry , c o n .
M ~ c ~ ~ S t a ~ ~ ~ n
.
R e ntle f n?i c m stati o n v 8 >. ne a rl Y a cce s s I ne A 2
~
BROWSER
THEOBJECT
We talk to Microstation through its Object Model. The top level of the
Microstations 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
I The Object Browser I 43
displays its properties, methods, and events in the listbox on the right
side of the window.
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
Files properties, methods, and events in the Members list on the right.
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.
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
In this Chapter:
Modules
Forms
Classes
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. Lets write some code.
Lets begin by creating a new VBA project named Chapter 05. Save it in
the folder C:\MicroStution VBA. After this new project is created, you can
see that a code module named Module 1 is created automatically.
Rename this module modCh05.
Continue by creating a new procedure named Main. Inside the code
module, type
Sub M a i n 0
Sub M a i n 0
Declare Variables
D i m M y L i n e As L i n e E l e m e n t
D i m MyCir As E l l i p s e E l e m e n t
D i m C e n P t As P o i n t 3 d
D i m L i n e S t As P o i n t 3 d
D i m L i n e E n As P o i n t 3 d
D i m R o t M a t r i x As M a t r i x 3 d
Create Horizontal Line
LineSt.X = -1
LineEn.X = 1
S e t MyLine = Application.CreateLineElernent2(Nothing. LineSt, LineEn)
A p p l ic a t i on . A c t i v e M o d e l R e f e r e n c e . A d d E 1 e m e n t M y L i n e
Create Vertical Line
LineSt.X = 0: L i n e S t . Y = 1
I Modules I 47
LineEn.X = 0: LineEn.Y = -1
Set MyLine = Application.CreateLineElernentZ(Nothing, L i n e S t . L i n e E n )
Appl ication.ActiveMode1 Reference.AddElement MyLine
'Create Circles
Set MyCir = Application.CreateEllipseElement2(Nothing, ~
wish.
If we need to draw the target centered at (4,5,0) we can copy and paste
the code, and rename the procedure to Mai n2. And then we can create
The more M a i n 3 with different coordinates, then Main4, then M a i n 5 and so on.
hard-coding
we do, the less Right? Well, we could do that but there is a better way.
offen we will Let's change the way we are doing things a little bit. Instead of having
be able to run
our macros. Mai n 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.
The code from Mai n has been copied and pasted into the same code
module. The new pasted procedure is then renamed D r a w T a r g e t . The
goal here is to make our code more flexible, so we can draw targets
anywhere we specify. Our new procedure, D r a w T a r g e t requires us to
specify three parameters named CenX, CenY, and CenZ: We declare
them as doubles (very precise numbers). Lets take a look at how we
use it.
I Forms I 49
Sub Main0
Draw T a r g e t s
DrawTarget 0, 0, 0
DrawTarget 3, 0, 0
DrawTarget -3, 0, 0
DrawTarget 0, 3, 0
DrawTarget 0, -3, 0
End S u b
You only need to enter one line of code to use both the D r a w T a r g e t
procedure and the values entered into the forms 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 dont want the
user to enter the VBA environment to run this program, so we will make
another change to the procedure named Ma in.
Sub M a i n 0
D i s p l a y the Form
frmCh05.show
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 51
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.
Thats pretty powerful and it meets our needs today. What happens,
however, if a year from now we find we want to change the targets size?
The procedure DrawTarget 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 doesnt 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.
Lets add a new class module to our project. Do this by using the VBA
menu Insert > Class Module. Name it &Target (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.
P u b l i c X As D o u b l e
P u b l i c Y As Double
P u b l i c Z As Double
I Classes I 53
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.
P u b l i c X 85 D o u b l e
P u b l i c Y As D o u b l e
P u b l i c Z As D o u b l e
Sub Draw!)
D e c l a r e FsriSJ31es
D i m N y L i n e As L i n e E l e m e n t
D i m N y C i r As EllipseElement
D i m C e n P c As P o l n t S d
D i m L i n e s t As P o l n c 3 d
D i m L i n e E n As P o l n t 3 d
D i m R O t N a t r i x 83 N a t r l x a d
Create H n r i z o n s a l L i o e
L1neSt.X = X - 1
L1neSt.Y = Y
Llne5t.Z = Z
L1neEn.X = X + I
LineEn.Y = Y
L1neEn.Z = Z
Set N y L l n e = B p p l i c B t i ~ n . C T e a t e L i n = E l = m = n t 2( N o t h i n g , L l n e S c , L l n e E n )
AppliCatiOn.ACtIveModelRefeTence.*ddEl~m~nt N y L i n e
i r e a i r vertzzai Line
Line5t.X = X
LineSt.Y = Y + 1
L1neSt.Z = Z
L1neEn.X = X
L1neEn.Y = Y - 1
L1neEn.Z = Z
Set N y L i n e = h p p l i c a t i O n . C T e a t e L i n e E l e m e n f 2 ( N o t h i n g , L i n e s t , L l n e E n )
Application.ActiveModelReference.BddEl~m~nt N y L i n e
create Circles
CenPt.X = X
CenPt.Y = Y
CenPt.Z = Z
Set N y C i r = Application.CreateEllipseElemenrZ(Nothing, C e n P t , 0 . 2 5 , 0.25, R o t N a t r i x )
Application.ActiveModelReference.BddElem~nt N y C i r
Set N y C l r = A p p l i c a t i ~ n . C ~ e ~ F e E l l ~ p ~ ~ E( Nl m ~ hml n~ gn, c C2 e n P t , 0 . 5 , 0 . 5 , R o t M a t r i x )
AppliCatiOn.ACtIveModelRefeTence.lddEl~m~nt N y C i r
End S m
P u b l i c X As Double
P u b l i c Y As Double
P u b l i c Z As Double
Sub D r a w ( x As D o u b l e , Y As D o u b l e , Z As Double)
Declare Variables
Dim MyLine A s L i n e E l e m e n t
Dim MyCi r A s El 1 i p s e E l e m e n t
54 I Chapter 5: Modules, Forms, and Class Modules I
D i m C e n P t As P o i n t 3 d
D i m L i n e S t As P o i n t 3 d
D i m L i n e E n As P o i n t 3 d
D i m R o t M a t r i x As M a t r i x 3 d
'Create Horizontal Line
LineSt.X = X - 1
LineSt.Y = Y
LineSt.Z = Z
LineEn.X = X + 1
LineEn.Y = Y
LineEn.Z = Z
S e t MyLine = Application.CreateLineElementZ(Nothing, L i n e S t , L i n e E n )
A p p l ic a t i on . A c t i v e M o d e l R e f e r e n c e . A d d E 1 e m e n t M y L i n e
'Create Vertical Line
LineSt.X = X
LineSt.Y = Y + 1
LineSt.Z = Z
LineEn.X = X
LineEn.Y = Y - 1
LineEn.Z = Z
S e t MyLine = Application.CreateLineElementZ(Nothing, L i n e S t , L i n e E n )
A p p l ic a t i on . A c t i v e M o d e l R e f e r e n c e . A d d E 1 e m e n t M y L i n e
'Create Circles
CenPt.X = X
CenPt.Y = Y
CenPt.Z = Z
S e t MyCir Application.CreateEllipseElement2(Nothing, CenPt,
= ~
0.25, 0.25, R o t M a t r i x )
A p p l ic a t i on . A c t i v e M o d e l R e f e r e n c e . A d d E 1 e m e n t MyCi r
S e t MyCir = Application.CreateEllipseElementZ(Nothing, CenPt, -
0.5, 0.5, R o t M a t r i x )
A p p l ic a t i on . A c t i v e M o d e l R e f e r e n c e . A d d E 1 e m e n t MyCi r
End S u b
I Procedures and Functions I 55
Sub D r a w C i rcl e ( )
Declare Variables
D i m MyCi r As E l 1 i p s e E l e m e n t
D i m C e n P t As P o i n t 3 d
D i m R o t M a t r i x As M a t r i x 3 d
Create C i r c l e
CenPt.X = 0
CenPt.Y = 0
CenPt.Z = 0
S e t MyCir = Application.CreateEllipseElement2(Nothing, CenPt,
0.25, 0.25, R o t M a t r i x )
A p p l i c a t i o n . A c t i v e M o d e 1 R e f e r e n c e . A d d E l e m e n t MyCi r
56 I Chapter 5: Modules, Forms, and Class Modules I
End Sub
D r a w C i rcl e draws a circle at (0, 0,O) with a radius of 0.25. It can be run
by itself without the any other procedure or function.
Sub D r a w C i r c l e 2 ( Radi us A s D o u b l e )
Declare Variables
D i m MyCir As E l l i p s e E l e m e n t
D i m CenPt As P o i n t 3 d
D i m R o t M a t r i x As M a t r i x 3 d
Create C i r c l e
CenPt.X = 0
CenPt.Y = 0
CenPt.Z 0 =
Radius, Radius, R o t M a t r i x )
Appl ic a t i on . A c t i veModel Reference.AddE1 ement MyCi r
End Sub
Sub D r a w C i r c l e 3 ( x As D o u b l e , Y As D o u b l e , Z As D o u b l e , ~
O p t i o n a l Radius As D o u b l e = 1.25)
Declare Variables
D i m MyCir As E l l i p s e E l e m e n t
D i m CenPt As P o i n t 3 d
D i m R o t M a t r i x As M a t r i x 3 d
Create C i r c l e
CenPt.X = X
CenPt.Y = Y
CenPt.Z = Z
Set MyCir = Application.CreateEllipseElernentZ(Nothing, CenPt,
Radius, Radius, R o t M a t r i x )
Appl ic a t i on . A c t i veModel Reference.AddE1 ement MyCi r
End Sub
I Proceduresand Functions I 57
Sub T e s t D r a w C i r c l e3( 1
D r a w C i r c l e 3 2.25, 2.25, 0
D r a w C i r c l e 3 2.25, 2.25, 0, 1.125
End Sub
Sub D r a w C i r c l e 4 ( x As D o u b l e , Y As D o u b l e , Z As D o u b l e ,
P a r a m A r r a y R a d i i 0 As V a r i a n t )
Declare Variables
D i m MyCi r As E l 1 i p s e E l e m e n t
D i m C e n P t As P o i n t 3 d
D i m R o t M a t r i x As M a t r i x 3 d
D i m I As Long
Create Circles
CenPt.X = X
58 I Chapter 5: Modules, Forms, and Class Modules I
CenPt.Y = Y
CenPt.Z = Z
For I = LBound(Radii To U B o u n d ( R a d i i
S e t MyCir Application.CreateEllipseElementZ(Nothing, CenPt,
= -
We dont know how many radius values w ill 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 T e s t D r a w C i r c l e4( )
D r a w C i r c l e 4 1, 1, 0 , 0 . 2 5 , 0.5, 0.75, 1, 1 . 2 5 , 1.5
End Sub
F u n c t i o n P i 0 As D o u b l e
Pi = Atn(1) * 4
End F u n c t i o n
Here is a function named P i . 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, P i , can be used now wherever we need the value of Pi.
The procedure DrawCi r c l e 3 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 P i function we just created.
Sub T e s t P i 0
D i m C i r c l e A r e a As D o u b l e
D i m C i r c l e R a d i u s As D o u b l e
CircleArea = 3.5
C i r c l e R a d i us = Sqr(Ci rcleArea / P i )
DrawCi r c l e 3 2 . 5 , 2.5, 0 , C i r c l eRadi us
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 D r a wC ir c 1 e3.
The function P i we just created does not have any parameters. It does
not need them because the calculation is always the same. Lets 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).
F u n c t i o n R T D ( A n g l e 1 n R a d i a n s As D o u b l e ) As D o u b l e
RTD = AngleInRadians * 180 / P i
End F u n c t i o n
F u n c t i o n D T R ( A n g l e 1 n D e g r e e s As D o u b l e ) As D o u b l e
DTR = AngleInDegrees * P i / 180
End F u n c t i o n
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 w ill 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. Heres what it looks like:
F u n c t i o n P o l a r P o i n t ( X As D o u b l e , Y As D o u b l e , Z As D o u b l e , -
The A n g l e As D o u b l e , D i s t a n c e As D o u b l e ) As V a r i a n t
underscore D i m XChange As D o u b l e
(J character D i m YChange As D o u b l e
allows one XChange = Cos(Ang1e) * Distance
line ofcode to YChange = Sin(Ang1e) * Distance
span multiple
D i m P P o i n t ( 0 To 2) As D o u b l e
lines.
PPoint(0) = X + XChange
PPoint(1) = Y + YChange
PPoint(2) = Z
Polarpoint = PPoint
End F u n c t i o n
I Procedures and Functions I 61
Sub TestPolarPointO
D i m S t a r t C e n As P o i n t 3 d
D i m C e n P t As P o i n t 3 d
D i m R o t M a t r i x As M a t r i x 3 d
D i m X As V a r i a n t
StartCen.X = 2
StartCen.Y = 2
StartCen.Z = 0
S e t MyCir = Application.CreateEllipseElement2(Nothing, -
S t a r t C e n , 1, 1, R o t M a t r i x )
Application.ActiveModelReference.AddE1ement MyCir
D i m R o t A n g l e As D o u b l e
For RotAngle = 0 To 3 6 0 S t e p 3 0
X = PolarPoint(StartCen.X, StartCen.Y, StartCen.Z, ~
D T R ( R o t A n g l e ) , 4)
CenPt.X = X(0)
CenPt.Y = X(1)
CenPt.Z = X(2)
S e t MyCir = Application.CreateEllipseElement2(Nothing, -
C e n P t , 1, 1, R o t M a t r i x )
Application.ActiveModelReference.AddElement MyCir
Next RotAngl e
End Sub
62 I Chapter 5: Modules, Forms, and Class Modules I
What do we get when we run T e s t P o l ar P o i n t ?
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
PolarPoint function and make use of this type.
F u n c t i o n P o l a r P o i n t 2 ( x As D o u b l e , Y As D o u b l e , Z As D o u b l e , -
A n g l e As D o u b l e , D i s t a n c e As D o u b l e ) As P o i n t 3 d
D i m XChange As D o u b l e
D i m YChange As D o u b l e
XChange = Cos(Ang1e) * Distance
YChange = Sin(Ang1e) * Distance
D i m P P o i n t ( 0 To 2 ) As D o u b l e
PolarPoint2.X = X + XChange
PolarPoint2.Y = Y + YChange
PolarPoint2.Z = Z
End F u n c t i o n
Sub T e s t P o l a r P o i n t 2 ( )
D i m S t a r t C e n As P o i n t 3 d
D i m C e n P t As P o i n t 3 d
D i m R o t M a t r i x As M a t r i x 3 d
D i m X As V a r i a n t
StartCen.X = 2
I Procedures and Functions I 63
StartCen.Y = 2
StartCen.Z = 0
S e t MyCir = Application.CreateEllipseElement2(Nothing, ~
S t a r t C e n , 1, 1, R o t M a t r i x )
A p p l i c a t i o n . A c t i v e M o d e 1 R e f e r e n c e . A d d E l e m e n t MyCi r
D i m R o t A n g l e As D o u b l e
For RotAngle = 0 To 3 6 0 S t e p 3 0
CenPt = PolarPointZ(StartCen.X, StartCen.Y, StartCen.Z, ~
D T R ( R o t A n g l e ) , 4)
S e t MyCir = Application.CreateEllipseElement2(Nothing, ~
C e n P t , 1, 1, R o t M a t r i x )
A p p l i c a t i o n . A c t i v e M o d e 1 R e f e r e n c e . A d d E l e m e n t MyCi r
Next RotAngl e
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.
F u n c t i o n GetExcel WS() As O b j e c t
D i m E x c e l A p p As O b j e c t
Set ExcelApp = Getobject(, "Excel . A p p l i c a t i o n " )
S e t GetExcelWS = ExcelApp.activesheet
End F u n c t i o 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 GetThreeVals(x As D o u b l e , Y As D o u b l e , Z A s D o u b l e )
X = l
Y = 2
z = 3
End Sub
This procedure accepts three parameters. Inside the code, we use the
parameters 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 TestGetThreeValsO
D i m A As Double
D i m B As Double
D i m C As Double
A = 100
B = 200
C = 300
GetThreeVals A , B, C
End Sub
GetThreeVal s 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 first technique requires discipline on our part. The second
technique is a more definite method.
Sub G e t T h r e e V a l s P ( X As D o u b l e , Y As D o u b l e , Z A s D o u b l e )
Dim dblX As Double
Dim dblY As Double
Dim d b l Z As Double
dblX = X
dblY = Y
dblZ = Z
dblX = 1
dblY = 2
dblZ = 3
End Sub
Sub G e t T h r e e V a l s 3 ( B y V a l X As D o u b l e , ByVal Y A s D o u b l e , -
ByVal Z A s D o u b l e )
X = l
Y = 2
z = 3
End Sub
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 variables 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.
D i m S t a r t P o i n t X A s Double
P r i v a t e S t a r t P o i n t Y A s Double
P u b l i c S t a r t P o i n t Z A s Double
D i m S t a r t P o i n t X as Double
UserForml.TestVariable = 4.5
We can use the variable Testvariable only by addressing it through the
form and the form must be in scope for this to work.
Modules - Publicly declared variables are in scope for all areas within
the same 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 in the General
Declarations area of modules,
forms, and classes.
In this example, we have declared Option
Explicit in the General Declarations area.
When we attempt to run the macro test shown
above we get an error.
To avoid this error, we need to declare X as a e
double, integer, or long. More on variable types
in the next chapter.
REVIEW
Write code as procedures, functions, or inside user form events.
In procedures and functions utilize required and optional
parameters.
B In functions you can return values, arrays, types, and objects.
I Chapter 5: Modules, Forms, and Class Modules I
In procedures and functions you can make changes to the
variables passed into them as parameters if the parameters are
declared as ByRef.Declare a parameter as ByVal to keep the
variables value from changing.
Declare variables in procedures, functions, and events or in the
General Declarations area. The scope of these variables depends
on where they are declared and what keywords accompany the
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.
What string of characters does it represent?
Is: E
In this Chapter:
Standard VBA Variable Trpes
Microstation-Specific Variable Trpes
Assigning Values and Setting Objects
Arrays
Constants
Variable Names
Option Explicit
Using Variables
69
70 I Chapter 6: Variables I
TYPES
STANDARDVBA VARIABLE
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 object a variable will
be representing. Since we know this, we specify what type of variable we
will use by declaring it.
D i m N as I n t e g e r
N = 7 - 3 - 1
D i m N As S t r i n g
N = "IS"
Integer
D i m PageNumber a s I n t e g e r
PageNumber = 2
Long
D i m M y S a l a r y as Long
MySalaray = 123456
Double
D i m HoursToLearnVBA as D o u b l e
HoursToLearnVBA = 36.25
Sub VariableTestCO
D i m N As Double
N = 1.23456789012346
N = 12.3456789012346
N = 123.456789012346
N = 1234.56789012346
N = 12345.6789012346
N = 123456.789012346
N = 1234567.89012346
N = 12345678.9012346
N = 123456789.012346
N = 1234567890.12346
N = 12345678901.2346
72 I Chapter 6: Variables I
N = 123456789012.346
N = 1234567890123.46
N = 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
D i m ICanLearnThis as Boolean
ICanLearnThis = True
A Boolean data type can hold one of two values: True or False.
Date
D i m XMReleaseDate as Date
XMReleaseDate = 5/19/2006 8:OO:OO AM
String
D i m MyLevelName a s S t r i n g
M y L e v e l Name = u t i 1E l e c t r i c i t y
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
D i m M y E x c e l A p p as O b j e c t
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
D i m P o i n t A r r a y as V a r i a n t
Variables declared as a variant can hold any type of value, point to any
type of object, or even contain an array of values.
VARIABLE
MICROSTATION-SPECIFIC 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. Lets consider some of the variable types specific to
Microstation that we will regularly use.
A ppIicat ion
D i m MSApp As A p p l i c a t i o n
S e t MSApp = Application
DesignFile
D i m MyDGN As D e s i g n F i l e
S e t MyDGN = Application.ActiveDesignFile
The DesignFile object refers to a Microstation DGN file. Top-level DGN
properties and collections are available to us via the DesignFile object.
Get and set the Author, Client, Comments, Company,
Keywords, Manager, Subject, and Title Properties
Get the FormatMajorVersion and FormatMinorVersion
properties
Get the Levels collection
Get the Models collection
Get the Name and Path properties
ModelReference
D i m MyModel As M o d e l R e f e r e n c e
S e t MyModel = Appl i c a t i o n . A c t i v e M o d e 1 Reference
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
D i m M y L e v e l As L e v e l
S e t MyLevel = A p p l i c a t i o n . A c t i v e D e s i g n F i 1e . L e v e l s ( l )
ElementLineWeight
IsActive
IsDisplayed
IsFrozen
El IsLocked
Name
Number
Plot
LineElement
D i m M y L i n e As L i n e E l e m e n t
EllipseElement
D i m M y C i r c l e As E l l i p s e E l e m e n t
D i m R o t M a t r i x As M a t r i x 3 d
Set MyCircle = CreateEllipseElementZ(Nothing, -
Lines, circles, and arcs form the basis of much of the geometry found in
our Microstation files. From Microstations perspective, circles are
essentially ellipses with equal major and minor radii. The code shown
above draws a circle centered at (0, 0,O) with a radius of 1.5.
ArcElement
D i m MyArc As A r c E l e m e n t
D i m R o t M a t r i x As M a t r i x 3 d
S e t MyArc = CreateArcElementZ(Nothing, -
Text Element
D i m M y T e x t As T e x t E l e m e n t
D i m R o t M a t r i x As M a t r i x 3 d
S e t MyText = C r e a t e T e x t E l e m e n t l ( N o t h i n g , " M i c r o S t a t i on V B A " ,
P o i n t 3 d F r o m X Y Z ( O , 0 , 0). R o t M a t r i x )
Application.ActiveModelReference.AddE1ement 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.
L e v e l Name = "Easement"
D i m E a s e m e n t L e v e l As L e v e l
S e t EasementLevel = -
A c t i v e D e s i g n F i l e . A d d N e w L e v e 1 ( L e v e l Name)
Here we have two 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
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.
D i m S t a r t P o i n t ( 0 t o 2 ) as D o u b l e
StartPoint(0) = 4.5
StartPoint(1) = 5.6
StartPoint(2) = 6.7
MyVerticies(O).X = 1
MyVerticies(O).Y = 2
MyVerticies(O).Z = 3
MyVerticies(l).X = 4
MyVerticies(l).Y = 5
MyVerticies(l).Z = 6
S e t MyLi ne = C r e a t e L i neEl e m e n t l ( N o t h i ng, M y V e r t ic i es)
ActiveModel Reference.AddElement MyLine
End S u b
78 I Chapter 6: Variables I
CONSTANTS
A constant is similar to a variable with one significant difference: a
constant value does not change.
C o n s t PI As Double = 3.14159
NAMES
VARIABLE
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.
Variables must begin with an alpha character (A through Z).
Variable names cannot contain spaces.
Name characters are A-Z, 0-9, and - (underscore).
Variable names must be 255 characters or less.
Variable names cannot be keywords (such as Dim,New,Left,
Right,Form,Public).
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:
D i m m y L i n e As L i n e E l e m e n t
D i m t x t e M y T e x t As T e x t E l e m e n t
D i m s t r N a m e As S t r i n g
D i m d b l S t a r t X As D o u b l e
D i m intLevelNumber As I n t e g e r
D i m p t 3 d S t a r t P o i n t As P o i n t 3 d
Case Sensitivity
Consider the following variable declarations:
D i m myLine As LineElement
D i m M Y l i n e As L i n e E l e m e n t
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 owhatafooliam: One way we can
make sure we declare our variables is to use Option Explicit in the
I Using Variables I 81
OK
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 V a r i a b l e T e s t D O
D i m MySalary A s Double
D i m MyHourly A s Double
MySalary = 1234567
MyHourly = MySalary / 52 / 40
2, v b f a l s e , v b f a l s e , vbTrue)
End Sub
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.
7 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()
D i m B o o k T i t l e As S t r i n g
B o o k T i t l e = L e a r n i n g M i c r o s t a t i o n VBA
MsgBox U C a s e ( B o o k T i t 1 e )
MsgBox L C a s e ( B o o k T i t 1 e )
MsgBox L e f t ( B o o k T i t l e , 12)
MsgBox R i g h t ( B o o k T i t l e , 12)
End S u b
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.
83
84 I Chapter 7: Working With Text I
VBA STRINGFUNCTIONS
Let's take a look at each of the VBA functions that deal with text
(Strings) one-by-one.
UCase
Function UCase(Stri ng)
The U Ca s e function converts the supplied string to upper case.
Sub TextWorkOP(
D i m s t r N e w L e v e l As S t r i n g
s t r N e w L e v e l = I n p u t B o x ( " E n t e r New L e v e l N a m e : " )
strNewLevel = UCase(strNewLeve1
A p p l ic a t i on . A c t i v e D e s i g n F i 1 e.AddNewLeve1 s t r N e w L e v e 1
End Sub
sidewalk
Sub TextWork03(
D e b u g . P r i n t LCase("LCase Lowers C a p i t a l L e t t e r s . " )
End Sub
I VBA String Functions I 85
Debug.Print
is used to lcase lowers c a p i t a l l e t t e r s .
place text in
the
Immediate
Window. It is In this example we used text directly in the function instead of assigning
often used to the text to a variable.
display text to
aid in
debugging StrConv
our
F u n c t i o n S t r C o n v ( S t r i n g , C o n v e r s i o n As V b S t r C o n v , -
applications.
To view the [ L o c a l e I D As L o n g ] )
lmmediate
StrConv is used to convert the provided string through a variety of
Window, go
to the VBA parameters. The constant most used with StrConv is 'vbProperCase'.
menu View > Sub T e x t W o r k 0 4 ( )
Immediate D i m B o o k T i t l e As S t r i n g
Window. B o o k T i t l e = " l e a r n i n g m i c r o s t a t i o n vba"
MsgBox S t r C o n v ( B o o k T i t l e , vbProperCase)
End Sub
WeekDayName, WeekDayNumber
F u n c t i o n We e k d a y N a m e(W eekday As L o n g , -
[ A b b r e v i a t e As B o o l e a n = F a l s e ] , -
C F i r s t D a y O f W e e k A s VbDayOfWeek = vbUseSystemDayOfWeek1) -
As S t r i n g
Each day of the week (Sunday through Saturday) has a number assigned
to it. The WeekdayName function takes that number and converts it to the
day's name.
Sub T e x t W o r k 0 5 ( )
D i m TodaysDate A s Dat e
D i m WeekDayNumber A s Long
TodaysDate = N o w
WeekDayNumber = Weekday(T0daysDate)
MsgBox WeekdayName(WeekDayNumber)
MsgBox WeekdayName(WeekDayNumber, T r u e )
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 P
MonthName
F u n c t i o n MonthName(Month As Long, -
[ A b b r e v i a t e As B o o l e a n = F a l s e ] ) As S t r i n g
The MonthName function is similar to the
WeekdayName function but as the name
implies, it returns the name of the month January
instead of the name of the day. February
March
April
Wau
Sub TextWork05B() June
D i m MonthNum As L o n g July
August
F o r MonthNum = 1 To 1 2 September
D e b u g . P r i n t MonthName(MonthNum1 October
November
N e x t MonthNum December
End Sub
Use the Tr im functions to remove spaces from the beginning, end, and
both beginning and end of a string.
Sub TextWorkOG( 1
D i m S t r i n g T o T r i m As S t r i n g
S t r i n g T o T r i m = " T r i m F u n c t i o n s T r i m Space C h a r a c t e r s . "
Debug. P r in t L T r im ( S t r in g T o T r im 1
Debug. P r in t RTr im ( S t r in g T o T r im 1
Debug. P r in t T r im ( S t r in g T o T r im 1
End Sub
I VBA String Functions I 87
StrComp
Function StrComp(String1, String2, -
[Compare A s VbCompareMethod = vbBinaryCompare1)
The need to compare two pieces of text is common. Is "Sidewalk" the
same as "SIDEWALK"? Not always.
Sub TextWork07()
D i m s t r N e w L e v e 1 As S t r i n g
D i m 1 v l E x i s t L e v e l As L e v e l
s t r N e w L e v e 1 = I n p u t B o x ( " E n t e r New L e v e l Name:")
F o r Each l v l E x i s t L e v e l I n A p p l ication.ActiveDesignFile. L e v e l s
I f S t r C o m p ( s t r N e w L e v e 1 , 1 v l E x i s t L e v e l .Name,
~
v b T e x t C o m p a r e ) = 0 Then
MsgBox " T h e l e v e l " & s t r N e w L e v e 1 & " a l r e a d y e x i s t s . "
E x i t Sub
End I f
Next
Application.ActiveDesignFile.AddNewLeve1 s t r N e w L e v e 1
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 the 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. P r i n t StrComp("SWa1 k " , "swal k", vbTextCompare)
Debug. P r i n t StrComp("swa1 k " , "SWal k", vbTextCompare)
Debug.Print StrComp("SWa1 k " , "swal k", vbBinaryCompare)
Debug.Print StrComp("swa1 k " , "SWal k", vbBinaryCompare)
End Sub
88 I Chapter 7: Working With Text I
0
0
-1
1
StrComp 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 Bubbl e S o r t ( 1
D i m s t r N m s ( 0 To 7 ) As S t r i n g
strNms(0) = " J e r r y "
strNms(1) = "Candice"
strNms(2) = "Brandon"
strNms(3) = "Kyle"
strNms(4) = "Benjamin"
strNms(5) = "Jacob"
strNms(6) = "Nathan"
strNms(7) = " O l i v i a "
D i m Madechange As B o o l e a n
D i m tmpName As S t r i n g
D i m I A s Long
Madechange = T r u e
W h i l e Madechange = T r u e
Madechange = F a l s e
F o r I = L B o u n d ( s t r N r n s ) To U B o u n d ( s t r N m s ) - 1
I f S t r C o r n p ( s t r N r n s ( 1 ) . s t r N m s ( 1 + l), -
V b B i n a r y C o m p a r e ) = 1 Then
trnpNarne = s t r N r n s ( 1 )
s t r N r n s ( 1 ) = s t r N r n s ( 1 + 1)
s t r N m s ( 1 + 1 ) = tmpName
Madechange = T r u e
End I f
Next I
Wend
F o r I = L B o u n d ( s t r N m s 1 To U B o u n d ( s t r N m s )
Debug.Print I & " " & strNrns(1)
Next I
End Sub
D i m strNms(0 To 7 ) A s S t r i n g
strNms(0) = "Jerry"
strNms(1) = "Candice"
strNms(2) = "Brandon"
strNms(3) = "Kyle"
strNms(4) = "Benjamin"
strNms(5) = "Jacob"
strNms(6) = "Nathan"
strNms(7) = "Olivia"
The first thing we do is declare an array of strings and give each element
in the array a value.
D i m Madechange A s B o o l e a n
D i m tmpName A s S t r i n g
D i m I As L o n g
Madechange = T r u e
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.
W h i l e Madechange = T r u e
Madechange = F a l s e
F o r I = L B o u n d ( s t r N m s ) To U B o u n d ( s t r N m s ) - 1
I f StrComp(strNms(I), strNms(1 + 11,
vbBinaryCompare) = 1 Then
tmpName = s t r N m s ( 1 )
s t r N m s ( 1 ) = s t r N m s ( 1 + 1)
s t r N m s ( 1 + 1) = tmpName
Madechange = T r u e
End I f
Next I
Wend
F o r I = L B o u n d ( s t r N m s ) To U B o u n d ( s t r N m s )
Debug.Print I & " " & strNms(1)
Next I
The last little segment of code prints out the elements in the strNms
variable array in their sorted condition.
90 I Chapter 7: Working 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 StrComp
function.
Len
Function Len(Expression1
Sub TextWork09(
D i m L e v e l N a m e As S t r i n g
L e v e l N a m e = I n p u t B o x ( " E n t e r new l e v e l name: " & ~
(Must b e 8 c h a r a c t e r s ) " )
I f L e n ( L e v e l N a m e 1 <> 8 T h e n
MsgBox " T h e l e v e l name m u s t b e 8 c h a r a c t e r s . T r y a g a i n . "
End I f
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.
F u n c t i o n L e f t ( S t r i n g , L e n g t h As L o n g )
The Left function 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 T e x t w o r k l o (
D i m F i l e P a t h As S t r i n g
D i m F i l e D r i v e As S t r i n g
F i l e P a t h = A p p l ic a t i o n . A c t i v e D e s i g n F i 1 e . P a t h
F i l e D r i v e = L e f t ( F i 1 e P a t h . 1)
MsgBox " T h e c u r r e n t f i l e i s o n t h e " & F i l e D r i v e & " 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 FileDrive variable. A
MessageBox then displays the FileDrive variable with some other text.
Right
F u n c t i o n R i g h t ( S t r i n g , L e n g t h As L o n g )
I VBA String Functions I 91
Sub T e x t W o r k l l O
D i m FileName As S t r i n g
F i 1 eName = D i r ( " C : \ P r o g r a m F i 1 e s \ B e n t l e y \ M i c r o S t a t i o n \ * . * " , vbArchive)
W h i l e F i l e N a m e <> " "
MsgBox F i l e N a m e & " i s a " & R i g h t ( F i l e N a m e , 3 ) & " f i l e . "
FileName = D i r
Wend
End Sub
F u n c t i o n M i d ( S t r i n g , S t a r t As Long, [ L e n g t h ] )
The M i d 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
specifying how many characters we want to have returned or we can
leave the Length parameter empty and have Mid return all of the
characters following the specified 'Start' character index.
Sub TextWorkl2()
D i m B o o k T i t l e As S t r i n g
B o o k T i t l e = " L e a r n i n g M i c r o S t a t i o n VBA"
D e b u g . P r i n t M i d ( B o o k T i t l e , 3 , 6)
D e b u g . P r i n t M i d ( B o o k T i t l e , 6)
Debug.Print Mid(BookTitle, InStr(1, BookTitle, " ") + 1)
End Sub
92 I Chapter 7: Working With Text I
We used the M i d function three times in
the above example, each time a little
arning
i n g M i c r o s t a t i o n VBA differently. The first time we asked M i d
M i c r o s t a t i o n VBA
to begin at the third character and to
return six characters in all. The second
time we asked for 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 I nSt r 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.
RepIace
F u n c t i o n R e p l a c e ( E x p r e s s i 0 n As S t r i n g , F i n d As S t r i n g , -
R e p l a c e As S t r i n g , -
[ S t a r t A s Long = 11, [ C o u n t A s Long = -11, -
[ C o m p a r e A s VbCompareMethod = v b B i n a r y C o m p a r e 1 ) As S t r i n g
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 T e x t W o r k l 3 0
D i m F i l e P a t h As S t r i n g
D i m F i l e p a t h 2 As S t r i n g
F i l e P a t h = A p p l i c a t i o n . A c t i v e D e s i g n F i l e . F u l l Name
Filepath2 = Replace(FilePath, \ , //I)
MsgBox F i l e P a t h & v b C r & t u r n s i n t o & v b C r & F i l e p a t h 2
End Sub
InStr
Function InStr(CStart1, -
[ S t r i n g l l , CString21, -
[ C o m p a r e As VbCompareMethod = vbBinaryCompare1)
I n St r helps us identify where a character or group of characters appear
in a string. For example, if we look in the string ABCD for string C,
InStr returns the number 3 because C is the third character in
I VBA String Functions I 93
Sub TextWorkl4()
D i m F u l l N a m e As S t r i n g
D i m F i r s t s p a c e As Long
D i m F i r s t N a m e As S t r i n g
FullName = I n p u t B o x ( " E n t e r y o u r f u l l name.")
F i r s t s p a c e = I n S t r ( 1 , FullName, " " )
F i r s t N a m e = L e f t ( F u l l N a m e , F i r s t s p a c e - 1)
MsgBox " Y o u r f i r s t name i s " & F i r s t N a m e & " . "
End Sub
Sub TextWorklS()
D i m F i l e P a t h As S t r i n g
D i m F i r s t F o l d e r P a t h As S t r i n g
D i m S e c o n d B a c k S l a s h As Long
F i 1 e P a t h = A c t i v e D e s i g n F i l e . F u l l Name
SecondBackSlash = I n S t r ( 4 , F i l e P a t h , " \ " )
F i r s t F o l d e r P a t h = L e f t ( F i l e P a t h , SecondBackSl a s h )
MsgBox F i l e P a t h & v b C r & F i r s t F o l d e r P a t h
End Sub
Sub TextWorklG()
Dim T e x t E l e m As T e x t E l e m e n t
Dim MyMod As Model R e f e r e n c e
Set MyMod = Application.ActiveModelReference
Dim MyElems As E l e m e n t E n u m e r a t o r
Set MyElems = MyMod.GetSelectedElements
Dim MyElem As E l e m e n t
94 I Chapter 7: Working With Text I
W h i l e MyElems.MoveNext
S e t MyElem = M y E l e m s . C u r r e n t
S e l e c t Case MyElem.Type
Case m s d E l e m e n t T y p e T e x t
S e t T e x t E l e m = MyElem
I f I n S t r ( 1 , T e x t E l e m . T e x t , " [ B Y ] " ) > 0 Then
TextElem.Text = Replace(TextElem.Text, -
" [ BY I" , " J KW " 1
T e x t E l em. R e w r i t e
End I f
End S e l e c t
Wend
End Sub
InStr Rev
F u n c t i o n I n S t r R e v ( S t r i n g C h e c k As S t r i n g , -
S t r i n g M a t c h As S t r i n g , -
[ S t a r t A s Long = -11, -
[Compare A s VbCompareMethod = v b B i n a r y C o m p a r e 1 ) As Long
I n S t r Rev, as the name implies, looks at the end of a string first instead of
the beginning. This is the reverse of I n S t r which begins looking at the
beginning. Here is one way to use it:
Sub TextWorkl7( 1
D i m F i l e P a t h As S t r i n g
D i m F o l d e r N a m e As S t r i n g
FilePath = ActiveDesignFile. Path
F o l d e r N a m e = M i d ( F i l e P a t h , I n S t r R e v ( F i 1 e P a t h . " \ "+I 1)
MsgBox " T h e c u r r e n t f i l e i s i n t h e " & F o l d e r N a m e & ~
"folder."
End Sub
I VBA String Functions I 95
We get the path of the current file, then use the I n S t r Rev function inside
a Mi d function to get the location of the first backslash we find. Since we
don't want to display the folder name beginning with the backslash, we
add one (1) in our Mid function to get the characters following the
backslash.
Sub TextWorkl8()
D i m F i l e P a t h As S t r i n g
D i m NewTextFilePath As S t r i n g
D i m x S p l i t As V a r i a n t
F i 1 e P a t h = A c t i v e D e s i g n F i l e . F u l l Name
x S p l i t = Spl i t (F i l e P a t h , " \ " )
x S p l it(UBound(xSp1 i t ) )= x S p l i t ( U B o u n d ( x S p 1 it ) 1 & " . e x t r a c t "
NewText F i 1 ePa t h = J o in ( x S p l it , " \ " )
Open N e w T e x t F i l e P a t h F o r O u t p u t A s #1
P r i n t 81, F i l e P a t h
C l o s e 81
End Sub
The variable FilePath contains the path to the active design file. The
variable xSplit is an array Spl i t from FilePath using the backslash (\) as
96 I Chapter 7: Working With Text I
the delimiter. Take the last element in the array (using the UBound
function) and add a new file extension of .extract to it.
Next, Join the array with the backslash (\) as the delimiter into the
variable NewText FilePath.
Lastly, create an ASCII Text file using the NewTextFilePath variable as
the file name. Inside this new file print the contents of the FilePath
variable.
Here is what the
file looks like
when it is opened c:\Microstation vBA\docs\chaptero7.dgn
in Notepad.
Sub TextWorkl9( 1
D i m s t r c h a r a c t e r As S t r i n g
F o r I = 0 To 2 5 5
D e b u g . P r i n t I & vbTab & C h r ( 1 )
Next I
End Sub
I VBA String Functions I 97
-
174 8
come in handy. For example, character 175 -
number 169 is the copyright symbol. 176
177 f
Character number 174 is the registered
symbol. Character 176 is the degrees symbol.
Character 177 is the plus/minus symbol used
for geometric tolerances.
Now that you know that character 169 is the copyright symbol, you can
use it in a message box:
Sub TextworkPo()
D i m s t r C o p y r i g h t N o t i c e As S t r i n g
strCopyrightNotice = " L e a r n i n g M i c r o s t a t i o n VBA " & -
P r i v a t e S u b T e x t B o x l L K e y P r e s s ( B y V a 1 K e y A s c i i As
MSForms.Return1nteger)
S e l e c t Case K e y A s c i i
Case A s c ( " 0 " ) To A s c ( " 9 " )
Case E l s e
KeyAscii = 0
End S e l e c t
End S u b
FormatCurrency
Function ForrnatCurrency(Expression, -
C N u r n D i g i t s A f t e r D e c i m a l As Long = -11, -
C I n c l u d e L e a d i n g D i g i t As V b T r i S t a t e = vbUseDefaul t l , -
C U s e P a r e n s F o r N e g a t i v e N u r n b e r s As V b T r i S t a t e = -
vbUseDefaul t l , -
CGroupDigi t s As V b T r i S t a t e = vbUseDefau1 t l ) As S t r i n g
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( 1
D i m M y S a l a r y As D o u b l e
D i m M y s a l a r y 2 As D o u b l e
MySalary = 123456.78
Mysalary2 = 0.1234
MsgBox FormatCurrency(MySa1ary. 2 , v b f a l s e , v b f a l s e , v b T r u e )
MsgBox FormatCurrency(MySalary, 0 , v b f a l s e , v b f a l s e , v b T r u e )
MsgBox FormatCurrency(MySalary2, 2 , v b f a l s e , v b f a l s e , v b T r u e )
MsgBox FormatCurrency(MySalary2, 2 , v b T r u e , v b f a l s e , v b T r u e )
End Sub
FormatNumber
F u n c t i o n ForrnatNumber(Expression, -
C N u r n D i g i t s A f t e r D e c i m a l As Long = -11, -
C I n c l u d e L e a d i n g D i g i t As V b T r i S t a t e = vbUseDefaul t l , -
C U s e P a r e n s F o r N e g a t i v e N u r n b e r s As V b T r i S t a t e =
vbUseDefaul t l , -
CGroupDigi t s As V b T r i S t a t e = vbUseDefau1 t l ) As S t r i n g
I VBA String Functions I 99
Sub T e x t W o r k 2 2 ( )
D i m M y S a l a r y As D o u b l e
D i m M y s a l a r y 2 As D o u b l e
MySal a r y = 123456.78
Mysalary2 = 0.1234
MsgBox F o r m a t N u m b e r ( M y S a l a r y , 2, v b f a l s e , v b f a l s e , v b T r u e )
MsgBox F o r m a t N u m b e r ( M y S a l a r y , 0 , v b f a l s e , v b f a l s e , v b T r u e )
MsgBox F o r m a t N u m b e r ( M y S a l a r y 2 , 2 , v b f a l s e , v b f a l s e , v b T r u e )
MsgBox F o r m a t N u m b e r ( M y S a l a r y 2 , 2 , v b T r u e , v b f a l s e , v b T r u e )
End Sub
FormatDateTime
F u n c t i o n FormatDateTime(Expression, -
CNamedFormat As VbDateTimeFormat = v b G e n e r a l D a t e 1 ) -
As S t r i n g
Use F o r m a t D a t e T i m e to specify a datehime and how format it. Here are
your options and the results:
Sub T e x t W o r k 2 3 ( )
D i m DateToFormat As Date
DateToFormat = "1/1/2005 4:45 PM"
MsgBox F o r m a t D a t e T i m e ( D a t e T o f o r m a t , vbGeneralDate)
MsgBox F o r m a t D a t e T i m e ( D a t e T o f o r m a t , v b L o n g D a t e )
MsgBox F o r m a t D a t e T i m e ( D a t e T o f o r m a t , v b L o n g T i m e )
MsgBox F o r m a t D a t e T i m e ( D a t e T o f o r m a t , v b S h o r t D a t e )
MsgBox F o r m a t D a t e T i m e ( D a t e T o f o r m a t , vbShortTime)
End Sub
100 I Chapter 7: Working With Text I
Format
Function Format(Expression, [Format], -
C F i r s t D a y O f W e e k A s VbDayOfWeek = v b S u n d a y 1 , -
C F i r s t W e e k O f Y e a r A s VbFi r s t W e e k O f Y e a r = v b F i r s t l l a n l l )
Sub T e x t W o r k 2 4 0
D i m MyPhone As S t r i n g
My P hone = "800 5 5 5 1212 "
MsgBox F o r m a t ( My Phone, " (IHHI) l ~ l ~ l ~ - l1~ l ~ l ~ l ~ "
End Sub
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 <F8> button.
The next section deals with numbers.
Working With Numbers
NUMERIC
FUNCTIONS
VBA makes working with numbers a breeze. It doesnt do all of the work
for us, but we can do a great deal with very little pain.
103
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 TestAdditionSubtractionO
D i m S e l P t As P o i n t 3 d
D i m C e n P t As P o i n t 3 d
D i m CadMsg As C a d I n p u t M e s s a g e
D i m T e x t E l e m As T e x t E l e m e n t
D i m R o t M a t r i x As M a t r i x 3 d
S e t CadMsg = Application.CadInputQueue.Get1nput
Do W h i l e T r u e
S e l e c t Case C a d M s g . I n p u t T y p e
Case msdCadInputTypeDataPoint
Sel P t = CadMsg. P o i n t
E x i t Do
End S e l e c t
Loop
CenPt = SelPt
CenPt.X = CenPt.X + 1
S e t TextElem = Appl i c a t i o n . C r e a t e T e x t E 1 e m e n t l ( N o t h i n g , "l", ~
CenPt, R o t M a t r i x )
ActiveModel Reference.AddElement TextElem
CenPt = SelPt
CenPt.Y = CenPt.Y + 1
S e t TextElem = Appl i c a t i o n . C r e a t e T e x t E 1 e m e n t l ( N o t h i n g , "Z", -
CenPt, R o t M a t r i x )
ActiveModel Reference.AddElement TextElem
CenPt = SelPt
CenPt.X = CenPt.X - 1
S e t TextElem = Appl i c a t i o n . C r e a t e T e x t E 1 e m e n t l ( N o t h i n g , "3", ~
CenPt, R o t M a t r i x )
ActiveModel Reference.AddElement TextElem
CenPt = SelPt
CenPt.Y = CenPt.Y - 1
S e t T e x t E l e m = Application.CreateTextElementl(Nothing, "4", ~
CenPt, R o t M a t r i x )
ActiveModel Reference.AddElement TextElem
End Sub
I Numeric Functions I 105
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 TestAdditionSubtraction.
Mu1tip1ication
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 TestMultiplicationO
Dim DistanceInInches A s Double
Dim DistanceInMM A s Double
DistanceInInches = CDbl (InputBox(Enter distance in inches:))
DistanceInMM = DistanceInInches * 25.4
106 I Chapter 8: Working With Numbers I
MsgBox D i s t a n c e I n I n c h e s & " i s equal t o " & -
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 ( /
) symbol like this: 5 / 2 = 2.5
F u n c t i o n T o M i l e s ( D i s t a n c e 1 n F e e t as D o u b l e ) A s D o u b l e
T o M i l e s = D i s t a n c e I n F e e t / 5280
End F u n c t i o n
Function GetCircleArea(Circ1eRadius A s D o u b l e ) As D o u b l e
D i m P i As D o u b l e
Pi = Atn(1) * 4
GetCircleArea = Pi * CircleRadius 2
End F u n c t i o n
I Numeric Functions I 107
Square Root
Use the Sqr function to get the square root of a number. Here's one way
to use it:
Sub G e t L i n e L e n g t h O
D i m SelElem As Element
D i m LineElem As LineElement
D i m S e l Elems As E l e m e n t E n u m e r a t o r
S e t S e l Elems = ActiveModelReference.GetSelectedElements
W h i l e SelElems.MoveNext
Set SelElem = SelElems.Current
S e l e c t Case S e l E l e m . T y p e
Case m s d E l e m e n t T y p e L i n e
S e t L i n e E l em = S e l E l em
D i m S t P t As P o i n t 3 d
D i m EnPt As P o i n t 3 d
StPt = LineElem.StartPoint
EnPt = LineElem.EndPoint
LineLength = Sqr((StPt.X - EnPt.X) A 2 + ~
(StPt.Y - EnPt.Y) 2)
MsgBox " L i n e f o u n d w i t h l e n g t h o f " & LineLength
End S e l e c t
Wend
End Sub
Sub T e s t S i n C o s O
D i m XChange A s D o u b l e
D i m YChange As D o u b l e
D i m P i As D o u b l e
D i m H y p L e n g t h As D o u b l e
D i m HypAngleDegrees As Double
D i m HypAngleRadians As Double
Pi = Atn(1) * 4
108 I Chapter 8: Working With Numbers I
HypLength = C D b l ( I n p u t B o x ( " E n t e r Hypotenuse L e n g t h : " ) )
HypAngleDegrees = CDbl ( I n p u t B o x ( " E n t e r A n g l e : " ) )
HypAngleRadians = HypAngleDegrees * P i / 180
YChange = HypLength * Sin(HypAng1eRadians)
XChange = HypLength * Cos(HypAng1eRadians)
Debug. P r in t " T e s t S i n Cos ( 1 "
Debug.Print "HypLength = " & HypLength
Debug.Print "HypAngleDegrees = " & HypAngleDegrees
Debug.Print "HypAngleRadians = " & HypAngleRadians
D e b u g . P r i n t "XChange = " & XChange
D e b u g . P r i n t "YChange = " & YChange
End Sub
TestSinCos ( )
HypLength = 1 0
HypAngleDegrees = 3 0
HypAngleRadians = 0 , 5 2 3 5 9 8 7 7 5 5 9 8 2 9 9
XChange = 8 . 6 6 0 2 5 4 0 3 7 8 4 4 3 9
YChange = 5
Let's use the T a n function now. The first example supposes you know the
leg of the triangle along the X axis.
Sub TestTanl( 1
D i m XChange As D o u b l e
D i m YChange As D o u b l e
D i m P i As D o u b l e
D i m H y p A n g l e D e g r e e s As D o u b l e
D i m H y p A n g l e R a d i a n s As D o u b l e
Pi = Atn(1) * 4
XChange = CDbl(InputBox("Enter X Side Length:"))
HypAngleDegrees = CDbl ( I n p u t B o x ( " E n t e r A n g l e : " ) )
HypAngleRadians = HypAngleDegrees * P i / 180
YChange = Tan(HypAng1eRadians) * XChange
Debug.Print "TestTanl( 1"
I Numeric Functions I 109
TestTanlO
XChange = 4
HypAngleDegrees = 36.8699
HypAngleRadians = 0.643501149881057
YChange = 3.00000025679859
Sub T e s t T a n 2 ( )
D i m XChange As D o u b l e
D i m YChange As D o u b l e
D i m P i As D o u b l e
D i m H y p A n g l e D e g r e e s As D o u b l e
D i m H y p A n g l e R a d i a n s As D o u b l e
Pi = Atn(1) * 4
YChange = CDbl(InputBox("Enter Y Side Length:"))
HypAngleDegrees = CDbl ( I n p u t B o x ( " E n t e r A n g l e : " ) )
HypAngleRadians = HypAngleDegrees * P i / 180
XChange = YChange / T a n ( H y p A n g 1 e R a d i a n s )
Debug. P r i n t " T e s t T a n Z ( ) "
Debug.Print "YChange = " & YChange
Debug. P r i n t " H y p A n g l e D e g r e e s = " & HypAngleDegrees
Debug. P r i n t " H y p A n g l e R a d i a n s = " & HypAngleRadians
Debug.Print "XChange = " & XChange
End Sub
TestTan2 ( )
YChange = 3
HypAngleDegrees = 36.8699
HypAngleRadians = 0.643501149881057
XChange = 3.99999965760191
110 I Chapter 8: Working With Numbers I
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 doesnt work. It is
helpful to test our calculations with numbers that give us predictable
results.
Arc Tangent
S i n, Cos, and Tan help when we know the angle involved. If we do not
know the angle, we can get the angle by using A t n (ArcTangent).
Absolute Value
The Abs function gives us the Absolute Value of the supplied number.
Sub TestAbs()
D e b u g . P r i n t The a b s o l u t e v a l u e o f 4 i s & Abs(4)
D e b u g . P r i n t The a b s o l u t e v a l u e o f - 5 i s & Abs(-5)
End Sub
I Numeric Functions I 111
The a b s o l u t e v a l u e o f 4 is 4
The a b s o l u t e v a l u e o f -5 is 5
Above we use the CDbl function to convert the results of the InputBox to
a double.
Sub TestCInt()
Debug.Print CInt(4.56)
Debug.Print CInt(4.23)
Debug.Print C I n t C 4 . 5 6 )
Debug.Print C I n t C 4 . 2 3 )
End Sub
CLng
The C Ln g function works just like the C In t function, except it converts
the provided number to a long. You could ask, If CLng does the same
thing as CInt, which one should I use? That is a good question.
Remember, that a long number can be significantly larger than an
integer. To use C I n t on a number such as 40,000.123 would create an
overflow error. C I n t and C Ln g 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 CInt. If you are assigning a value to a variable declared as a
long, use CLng.
Sub T e s t C L n g ( 1
D e b u g . P r i n t CLng(40000.56)
40001
D e b u g . P r i n t CLng(40000.23) 40000
Debug.Print CLng(-40000.56) -40001
-40000
Debug.Print CLng(-40000.23)
End Sub
The F i x function looks like it works the same as the CInt 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 T e s t F i x ( )
Debug. P r i n t F i x ( 4 0 0 0 0 . 5 6 )
Debug.Print Fix(40000.23)
Debug.Print Fix(-40000.56)
Debug.Print Fix(-40000.23)
End Sub
Sub T e s t D o u b l e ( 1
D i m L i n e L e n g t h As D o u b l e
LineLength = CDbl ( I n p u t B o x ( " E n t e r t h e l i n e l e n g t h : " ) )
End Sub
I Numeric Functions I 113
CInt, CLng, and CDbl 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 Val
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 TestVal ( 1
Debug. P r i n t V a l ( " 4 . 5 " " " )
Debug.Print Val("4.5 inches")
Debug.Print Val ( " $ 5 , 0 0 0 " )
Debug.Print Val ( " 4 5 d e g r e e s " )
D e b u g . P r i n t V a l ( " A p p r o x . 5280 f e e t " )
Debug.Print Val("23 f e e t 12 inches")
End Sub
IsNumeric
Many of the functions we have just reviewed return numeric values.
I s N u m e r i c returns a Boolean value (True or False). It looks at the
parameter and determines if it is numeric.
Sub TestIsNumeric( 1
Debug. P r in t I s Nume r ic ( " 4 . 5 " " " )
Debug. P r in t I s Nume r ic ( " 4 . 5 in c h e s " )
Debug. P r i n t I s N u m e r i ~ ( " $ 5 , 0 0 0 " )
Debug . P r in t Is N ume r ic ( " 4 5 d e g r e e s " )
Debug.Print IsNumeric("Approx. 5280 f e e t " )
Debug.Print IsNumeric("23 f e e t 12 inches")
End Sub
114 I Chapter 8: Working With Numbers I
IsNurneric 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.
Round
C I n t and C Lng 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 TestRound()
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(l.4, 0)
Debug.Print Round(l.5, 0) L
End S u b
Sub TestModl( 1
Debug.Print 5 Mod 2
Debug.Print 7 Mod 3
Debug.Print 23 Mod 7
Debug.Print 280 Mod 2
End S u b
I Numeric Functions I 115
Sub TestSgn( 1
Debug.Print SgnC4.5)
Debug.Print Sgn(0)
Debug.Print Sgn(4.5)
End Sub
Sub TestRnd( 1
D i m I As L o n g
D i m Lower As Long
D i m H i g h e r As Long
D i m P o i n t C e n ( 0 To 1) As P o i n t 3 d
D i m P o i n t E l e m As P o i n t S t r i n g E l e m e n t
Lower = 25
Higher = 50
Randomize
For I = 1 To 3 0 0
PointCen(O).X = Round((Higher - Lower + 1) * R n d ( l ) , 2)
PointCen(O).Y = Round((Higher - Lower + 1) * R n d ( l ) , 2)
PointCen(l).X = PointCen(O).X
PointCen(l).Y = PointCen(O).Y
Set PointElem = ~
Application.CreatePointStringElementl(Nothing, ~
PointCen, True)
ActiveModelReference.AddElement PointElem
Next I
End Sub
116 I Chapter 8: Working With Numbers I
Order of Operations
2 + 5 * 8 / 1 2 + 13 = ?
(2 + 5) * 8 / (12 + 13) = ?
2 + (5 * 8 / (12 + 13)) = ?
Each of these expressions returns a different result. The numbers are the
same and the operations are the same but the 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
should calculate your expressions.
MESSAGE
BOXES
We used MessageBoxes to display some text with an OK button. By
default, the code pauses until the user clicks the OK button.
Sub T e s t M e s s a g e B o x l O
MsgBox "Your h a r d d r i v e w i l l now b e f o r m a t t e d . "
End Sub
117
118 I Chapter 9: Standard VBA Calls I
Sub T e s t M e s s a g e B o x 2 0
D i m MsgResp As VbMsgBoxResul t
MsgResp = MsgBox("Unab1e t o open f i l e . " , VbAbortRetryIgnore)
MsgResp = MsgBox("Format H a r d D r i v e ? " , vbOKCancel )
MsgResp = MsgBox("New L e v e l A d d e d . " , vbOK0nly)
MsgResp = MsgBox("Not Connected t o I n t e r n e t . " , vbRetryCance1)
MsgResp = MsgBox("Do y o u want t o c o n t i n u e ? " , vbYesNo)
MsgResp = M s g B o x ( " C o n t i n u e Reading F i l e ? " , vbYesNoCance1 )
S e l e c t Case MsgResp
Case VbMsgBoxResult.vbAbort
' P l a c e Code Here
Case VbMsgBoxResul t.vbCance1
' P l a c e Code Here
Case V b M s g B o x R e s u l t . v b I g n o r e
' P l a c e Code Here
Case VbMsgBoxResul t . v b N o
' P l a c e Code Here
Case VbMsgBoxResul t.vbOK
' P l a c e Code Here
Case VbMsgBoxResul t . v b R e t r y
' P l a c e Code Here
Case VbMsgBoxResul t . v b Y e s
' P l a c e Code Here
End S e l e c t
End Sub
Sub T e s t M e s s a g e B o x 3 0
D i m MsgResp A s V b M s g B o x R e s u l t
MsgResp = M s g B o x ( " U n a b 1 e t o open f i l e . " , -
vbAbortRetryIgnore + v b c r i t i c a l )
MsgResp = M s g B o x ( " F o r m a t H a r d D r i v e ? " , -
vbOKCancel + v b E x c l a m a t i o n )
MsgResp = MsgBox("New L e v e l A d d e d . " , vbOKOnly + vbInformation)
MsgResp = MsgBox("Do y o u w a n t t o c o n t i n u e ? " , vbYesNo + -
vbQuestion)
End Sub
120 I Chapter 9: Standard VBA Calls I
Sub TestMessageBox40
MsgBox " T e s t i n g T i t l e " , v b c r i t i c a l , " T i t l e Goes H e r e "
MsgBox " T e s t i n g T i t l e " , , " T i t l e Goes H e r e "
End Sub
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 I nputBox returns an empty
string. An empty string is denoted in VBA as two quotation symbols
with no other character between them ("").
I InputBox I 121
S u b TestInputBoxl( 1
Dim InpRet As String
I n p R e t = I n p u t B o x ( " E n t e r Level Name:")
Debug.Print "User entered " & InpRet
End S u b
The I nputBox has additional parameters we can use. We will discuss four
of them here.
S u b TestInputBoxP( 1
Dim InpRet As String
I n p R e t = I n p u t B o x ( " E n t e r Level Name:", -
"Level C r e a t o r " , " S t r i p i n g " , 0, 0)
Debug.Print "User entered " & InpRet
End S u b
OK
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,O
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 Calls I
Now!
The Now function gives the current system date and time. This is useful
to make a datehime stamp. Now returns a Date type value.
Sub T e s t N o w ( )
MsgBox Now
End Sub
DateAdd
Now tells us the current datehime. DateAdd allows us to look into the
future or into the past. Here are a few examples of how to use DateAdd:
Sub T e s t D a t e A d d ( )
D i m NowDate As D a t e
NowDate = Now
D e b u g . P r i n t NowDate & v b T a b & D a t e A d d ( " d " , 4 , NowDate) ' D a y
D e b u g . P r i n t NowDate & v b T a b & D a t e A d d ( " h " , 4 , NowDate) ' H o u r
D e b u g . P r i n t NowDate & v b T a b & D a t e A d d ( " n " , 4 , NowDate) ' M i n u t e
D e b u g . P r i n t NowDate & v b T a b & D a t e A d d ( " s " , 4 , NowDate) ' S e c o n d
D e b u g . P r i n t NowDate & v b T a b & D a t e A d d ( " m " , 4 , NowDate) ' M o n t h
D e b u g . P r i n t NowDate & v b T a b & D a t e A d d ( " w " , 4 , NowDate) 'Week
D e b u g . P r i n t NowDate & v b T a b & D a t e A d d ( " y y y y " , 4 , N o w D a t e ) ' Y e a r
D e b u g . P r i n t NowDate & v b T a b & D a t e A d d ( " q " , 1, N 0 w D a t e ) ' Q u a r t e r
End Sub
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 datehime. Use a positive
I InputBox 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.
If you have two dates and want to know the time interval between them,
use DateDiff. Use the same interval parameters with DateAdd and
DateDi ff.
Sub TestDateDi f f ( )
D i m NowDate A s D a t e
NowDate = Now
D e b u g . P r i n t " D a y s " & vbTab & D a t e D i f f ( " d " , NowDate, " 1 / 1 / 3 0 0 0 " )
D e b u g . P r i n t " H o u r s " & vbTab & D a t e D i f f ( " h " , NowDate, " 1 / 1 / 3 0 0 0 " )
Debug. P r i n t " M i n u t e s " & vbTa b & D a t e D i f f ( " n " , NowDa t e , "1/ 1/3000" )
Debug. P r i n t "Seconds " & vbTa b & D a t e D i f f ( " s " , NowDa t e , "1/ 1/3000" )
Debug. P r i n t " M o n t h s " & v bTa b & Da t e D i f f ( " m " , Now D a t e , " 1/ 1 / 3 0 0 0 " )
D e b u g . P r i n t "Weeks" & vbTab & D a t e D i f f ( " w " , NowDate, " 1 / 1 / 3 0 0 0 " )
Debug. Pr in t " Y e a r s " & vbTa b & D a t e D i f f ( " y y y y " , NowDa t e , " 1/ 1 / 3 0 0 0 " )
Debug. Pr in t " Q u a r t e r s " & vbTa b & D a t e D i f f ( " q " , NowDa t e , " 1/ 1 / 3 0 0 0 " )
End S u b
Timer
The T i me r function tells us how many seconds have transpired since
midnight. This can be useful when testing our applications to find
bottlenecks in the code. If you are working late at night, however, be
careful. At the strike of midnight, the timer function returns a value of 0
(zero) and starts counting seconds all over again.
Sub T e s t T i m e r ( )
MsgBox T i m e r
End Sub
FileDateTime
FileDateTime gives the datehime the specified file was last modified.
Sub T e s t F i 1 e D a t e T i m e ( 1
D i m e x e D a t e As D a t e
exeDate = FileDateTime -
( " C : \ P r o g r a m F i 1 e s \ B e n t l e y \ M i c r o s t a t i o n \ u s t a t i on . e x e " )
MsgBox " M i c r o S t a t i o n D a t e / T i m e : " & exeDate
End Sub
Sub T e s t F i 1e L e n ( )
D i m e x e S i z e As L o n g
exeSize = FileLen
( " C : \ P r o g r a m F i 1 e s \ B e n t l e y \ M i c r o s t a t i o n \ u s t a t i on . e x e " )
MsgBox " M i c r o S t a t i o n S i z e : " & exesize
End Sub
ruse M k D 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 InputBox I 125
Sub TestMkDi r ( 1
MkDi r c : \ M i c r o s t a t i o n V B A \ S o u r c e C o d e
End Sub
RmDir
RmDi r removes a directory from the file system. The directory must be
empty, otherwise an error occurs.
Dir
The D i 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). D i r only returns one file/folder at a time. If you are looking
for a group of files or folders, call D i r again and leave the parameters
empty. When D i 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 pathhame to look for, you can specify the type of file/folder.
Since there is a great deal that you can do with the D i r function, we will
look at several examples and the results of the code.
Sub T e s t D i r l ( )
D i m R o o t P a t h As S t r i n g
D i m D i r R e t u r n As S t r i n g
RootPath = C:\Program Files\Bentley
DirReturn = Dir(RootPath & \*.*, vbDirectory)
Whi 1 e D i r R e t u r n <>
Sub T e s t D i r2( I
D i m R o o t P a t h As S t r i n g
D i m D i r R e t u r n As S t r i n g
RootPath = "C:\Program Files\Bentley\MicroStation"
DirReturn = Dir(RootPath & "\*.*"I
While DirReturn <> "I'
Sub T e s t D i r3( I
D i m R o o t P a t h As S t r i n g
D i m D i r R e t u r n As S t r i n g
I InputBox I 127
D i m D g n F i l e s O As S t r i n g
ReDim D g n F i l e s ( 0 ) A s S t r i n g
Root Path = " C : \ M i c r o St a t ion V B A\ Docs "
DirReturn = Dir(RootPath & "\*.dgn")
Whi 1 e D i r R e t u r n <> " "
DgnFiles(0) "C:
WlicroStation VBAIDocskhapter03.dgn"
DgnFiles(1) "C:
WlicroStation VBAIDocskhapter04.dgn"
DgnFiles(2) "C:
WlicroStation VBAIDocskhapterO6.dgn"
DgnFiles(3) "C:
WlicroStation VBAIDocskhapter07.dgn"
DgnFiles(4) "C:
WlicroStation VBAIDocskhapter08.dgn"
DgnFiles(5) "C:
WlicroStation VBAIDocskhapterO9.dgn"
S u b T e s t K i 11 ( )
K i l l "C:\MicroStation VBA\Docs\killtest.txt"
End S u b
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 T e s t s a v e s e t t i n g ( )
S a v e s e t t i n g " L e a r n i n g M i c r o s t a t i o n VBA", "Chapter 9",-
" S a v e s e t t i n g " , "It Works"
End Sub
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 Se t t in g.
Sub T e s t G e t S e t t i n g (
D i m R e g S e t t i n g As S t r i n g
Regsetting = G e t S e t t i n g ( " L e a r n i n g M i c r o s t a t i o n VBA". "Chapter 9 " . ~
"Savesetting")
D e b u g . P r i n t "The Key S a v e s e t t i n g v a l u e i s " " " & RegSetting & " " " "
End Sub
I InputBox 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.
S u b TestDeleteSettingl()
D e l e t e s e t t i n g " L e a r n i n g M i c r o s t a t i o n VBA",
"Chapter 9", " S a v e s e t t i n g "
End S u b
S u b TestDeleteSetting2()
D e l e t e s e t t i n g " L e a r n i n g M i c r o s t a t i o n VBA", "Chapter 9"
End S u b
S u b TestDeleteSetting3()
D e l e t e s e t t i n g " L e a r n i n g M i c r o s t a t i o n VBA"
End S u b
GetAllSettings
GetAl 1 Settings, as the name implies, gets all keys under the specified
app name and section and places them into a multi-dimensional array.
S u b TestGetSettings( 1
D i m A l l s e t t i n g s As V a r i a n t
A l l s e t t i n g s = GetAllSettings("Learning M i c r o s t a t i o n V B A " , -
" C h a p t e r 9")
End S u b
130 I Chapter 9: Standard VBA Calls I
AIISettings(0)
AIISettings(0,O) "SaveSetting"
AIISettings(0,l) "k Works"
AIISettings(1)
AIISettings(1 ,0) "SaveSettingZ"
AIISettings(1 ,I) "k WorksZ"
AIISettings(2)
AIISettings(2,O) "SaveSettingS"
AIISettings(2,l) "k WorksS"
Sub TestWriteASCIIBO
Open " C : \ o u t p u t . t x t " F o r O u t p u t As #I
Write 111, " F i r s t l i n e . "
W r i t e 111, "Second l i n e . "
C l o s e 81 " F i r 5 t 1 i ne. "
"second l i n e . "
End Sub
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 Calls I
confused as to which number should be used. This is where F r e e F i 1 e
comes in handy.
Sub TestWriteASCIIDO
D i m F F i l e A As L o n g
D i m F F i l e B As L o n g
FFileA = FreeFile
Open " C : \ o u t p u t a . t x t " F o r A p p e n d As # F F i l e A
P r i n t #FFileA, " A n o t h e r l i n e 1."
P r i n t #FFileA, "Another l i n e 2."
FFileB = FreeFile
Open " C : \ o u t p u t b . t x t " F o r A p p e n d As # F F i l e B
P r i n t IIFFileA, "Another l i n e 3."
P r i n t BFFileB, "Another l i n e 3."
P r i n t BFFileA, "Another l i n e 4."
P r i n t BFFileB, "Another l i n e 4."
C1 o s e # F F i 1 eB
C1 o s e # F F i 1 eA
End Sub
The above example works with two files at the same time. When you use
F r e e F i 1 e, assign the return value to a variable. In this example, I used
FFileA and FFileB as our variable names.
Sub TestWriteASCIIEO
D i m F F i l e A As Long
D i m e x e F i l e As S t r i n g
FFileA = FreeFile
Open " c : \ e x e f i l e s . x m l " F o r O u t p u t As I I F F i l e A
P r i n t B F F i l e A , "<?xml v e r s i o n = " " l . O " " ? > "
P r in t {IFF i1 e A, " < ? ms o - a p p 1 ic a t ion p r o g id= " " Exc e 1 . S h e e t " " ? > " " "
P r i n t BFFileA, -
"com:office:spreadsheet"">"
P r in t ]IF F i 1 eA , "
<Works hee t s s : Name= " " E X E F i 1 e s " " > "
P r i n t IIFFileA, " <Table>"
e x e F i 1e = D ir ( " C : \ W in d ow s \ Sys t e m 3 2 \ * . e x e " 1
While exeFile <> " "
" <Cel l > < D a t a s s : T y p e = " " S t r i n g " " > " & -
1.5,2.5,O,Note 1
34.2,54.12,O,Note 2 1.5,2.5,O,NOte 1
34.2,54.12,O,Note 2
43.2,l. 43, 0,Note 3 43.2,1.43,O,NOte 3
22.3,33.4,O,Note 4
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 L i ne I n p u t 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 ReadASCIIBO
D i m F F i l e As L o n g
D i m T e x t L i n e As S t r i n g
D i m T e x t p o i n t As P o i n t 3 d
D i m X S p l i t As V a r i a n t
D i m T e x t E l e m As T e x t E l e m e n t
D i m R o t M a t As M a t r i x 3 d
FFile = FreeFile
Open " C : \ M i c r o S t a t i o n VBA\TextPoints.txt" F o r I n p u t As # F F i l e
W h i l e EOF(FFi1e) = False
L i n e I n p u t # F F i 1e , T e x t L i n e
XSplit = Split(TextLine, ",")
I Controlling Code Execution I 135
TextP0int.X = XSplit(0)
TextP0int.Y = XSplit(1)
TextP0int.Z = XSplit(2)
Set TextElem = Application.CreateTextElementl(Nothing, ~
N 4
ot
CONTROLLINGCODEEXECUTION
It is essential that we know how to loop through code multiple times and
execute code based on certain conditions.
136 I Chapter 9: Standard VBA Calls I
For ...Next
When you know how many times to loop through a particular block of
code, use a For ... Next statement. Heres a simple example:
Sub ForNextA( 1
D i m I As Long
For I = 1 To 10
ActiveDesignFile.AddNewLeve1 NewLevel & I
Next I
End Sub
After this code is run, 10 new levels are created named NewLevel 1
through NewLevel 10:
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 of 2 (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 F o r N e x t B O
D i m I A s Long
For I = 1 To 1 0 S t e p 2
ActiveDesignFile.AddNewLeve1 NewLevelB & 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,
I 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 F o r N e x t C O
D i m I A s Long
For I = 1 0 To 1 S t e p - 1
ActiveDesignFile.AddNewLeve1 NewLevelC & I
Next I
End Sub
I Controlling Code Execution I 137
I just changed our Step parameter to -1. This means "I" 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 F o r N e x t D ( )
Dim X As D o u b l e
D i m Y As D o u b l e
D i m I n s P t As P o i n t 3 d
D i m C e l l E l e m As C e l l E l e m e n t
For X = 0 To 1 0 S t e p 0 . 2 5
For Y = 0 To 1 0 S t e p 0 . 2 5
1nsPt.X = X
1nsPt.Y = Y
Set CellElem = Application.CreateCellElement3("Column", -
InsPt, True)
ActiveModel Reference.AddElement C e l l Elem
Next Y
Next X
End Sub
While.. .Wend
When using Whi 1 e ... Wend we are uncertain how many times we need to
repeat a block of code. The code between the While and Wend
I Chapter 9: Standard VBA Calls 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.
.
Do.. Loop
Do ... Loop is very similar to the While ... Wend statement. However, it is
more versatile. Here is one example:
InsPt, True)
ActiveModelReference.AddElement Cell Elem
End Sub
I Controlling 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.
B N o t e , N o t e P t , R o t M a t )
A c t i v e M o d e l Reference.AddE1 ement T e x t E l e m
E x i t Do
End S e l e c t
Loop
TextToPl ace = T h e f o l l o w i n g n o t e s s u p e r c e d e a1 1 p r i o r n o t e s .
LineNumber = 1
Do
N0tePt.Y = N0tePt.Y - 0.375
Set TextElem = Application.CreateTextElementl(Nothing, ~
NotePt, RotMat)
A c t i v e M o d e l Reference.AddE1 ement T e x t E l e m
LineNumber = LineNumber + 1
TextToPlace = InputBox(Enter Note:)
Loop W h i l e TextToPl ace <> I
End Sub
This procedure uses two separate Do Loop statements. Lets 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
1. 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 Controlling Code Execution I 141
Sub T e s t F o r N e x t A ( )
D i m d g n L e v e l As L e v e l
F o r Each d g n L e v e l I n A c t i v e D e s i g n F i l e . L e v e 1 s
D e b u g . P r i n t d g n L e v e l .Name
Next
End Sub
NewLevelC 3
When we use For Each ... Next, we NewLevelC 2
NewLevelC 1
specify a variable to use for each
object and then the collection to
look in when we begin the For
Each ... Next statement.
.
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 T e s t I f T h e n A ( 1
D i m L e v e l N a m e As S t r i n g
L e v e l Name = I n p u t B o x ( " E n t e r L e v e l Name ( 3 l e t t e r s o n l y ) " )
I f Len(Leve1Name) = 3 Then
A c t i v e D e s i g n F i l e . A d d N e w L e v e 1 L e v e l Name
End I f
End Sub
If the user enters something with three characters, add the new level. A
very simple implementation of an If.. . Then statement.
Sub T e s t I f T h e n B ( 1
D i m L e v e l N a m e As S t r i n g
L e v e l Name = I n p u t B o x ( " E n t e r L e v e l Name ( 3 l e t t e r s o n l y ) " )
I f Len(Leve1Name) = 3 Then
A c t i v e D e s i g n F i l e . A d d N e w L e v e 1 L e v e l Name
Else
142 I Chapter 9: Standard VBA Calls I
MsgBox L e v e l N a m e & " has " & Len(Leve1Name) & -
" characters."
End I 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 when the entry
has anything other than three characters in it.
Sub T e s t I f T h e n C ( )
D i m L e v e l N a m e As S t r i n g
LevelName = I n p u t B o x ( " E n t e r L e v e l Name ( 3 l e t t e r s o n l y ) " )
I f Len(LevelName1 = 3 Then
ActiveDesignFile.AddNewLeve1 L e v e l Name
E l s e I f Len(LevelName1 > 3 Then
A c t i v e D e s i g n F i 1 e.AddNewLeve1 L e f t ( L e v e l Name, 3 )
Else
MsgBox L e v e l N a m e & " h a s " & L e n ( L e v e 1 N a m e ) & -
" characters."
End I f
End Sub
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 I f ... Then statement with
multiple E l s e I f statements or a S e l e c t Case statement.
Sel e c t Case lets us provide the condition and then multiple possible
matches for the condition.
Sub T e s t S e l e c t C a s e A ( )
D i m L e v e l N a m e As S t r i n g
LevelName = I n p u t B o x ( " E n t e r L e v e l Name:")
S e l e c t Case U C a s e ( L e f t ( L e v e 1 N a m e . 11)
Case " A "
I Controlling Code Execution I 143
In this example, look at the first 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, prepend 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( 1
On E r r o r GoTo e r r h n d
D i m L i n e L e n g t h As Double
LineLength = CDbl ( I n p u t B o x ( " E n t e r L i n e L e n g t h : " ) )
E x i t Sub
errhnd:
S e l e c t Case E r r . N u m b e r
Case 13 ' T y p e M i s m a t c h
MsgBox " L i n e L e n g t h s m u s t b e n u m e r i c . "
Err . C l e a r
End S e l e c t
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 dont handle
the error, the user sees this:
On E r r o r GoTo e r r h n d
This tells VBA that if an error is encountered jump to the area of code
labeled errhnd: Here it is:
errhnd:
S e l e c t Case E r r . N u m b e r
Case 1 3 T y p e M i s m a t c h
MsgBox L i n e L e n g t h s m u s t b e n u m e r i c .
Err.Cl e a r
End S e l e c t
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
Select 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. Lets go back to TestSelectCaseA covered a few
pages ago. Run that macro and enter aad: Everything should run fine.
Run it again and enter aad: What happens?
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 TestErrorHndB( 1
On E r r o r GoTo e r r h n d
D i m LineLength As Double
LineLength = CDbl ( I n p u t B o x ( E n t e r L i n e L e n g t h : ) )
E x i t Sub
errhnd:
S e l e c t Case Err.Number
Case 13 T y p e M i s m a t c h
MsgBox L i n e L e n g t h s m u s t be n u m e r i c .
Err . C 1 e a r
Resume N e x t
End S e l e c t
End Sub
146 I Chapter 9: Standard VBA Calls I
TestErrorHndB isidenticalto TestErrorHndAexceptforoneline.Addinga
Resume Next statement in TestErrorHndB executes our procedure to
continue the line after the error occurred.
Sub T e s t E r r o r H n d C ( )
On Error GoTo e r r h n d
D i m L i n e L e n g t h As D o u b l e
LineLength = CDbl ( I n p u t B o x ( " E n t e r L i n e L e n g t h : " ) )
E x i t Sub
errhnd:
S e l e c t Case Err.Number
Case 1 3 ' T y p e Mismatch
MsgBox " L i n e L e n g t h s must be n u m e r i c . "
Err.Cl e a r
Resume
End S e l e c t
End Sub
Sub T e s t E r r o r H n d D ( )
On Error Resume N e x t
D i m L i n e L e n g t h As D o u b l e
LineLength = CDbl ( I n p u t B o x ( " E n t e r L i n e L e n g t h : " ) )
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 T e s t E r r H n d E ( )
On Error Resume N e x t
D i m MyExcel As O b j e c t
S e t MyExcel = G e t o b j e c t ( , "Excel . A p p l i c a t i o n " )
I f Err.Number <> 0 Then
I Controlling Code Execution I 147
Err.Cl e a r
S e t MyExcel = CreateObject("Exce1 . A p p l i c a t i o n " )
End I f
On E r r o r GoTo e r r h n d
MyExcel . V i s i b l e = True
MsgBox MyExcel . A c t i v e S h e e t . N a m e
E x i t Sub
errhnd:
MsgBox "Error " & Err.Number & " has o c c u r r e d . " & vbCr & -
Err.Oescription, v b c r i t i c a l , " E r r o r I n TestErrHndE"
Err.Cl e a r
End S u b
"On Error G o t o 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 fx the bug, you can comment out the "On Error G o t o 0"
line so your Error Handling code is at work again.
We covered a number,
although not a
comprehensive list, of
useful and commonly
used VBA calls. You can Cglobalsa
Collection
use the Object Browser ColorConstants
in VBA to display all Constants
...Conversion
VBA calls natively ..................................................
;DateTime
.....................................................
Errobject
available to us. We will Filesystem
discuss the Object Financia I
FormShowConstar
Browser later in this Global
book. Here is a snapshot Information
Interaction
of what you will see if KeyCodeConstantr
Math
you filter on the VBA
Reference, the DateTime
Class, and the D a t e D i f f
member.
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 <F1> key.
DateDiff Function
See Also Example Speri!ics
Returns a Variant (Long) specifying the number of time intervals between two specified
dates.
Syntax
DateDiff(interva1, d a t e f , date2[. firstdayofweek[. firstweekofyear]])
The DateDiff function syntax has these named arsuments:
date1 , date2 Required; Variant (Date).Two dates you want to use in the
calculation.
firstda yofweek Optional. A constantthat specifies the first day of the week. If
not specified, Sunday is assumed.
firstweekofyear Optional. A constant that specifies the first week of the year.
If not specified, the first week is assumed to be the week in
which January 1occurs.
Settings
The interval arsument has these settings:
Many procedures and functions are built into VBA. You do not need to
write a function that tells the current date and time because we have the
Now function. Similarly, you do not need to write complex code that
stores your application information in the Windows registry as you can
use the Savesetting and Getsetting procedures.
10 Visual Interface
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. Lets 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. Well get into that a
little later. First, lets talk about
the controls we can add to our
user forms.
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.
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 XddItem 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 153
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.
M s g B o x CheckBoxl.Value
NewLevel Name = txtLevel Name.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.
Control Events
Now, lets look at using control events. You write code for control events
in the forms 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 (CommandButtonl is selected below), we can then select
an event in the right-hand ComboBox (the click event is selected
below).
When you select an event not previously selected, VBA fills in the
framework of the event for us.
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.
P r i v a t e S u b CommandButtonl-MouseDown(ByVa1 B u t t o n A s I n t e g e r , -
ByVal S h i f t AS I n t e g e r , -
ByVal x AS s i n g l e , ByVal Y AS s i n g l e )
End Sub
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.
COMMONCONTROLPROPERTIES
Before discussing each control individually, lets talk about properties
and events that nearly all controls have in common.
156 I Chapter 10: Visual Interface I
Name
Whats 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 = E n t e r L e v e l Name:
XVal = txtXValue.Text
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,O). 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 single-
line 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.
Ta blndex
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
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.
Cont rolTipText
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.
Labe1
Labels help users know what to enter or select. Properties of note are as
follows:
TextBox
The TextBox allows users to enter text or display text, often in single-
line 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
Locked
True: users cannot. When set to True; text in the
TextBox can be selected and copied to the Windows
Clipboard even though the text cannot be changed by
the user. When Locked, you change the text property
MaxLength
COMBOBOX
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
Methods
LISTBOX
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
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
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
Events
Click When user clicks 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 Triplestate mode).
Change
OPTIONB m o ~
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.
I Toggle Button I 163
Properties
Caption
Group Name
selected. To allow a user to select "Large': "Medium':
"Small" for a size and to allow them to select "Red':
"White", "Blue" for the color, use two group names for
each group of OptionButtons.
Events
BUTTON
TOGGLE
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
Events
FRAME
Frames are control containers. This means that controls can be placed
inside of them. If a frames 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
Visible
Properties
Events
TABSTRIP
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
TabOrientation
Methods
Events
MULTI
PAGE
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
Pages.Count
MuItiRow
Methods
Events
Change
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
Events
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.
167
Properties
Change
SpinUp
SpinDown
Use the image control to display images in your interface. Acceptable file
formats are .bmp, .gif, .jpg, .wmf, and .ico.
Properties
USERINTERFACE
EXERCISES
We started this chapter by discussing the importance of creating a useful
and intuitive interface, then introduced the standard controls. Now lets
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.
Here is the first interface you are going
to work with. Add the controls
beginning at the top and working down.
The first controls are two ComboBoxes.
By default they are inserted with the
names ComboBox 1 and ComboBox2.
Change the names to cmbLevels and
cmbCells. 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.
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 txtL Name the CommandButton cmdPick 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 I n it i a 1 ize in
the Procedure ComboBox.
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 <F5> 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 type
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 Chapter 10: Visual Interface I
Right-click on the top TextBox and select View Code. This takes us to
the Change e v e n t of the TextBox by default. Selecting K e y p r e s s in the
Procedure ComboBox takes us to the K e y p r e s s e v e n t .
P r i v a t e Sub t x t X L K e y P r e s s ( B y V a 1 -
K e y A s c i i As M S F o r r n s . R e t u r n 1 n t e g e r )
S e l e c t Case K e y A s c i i
Case A s c ( " 0 " ) To A s c ( " 9 " )
Case A s c ( " . " )
If InStr(1, txtX.Text, "."I > 0 Then
I User Interface Exercises I 171
KeyAscii = 0
End If
Case Else
KeyAscii = 0
End Select
End Sub
Exit Sub
End If
Dim InsPt As Point3d
Dim Cell El em As Cell Element
1nsPt.X = CDbl(txtX.Text)
1nsPt.Y = CDbl(txtY.Text)
1nsPt.Z = CDbl(txtZ.Text)
Set CellElem = CreateCellElement3(cmbCells.Text, InsPt, True)
Cell Elem. Level = ActiveDesignFi 1 e. Level s(cmbLeve1 s .Text)
ActiveModelReference.AddElement Cell Elem
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
the "Insert" button after clicking the "PICK" button.
I User Interface Exercises I 173
Sub DoCellInsertionO
frmCellInsertion.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.
CreateCellElement3(cmbCells.Text, -
SelPt, True)
Cell El em. Level = -
ActiveDesignFi 1 e. Level s(cmbLeve1 s .Text)
ActiveModel Reference.AddElement Cell Elem
End If
Exit Do
Case Else
Exit Do
174 I Chapter 10: Visual Interface I
End S e l e c t
Loop
E x i t Sub
errhnd:
Err . C l e a r
End Sub
Lets look through the code slowly. First, we declare some variables.
Thats the easy one. Second, we begin listening to the Input Queue. If the
user picks a point, we do the following:
Place the selected X, Y, and Z point elements into the three text
boxes.
If both 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 LISTREADER
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.
Pointsplit = Split(PointText, , I
176 I Chapter 10: Visual Interface I
1stPoints.AddItem PointSplit(0)
1 stPoints. List(1 stPoints. ListCount - 1 , 1) = PointSpl i t(l)
lstPoints.List(1stPoints.ListCount ~ 1, 2) = PointSplit(2)
lstPoints.List(1stPoints.ListCount ~ 1 , 3) = 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 AddItem to add the X component of the
point. AddItem is only used to add items to the first column of the
ListBox. Each additional columns 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.
Text1ns.Z = lstPoints.List(1 - 1 , 2 )
Set PT = Application.CreateTextElementl(Nothing, -
lstPoints.List(1 - 1 , 3 ) , TextIns, RotMat)
ActiveModel Reference.AddElement PT
Next I
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 Cancelbutton, execute the following code:
Sub D o P o i n t L i s t R e a d e r O
frmPoi ntLi st. Show
End Sub
The macro 0o Po int L is t Re a d e r is now used to display the form and all of
the great functionality we have just put in.
Sub P r i n t H e a d e r ( H e a d e r 1 n As S t r i n g , F i l e N u m As L o n g , ~
O p t i o n a l C o l u m n s As L o n g = 1)
I f optASCII.Value = T r u e Then
P r i n t #FileNum, "['I & HeaderIn & "I"
E l s e I f optHTML.Value = T r u e Then
P r i n t BFileNum, " < t a b l e width=660>"
P r i n t B F i 1 eNum, " < t r > < t d c o l s p a n = " & C o l u m n s & -
" a1 i g n = c e n t e r > < b > " & H e a d e r I n & " < / t d > < / t r > "
End f
End Sub
Use an I and E 1 s e I f statement to handle the two file formats for today.
Another E l seI f statement is all it takes to add another file format
tomorrow.
Sub P r i n t L i n e ( L i n e 1 n As S t r i n g , F i l e N u m As L o n g )
I f optASCII.Value = T r u e Then
P r i n t #FileNum, LineIn
I Write Out File I 179
E l s e I f optHTML.Value = T r u e Then
D i m X S p l i t As V a r i a n t
D i m I As L o n g
XSplit = Split(LineIn, vbTab)
P r i n t #FileNum, "<tr>"
F o r I = L B o u n d ( X S p 1 i t ) To U B o u n d ( X S p 1 i t )
P r i n t #FileNum, vbTab & " < t d > " & X S p l i t ( 1 ) & " < / t d > "
Next I
P r i n t #FileNum, "</tr>"
End I f
End Sub
Use the procedure Pri n t L i ne for each of the selected items found. Use
another I f and E 1 s e I f statement for the file formats.
Sub P r i n t F o o t e r ( F i 1 e N u m As L o n g )
I f optHTML.Value = T r u e Then
P r i n t BFileNum, " < / t a b l e > " & vbCrLf
End I f
End Sub
P r i v a t e Sub c m d O K L C l i c k 0
D i m M y F i l e As S t r i n g
D i m F F i l e As L o n g
D i m m y L e v e l As L e v e l
D i m m y L S t y l e As L i n e S t y l e
D i m m y T S t y l e As T e x t S t y l e
D i m m y V i e w As V i e w
FFile = FreeFile
I f optASCII.Value = T r u e Then
MyFile = "c:\output.txt"
E l s e I f optHTML.Value = T r u e Then
MyFile = "c:\output.htm"
End I f
Open M y F i l e F o r O u t p u t As # F F i l e
P r i n t H e a d e r " F I L E NAME", FFile, 1
I Chapter 10: Visual Interface I
P r i n t L i n e A c t i v e D e s i g n F i 1 e . F u l l Name, F F i 1 e
PrintFooter FFile
If chkLevels.Value = T r u e Then
P r i n t H e a d e r "LEVELS", FFile, 3
F o r Each m y L e v e l I n A c t i v e D e s i g n F i l e . L e v e l s
P r i n t L i n e myLevel.Name & v b T a b & -
myLevel .E l ementCol o r , F F i l e
Next
PrintFooter FFile
End I f
If chkLineStyles.Value = T r u e Then
P r i n t H e a d e r " L I N E STYLES", FFile, 2
F o r Each m y L S t y l e I n ActiveDesignFile.LineStyles
P r i n t L i n e myLStyle.Name & vbTab & ~
myLStyle.Number. FFile
Next
PrintFooter FFile
End I f
If chkTextStyles.Value = T r u e Then
P r i n t H e a d e r "TEXT STYLES", FFile, 3
F o r Each m y T S t y l e I n ActiveDesignFile.TextSty1es
P r i n t L i n e myTStyle.Name & vbTab & -
myTStyle.BackgroundFillColor, F F i l e
Next
PrintFooter FFile
End I f
I f chkViews.Value = T r u e Then
PrintHeader "VIEWS", FFile, 5
F o r Each myView I n A c t i v e D e s i g n F i 1 e . V i e w s
P r i n t L i n e myView.0rigin.X & vbTab & -
myview. CameraFocal L e n g t h , F F i l e
Next
I Write Out File I 181
PrintFooter FFile
End I f
I f chkAuthor.Value = T r u e Then
P r i n t H e a d e r "AUTHOR", FFile
PrintLine ActiveDesignFi le.Author, F F i 1e
PrintFooter FFile
End I f
I f chkSubject.Value = T r u e Then
P r i n t H e a d e r "SUBJECT", FFile
PrintLine ActiveDesignFi le.Subject, F F i 1e
PrintFooter FFile
End I f
If chkTitle.Value = T r u e Then
PrintHeader "TITLE", FFile
P r i n t L i n e A c t i v e D e s i g n F i l e . T i t l e , F F i 1e
PrintFooter FFile
End I f
C1 o s e # F F i 1 e
End Sub
P r i v a t e Sub c r n d C a n c e l L C l i c k 0
Unload frrnWriteDgnSettings
End Sub
Sub D o W r i t e O u t F i l e O
frrnWriteDgnSettings.Show
End Sub
182 I Chapter 10: Visual Interface I
I use the MultiPage control here. This provides tabs and unique
interfaces on each tab. I also use a few labels, a ComboBox, and three
scroll bars with Min values of -500 and max values of 500. When the
form is initialized, I populate the ComboBox with the View indexes. I
also set an initial value for the Pan scroll bars.
When you right-click on an existing tab in the MultiPage, you access
controls to add tabs (select New Page), to rename, delete, or move the
order of the pages.
cmbViews.ListIndex = 0
ViewCen = ActiveDesignFile.Views(l).Center
scrX.Value = ViewCen.X
scrY.Value = ViewCen.Y
End Sub
Here is the Initialize event of the UserForm. We add each Views Index
to the ComboBox named cmbviews. Select the first element by
assigning the ListIndex value to 0. 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
sr cY.
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:
P r i v a t e Sub s c r Z o o m - C h a n g e 0
SetZoom scrZoom.Value, scrZoom.Tag
scrZoom.Tag = scrZoom.Value
End Sub
P r i v a t e Sub s c r Z o o m - S c r o l l 0
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, lets talk about panning. We are using two scroll bars to set the X
and Y elements of the views center.
P r i v a t e Sub s c r X L C h a n g e 0
SetPan s c r X . V a l u e , scrY.Value
End Sub
P r i v a t e Sub s c r X L S c r o l 1 ( 1
SetPan s c r X . V a l u e , scrY.Value
End Sub
P r i v a t e Sub s c r Y - C h a n g e 0
SetPan s c r X . V a l u e , scrY.Value
End Sub
P r i v a t e Sub s c r Y - S c r o l l (1
SetPan s c r X . V a l u e , 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:
All controls have properties, methods, and events.
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.
At run-time, events are triggered as the user interacts with your
interface.
Display user forms using the Show method.
Use the Initialize event to set values and populate controls prior
to the form being displayed.
11 The Microstation Object
Model - Objects
Objects are the basis for 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 this Chapter:
The Object Browser
Auto List Members
i
l The Microstation VBA Help File
El Adding Watches
The Microstation Object Model
187
188 I Chapter 11:The Microstation Object Model - Objects I
BROWSER
THEOBJECT
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
Object Browser
Browser. The Object Browser can also be displayed by using the VBA
menu View > Object Browser or by pressing the <F2> key on the
keyboard.
eglobals> ACSManager
AccuDrawHints ActiveDesignFile
ACSManaaet ActiveModelReference
ActiveSettings
ApplicationElement ActiveWorkspace
ApplicationObjectConnector AddAttachmentEventsHandler
ArcElement AddChangeTrackEventsHandler
AreaPattern AddLevelChangeEventsHandler
Attachment AddModalDialogEventsHandler
Attachments AddModelActivateEventsHandler
AuxiliaryCoordinateSystemElement AddModelChatigeEventsHandler
B s p Ii n e AddSaveAsEventsHandler
BsplineCuns AddViewUpdateEventsHandler
BsplineCunsElement AppendXDatum
Bspline Surface ApplyHorizontalScalingFixForEMF
BsplineSurfaceElement ApplplerticalScalingFixForEMF
CadlnputMessage AssembleComplexStringsAndShapes
CadlnputQueue Atn2
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
Name
Members of Application
OnDesignFileClosed
show up in the Members
0nDe s ig nF i Ie 0pene d
ListBox. The Members
0penDe s ig nF iIe
ListBox displays the
0penDe s ignF iIe F orP ro gra m
Properties, Methods, and
Path
Events of the selected Class.
Pi
I The Object Browser I 189
Notice the cursor over the Hide/Show Search Results button in the
Object Browser. A search for text in the MicroStationDGN Trpe
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
The List Members list displays as we work in VBA. Once the list
displays, we can use the arrow keys and page up/down keys to scroll
through the list. If we select ActiveDesignFileat this time and press the
period key, we see the following:
Dim MyApp As Application
Set MyApp = Application
MsgBox myapp.ActiveDesignFi1e.
End S u b
The Xuto List Members list allows us to drill down through an Object
Model.
MICROSTATION
VBA HELPFILE
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 <F1>
key on the keyboard. We are then presented with information about the
I Microstation VBA Help File I 191
Once in the Microstation VBA Help File, we can click on the Index tab
and type Xpplication Structure in the Search box. Selecting
Xpplication 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
ADDINGWATCHES
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 the list are Collections of Objects that we
can examine in the Watch window.
ACSManager ACSManagerlACSManageI
ActiveDesignFile DesignFileDesignFile
ActiveModelReference ModelReferenceYodelRefi
ActiveSetLings Settingslsetlings
Activeworkspace WorkspacelWorkspace
AltachedCellLibrary <No cell libraryr CellLibrary
Bspline BsplineiBspline
CadlnputQueue CadlnputQueuelCadlnputOl
Caption "chapter11.dgn (20 - V8 DON) - Microslation V8 XM Edition" String
CommandState CommandStatelCommandS
CurrerdGraphicGreup 1 Long
Cursorhiormatnn cwsormiormatiOnlCursorlr
ExecutingVBProject ObjedNBProjed
FullName "C:Wogram Files~entleyVulicroStatien~~~ion
exe" String
HasActiveDesignFile True Boolean
HasAdiveModelReference True Boolean
Height 1208 Long
IsAcademicVersian False Boolean
IsCellLibraryAttached False Boolean
IsRegidered True Boolean
IsSerialized True Boolean
KeyinArguments String
LeitPosnion -4 Long
MdlLib MdlLibraryYdlLibrary
Messagecenter MessageCerderYessageC
Name '"ustation" String
Path '"C:.DrogramFiles~entleyVulicroStation" String
ProcesslD 3160 Long
RasterManager RasterManagerIRasterMan
StandardsCheckerCordroller StandardsCheckerCordrollt
TapPosition -4 Long
UserName '"Administrator" String
VBE ObjectNBE
Version "Version 08.09.00.92Wlndows x 8 6 String
Visible True Boolean
Wdth 1608 Long
THEMICROSTATION MODEL
OBJECT
Let's begin looking at the Microstation Object Model by examining the
Application Object.
I The Microstation Object Model I 193
Application Object
The Application Object points to the Microstation Application.
Accessors
Sub TestAppl i c a t i o n A o
D i m M y A p p A s New A p p l i c a t i o n
M s g B o x MyApp.Path
End S u b
Sub TestAppl i c a t i o n B 0
Dim MyApp As Application
Set MyApp = Application
M s g B o x MyApp.Path
End S u b
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 Applications 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 TestAppl i c a t i o n C 0
M s g B o x Application.Path
End S u b
In this example, we have not declared any variables or set any variables.
We just use the Object named ?Applicationbecause 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. Lets 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 ActiveDesi gnFi 1 e As Desi gnFi 1 e {read-
only1
Property ActiveModelReference As
ModelReference {read-only}
Property Activesettings As Settings {read-
only1
Property Activeworkspace As Workspace {read-
only1
S u b AddAttachmentEventsHandler(EventHand1er As
IAttachmentEvents)
S u b AddChangeTrackEventsHandler(EventHand1er
As IChangeTrackEvents)
S u b AddLevelChangeEventsHandler(EventHand1er
As ILevelChangeEvents)
S u b AddModalDialogEventsHandler(EventHand1er
As IModal Dial ogEvents)
S u b AddModelActivateEventsHandler(EventHand1er
As IModelActivateEvents)
S u b AddModelChangeEventsHandler(EventHand1er
As IModelChangeEvents)
S u b AddSaveAsEventsHandler(EventsHand1er As
ISaveAsEvents)
S u b AddViewUpdateEventsHandler(EventHand1er As
IViewUpdateEvents)
S u b AppendXDatum(XData0 As XDatum, Type As
MsdXDatumType, Value As Variant)
Function
ApplyHorizontal Scal i n g F i x F o r E M F ( P i x e 1 C o o r d i n a t
e As Double) As Long
Function
Applyvertical Scal i n g F i x F o r E M F ( P i x e 1 C o o r d i n a t e
As Double) As Long
Function
Assembl eCompl exStri ngsAndShapes (Chai nab1 eEl eme
n t s 0 As ChainableElement, CGapTolerance As
Double = -11 As El ementEnumerator
I The Microstation Object Model I 195
F u n c t i o n A t n 2 ( Y As D o u b l e , X As D o u b l e ) As
Double
Sub A t t a c h c e l l L i b r a r y ( C e l 1 L i b r a r y N a m e As
S t r i n g , CConvertFromV7 As MsdConversionMode =
msdConversionModeAlwaysl)
P r o p e r t y A t t a c h e d C e l 1 L i b r a r y As C e l l L i b r a r y
{ read-on1y 1
P r o p e r t y B s p l i n e As B s p l i n e { r e a d - o n l y }
F u n c t i o n B y C e l l C o l o r O As Long
F u n c t i on B y C e l l L i n e S t y l e ( ) As L i n e S t y l e
F u n c t i on B y C e l l L i neWei g h t ( As Long
F u n c t i o n B y L e v e l C o l o r O As Long
F u n c t i on By L e v e l L i n e S t y l e ( As L i n e S t y l e
F u n c t i o n B y L e v e l L i n e w e i g h t ( ) As Long
P r o p e r t y C a d I n p u t Q u e u e As C a d I n p u t Q u e u e
{ read-on1y 1
P r o p e r t y C a p t i o n As S t r i n g
P r o p e r t y Commandstate As Commandstate { r e a d -
only}
Function
ConstructCirclesTangentToThreeElements(E1ement
1 As E l e m e n t , E l e m e n t 2 As E l e m e n t , E l e m e n t 3 As
E l e m e n t , Temp1 a t e As E l e m e n t , [ O u t p u t T y p e As
MsdTangentElementOutputType =
msdTangentCi r c l e s l ,
CSamplesCount As Long = 1 0 1 ) As
E l ementEnumerator
Sub Copy D e s i g n F i 1 e ( E x i s t i n g D e s i g n F i 1 eName As
S t r i n g , NewDesignFileName As S t r i n g ,
COverwri t e As Boo1 e a n l )
Function
CreateApplicationElement(Application1D As
Long, A p p l i c a t i o n D a t a As D a t a B l o c k ) As
A p p l ic a t i o n E l ement
F u n c t i o n C r e a t e A r c E l e m e n t l ( T e m p 1 a t e As
E l e m e n t , S t a r t P o i n t As P o i n t 3 d , C e n t e r p o i n t As
P o i n t 3 d , E n d p o i n t As P o i n t 3 d ) As A r c E l e m e n t
F u n c t i o n C r e a t e A r c E l ement2(Templ a t e As
E l e m e n t , C e n t e r P o i n t As P o i n t 3 d , P r i m a r y R a d i u s
As Doubl e , S e c o n d a r y R a d i us As Doubl e , R o t a t i on
196 I Chapter 11 :The Microstation Object Model - Objects I
As Matri x3d, StartAngl e As Doubl e, SweepAngl e
As Double) As ArcElement
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
Doubl e , P1 anePoi nt As Poi nt3d) As ArcEl ement
Function CreateAreaPattern(R0wSpacing As
Double, ColSpacing As Double, Angle As Double,
CellName As String, Scale As Double) As
AreaPattern
Function CreateBsplineCurveElementl(Temp1ate
As Element, Curve As BsplineCurve) As
Bspl i neCurveEl ement
Function CreateBspl i neCurveEl ementZ(Temp1 ate
As El ement, Curve As Interpol ati oncurve) As
Bspl i neCurveEl ement
Function CreateBsplineSurfaceElementl(Temp1ate
As Element, Surface As BsplineSurface) As
Bspl ineSurfaceE1 ement
Function CreateCellElementl(Name As String,
El ements ( ) As -El ement , Ori gi n As Poi nt3d,
[ IsPointCell As Bool eanl) As Cell Element
Function Createcell El ementZ(Cel1 Name As
String, Origin As Point3d, Scale As Point3d,
Truescale As Boolean, Rotation As Matrix3d) As
Cell El ement
Function CreateCe 1 El ement3(Cell Name As
String, Origin As Poi nt3d, TrueScal e As
Bool ean) As Cell E ement
Function
CreateCompl exShapeEl ementl (Chai nabl eEl ements( )
As Chai nabl eEl ement, [Fi 1 1 Mode As MsdFi 1 1 Mode =
msdFi 1 1 ModeUseActi vel) As Compl exShapeEl ement
tN Function
CreateCompl exShapeEl ementZ(Chai nabl eEl ements( )
As Chai nabl eEl ement, [Fi 1 1 Mode As MsdFi 1 1 Mode =
I The Microstation Object Model I 197
msdFi 1 l M o d e U s e A c t i v e l , [GapTol e r a n c e As D o u b l e
= -11) As Compl exShapeEl ement
B Function
CreateCompl e x S t r i n g E l e m e n t l ( C h a i n a b l e E l ements (
) As Chai n a b l e E l e m e n t ) A s Compl e x S t r i n g E l ement
Function
CreateCompl e x S t r i n g E l e m e n t Z ( C h a i n a b 1 e E l e m e n t s (
) As Chai n a b l e E l e m e n t , [GapTol e r a n c e As Doubl e
= -11) As C o m p l e x S t r i n g E l e m e n t
B F u n c t i o n CreateConeEl ementl(Temp1a t e As
E l e m e n t , BaseRadi us As Doubl e , B a s e C e n t e r P o i n t
As P o i n t 3 d , TopRadi us As Doubl e , T o p C e n t e r P o i n t
As P o i n t 3 d , R o t a t i o n A s M a t r i x 3 d ) A s
ConeEl ement
F u n c t i o n C r e a t e C o n e E l ementZ(Temp1 a t e As
E l ement , Radi us As Doubl e , B a s e C e n t e r P o i n t As
P o i n t 3 d , T o p C e n t e r P o i n t As P o i n t 3 d ) As
ConeEl ement
B F u n c t i o n CreateCrossHatchPattern(Space1 A s
D o u b l e , Space2 As D o u b l e , A n g l e 1 As D o u b l e ,
A n g l e2 As D o u b l e ) A s C r o s s H a t c h P a t t e r n
F u n c t i o n C r e a t e C u r v e E l e m e n t l ( T e m p 1 a t e As
E l e m e n t , P o i n t s 0 As P o i n t 3 d ) As C u r v e E l e m e n t
B F u n c t i o n C r e a t e D a t a b a s e L i n k ( M s 1 i n k A s Long,
E n t i t y A s Long, L i n k T y p e As M s d D a t a b a s e L i n k a g e ,
I s I n f o r m a t i o n As Boolean,
D i s p l a y a b l e A t t r i buteType As Long) As
DatabaseLink
F u n c t i o n CreateDesignFile(SeedFi1eName As
S t r i n g , NewDesignFileName As S t r i n g , Open As
Boo1 e a n ) As D e s i g n F i 1 e
B F u n c t i o n CreateDimensionEl ementl(Temp1a t e As
Element, R o t a t i o n As M a t r i x 3 d , Type As
MsdDimType, C T e x t O r i e n t a t i o n V i e w A s V i e w ] ) A s
D i m e n s i o n E l ement
F u n c t i o n CreateEllipseElementl~Template As
E l e m e n t , P e r i m e t e r P o i n t l As P o i n t 3 d ,
P e r i m e t e r p o i n t 2 As P o i n t 3 d , P e r i m e t e r P o i n t 3 As
P o i n t 3 d , C F i 11 Mode As MsdFi 11 Mode =
m s d F i l l M o d e U s e A c t i v e I ) As E l l i p s e E l e m e n t
B F u n c t i o n CreateEllipseElement2(Template A s
Element, O r i g i n As Point3d, PrimaryRadius As
198 I Chapter 11 :The Microstation Object Model - Objects I
Doubl e , S e c o n d a r y R a d i us As Doubl e , R o t a t i on As
M a t r i x 3 d , [ F i l l M o d e As M s d F i l l M o d e =
msdFi 11 ModeUseActi v e l ) As E l 1 ip s e E l ement
F u n c t i o n C r e a t e E l 1 ip t i c a l E l e m e n t l (Templ a t e As
E l e m e n t , E l 1 ip s e As E l 1 ip s e 3 d , [ F i 11 Mode As
M s d F i l l M o d e = m s d F i l l M o d e U s e A c t i v e 1 ) As
Element
F u n c t i o n CreateHatchPatternl(Space As D o u b l e ,
A n g l e As D o u b l e ) As H a t c h P a t t e r n
F u n c t i o n CreateLineElementl(Temp1ate As
E l e m e n t , V e r t i c e s ( ) As P o i n t 3 d ) As L i n e E l ement
F u n c t i o n CreateLineElementZ(Temp1ate As
E l e m e n t , S t a r t P o i n t As P o i n t 3 d , EndPoi n t As
P o i n t 3 d As L i n e E l ement
F u n c t i o n CreateObjectInMicroStation(Prog1D As
S t r i n g ) A s Unknown
F u n c t i o n C r e a t e P o i n t S t r i n g E l e m e n t l (Templ a t e As
E l e m e n t , V e r t i c e s 0 As P o i n t 3 d , D i s j o i n t As
B o o l e a n ) As P o i n t S t r i n g E l ement
F u n c t i o n C r e a t e S a v e d V i ewEl ement ( V i ewSpeci f i e r
A s V a r i a n t , Name As S t r i n g , [ D e s c r i p t i o n As
S t r i n g 1 ) A s SavedVi ewEl ement
F u n c t i o n CreateShapeElementl(Temp1ate As
E l e m e n t , V e r t i c e s ( ) As P o i n t 3 d , [ F i 11 Mode As
M s d F i l l M o d e = m s d F i l l M o d e U s e A c t i v e 1 ) As
ShapeEl ement
F u n c t i o n CreateSharedCellElementl(Name As
S t r i n g , E l e m e n t s 0 As - E l e m e n t , O r i g i n As
P o i n t 3 d , [ I s P o i n t C e l l As B o o l e a n ] ) As
SharedCell Element
F u n c t i o n CreateSharedCellElementZ~CellNameAs
S t r i n g , O r i g i n As P o i n t 3 d , S c a l e As P o i n t 3 d ,
T r u e s c a l e As B o o l e a n , R o t a t i o n As M a t r i x 3 d ) As
SharedCell Element
F u n c t i o n CreateSharedCellElement3(CellName As
S t r i n g , O r i g i n As P o i n t 3 d , T r u e S c a l e As
Bool ean) As SharedCell Element
F u n c t i o n CreateTextElementl(Temp1ate As
E l e m e n t , T e x t As S t r i n g , O r i g i n As P o i n t 3 d ,
R o t a t i o n As M a t r i x 3 d ) As T e x t E l e m e n t
I The Microstation Object Model I 199
il F u n c t i o n CreateTextNodeElementl(Temp1ate As
E l e m e n t , O r i g i n As P o i n t 3 d , R o t a t i o n As
M a t r i x 3 d ) As T e x t N o d e E l ement
il F u n c t i o n CreateTextNodeElementZ(Temp1ate As
E l e m e n t , O r i g i n As P o i n t 3 d , R o t a t i o n As
M a t r i x 3 d , [ I n c r e m e n t N o d e N u m b e r As B o o l e a n =
T r u e ] , [ R e s e r v e d As Unknown]) As
T e x t N o d e E l ement
il P r o p e r t y C u r r e n t G r a p h i c G r o u p As Long { r e a d
only}
il P r o p e r t y C u r s o r I n f o r m a t i o n As
CursorInformation {read-only}
il Function
D a t a E n t r y R e g i o n F r o m C r i t e r i a ( S t a r t P o s i t i on As
Long, L e n g t h As Long, J u s t i f i c a t i o n As
MsdData E n t r y Regi on J u s t if ic a t ion As
D a t a E n t r y Regi on
il F u n c t i o n D e g r e e s ( R a d i a n s As D o u b l e ) As D o u b l e
il Sub D e l e t e X D a t u m ( X D a t a 0 As XDatum, I n d e x As
Long)
il Sub D e t a c h c e l l L i b r a r y ( )
il F u n c t i o n DLongAbs(Va1ue As DLong) As DLong
il F u n c t i o n DLongAdd(Term1 As DLong, Term2 As
DLong) As DLong
il F u n c t i o n DLongComp(Value1 As DLong, V a l u e 2 As
DLong) As Long
il F u n c t i o n D L o n g D i v i d e ( N u m e r a t 0 r As DLong,
D e n o m i n a t o r As DLong) As DLong
il F u n c t i o n DLongFromDoubl e ( V a 1 ue As D o u b l e ) As
DLong
il F u n c t i o n DLongFromHexString(Va1ue As S t r i n g )
As DLong
il F u n c t i o n D L o n g F r o m I n t 6 4 ( V a l ue As E m p t y ) As
DLong
il F u n c t i o n DLongFromLong(Va1ue As L o n g ) As DLong
il F u n c t i o n D L o n g F r o m S t r i n g ( V a 1 u e As S t r i n g ) As
DLong
il F u n c t i o n DLongMod(Numerator As DLong,
D e n o m i n a t o r As DLong) As DLong
200 I Chapter 11 :The Microstation Object Model - Objects I
l l Function DLongMultiply(Factor1 As DLong,
Factor2 As DLong) As DLong
Function DLongNegate(Va1ue As DLong) As DLong
Function DLongSubtract(Minuend As DLong,
Subtrahend As DLong) As DLong
Function DLongToHexString(Va1ue As DLong) As
String
S u b DLongToInt64(Value As DLong)
Function DLongToLong(Va1ue As DLong) As Long
Function DLongToString(Va1ue As DLong) As
String
Function
El 1 ipse3dFromEll i pti cal El ement (El ement As
El 1 i pti cal El ement) As El 1 i pse3d
Property ExecutingVBProject As Object {read-
only1
Property Full Name As String {read-only}
Function
GetCellInformationEnumerator(IncludeSharedCel1
s As Boolean, IncludeFullPath As Boolean) As
Cell InformationEnumerator
Function GetCExpressionValue(CExpression As
String, [MdlApplicationName As String]) As
Variant
Function
GetCExpressionValueAsDLong(CExpression As
String, [MdlApplicationName As String]) As
DLong
Function GetFloodBoundary(CandidateElements0
A s -Element, Template As Element, SeedPoint A s
Point3d, [Viewspecifier As Variant],
[FindHoles As Boolean = True], [Tolerance As
Double = - 1 1 , [FillMode As MsdFillMode =
msdFi 1 1 ModeUseActi vel) As El ement
Function GetRegionDifference(RegionSolid0 As
-El ement, Regi onHol es( ) As -El ement, Templ ate
I4 F u n c t i o n Matrix3dIsOrthogonal(Matrix As
M a t r i x 3 d ) As B o o l e a n
I4 F u n c t i on M a t r i x3d Is R i g i d ( M a t r i x As M a t r i x 3 d )
As B o o l e a n
I4 Fun c t ion M a t r ix3d Is Rot a t e S c a 1 e Ro t a t e ( M a t r ix As
M a t r i x 3 d , R o t a t i o n 1 As M a t r i x 3 d , S c a l e F a c t o r s
As P o i n t 3 d , R o t a t i o n 2 As M a t r i x 3 d ) As B o o l e a n
I4 F u n c t i o n Matrix3dIsSignedPermutation(Matrix As
M a t r i x 3 d ) As B o o l e a n
I4 F u n c t i o n
M a t r i x 3 d I s X R o t a t i onY R o t a t i o n Z R o t a t i onScal e ( M a t
r i x As M a t r i x 3 d , RadiansX As D o u b l e , RadiansY
As D o u b l e , RadiansZ As D o u b l e , S c a l e As D o u b l e )
As B o o l e a n
I4 F u n c t i on M a t r i x 3 d I s X Y R o t a t i o n ( M a t r i x As
M a t r i x 3 d , X Y R o t a t i o n R a d i a n s As D o u b l e ) As
Boo1 ean
I4 F u n c t i o n
M a t r i x3dIsXY R o t a t i onS kewAndScal e ( M a t r i x As
M a t r i x 3 d , X A x i s A n g l e As D o u b l e , YAxisSkewAngle
As D o u b l e , X s c a l e As D o u b l e , Y s c a l e As D o u b l e ,
Z s c a l e As D o u b l e ) As B o o l e a n
I4 F u n c t i o n M a t r i x 3 d M a x A b s ( M a t r i x As M a t r i x 3 d ) As
Double
I4 F u n c t i o n M a t r i x 3 d M a x D i f f ( M a t r i x l As M a t r i x 3 d ,
M a t r i x 2 As M a t r i x 3 d ) As D o u b l e
I4 F u n c t i o n M a t r i x 3 d R o t a t i o n F r o m C o l umnZ(Norma1 As
P o i n t 3 d ) As M a t r i x 3 d
I4 F u n c t i o n
M a t r i x 3 d R o t a t i onFromPoi n t 3 d O r i g i nXY (Orig i n As
P o i n t 3 d , X P o i n t As P o i n t 3 d , Y P o i n t As P o i n t 3 d )
As M a t r i x 3 d
I4 F u n c t i o n Matrix3dRotationFromRowZ(Normal As
P o i n t 3 d ) As M a t r i x 3 d
I4 Sub Matrix3dSetComponentByRowAndColumn(Matrix
As M a t r i x 3 d , RowIndex As Long, ColumnIndex As
Long, V a l u e As D o u b l e )
I4 F u n c t i o n Matrix3dSumSquares(Matrix As
M a t r i x 3 d ) As D o u b l e
I4 F u n c t i o n Matrix3dTranspose(Matrix As M a t r i x 3 d )
As M a t r i x 3 d
204 I Chapter 11 :The Microstation Object Model - Objects I
F u n c t i o n M a t r i x 3 d Z e r o O As M a t r i x 3 d
Function
Mdl C r e a t e E l ementFromE1 e m e n t D e s c r P ( E l e m e n t D e s c r
P A s L o n g ) As E l e m e n t
Function
MdlGetDesignFileFromModel RefP(Mode1 RefP As
L o n g ) As D e s i g n F i l e
Function
MdlGetModelReferenceFromModelRefP(Mode1RefP As
Long) As ModelReference
P r o p e r t y M e s s a g e c e n t e r As M e s s a g e c e n t e r
{read-only}
P r o p e r t y Name As S t r i n g { r e a d - o n l y }
Sub OnDesignFileClosed(DesignFi1eName As
String)
Sub OnDesignFileOpened(DesignFi1eName As
String)
F u n c t i o n OpenDesignFile(DesignFi1eName As
S t r i n g , [ R e a d o n l y As B o o l e a n ] , C V 7 A c t i o n As
M s d V 7 A c t i o n = m s d V 7 A c t i o n A s k U s e r l ) As
D e s i gn F i 1 e
Function
OpenDesignFileForProgram(DesignFi1eName As
S t r i n g , [ R e a d o n l y As B o o l e a n ] ) As D e s i g n F i l e
P r o p e r t y P a t h As S t r i n g { r e a d - o n l y }
F u n c t i o n P i ( ) As D o u b l e
Function
P1 a n e 3 d I n t e r s e c t s P l a n e 3 d ( I n t e r s e c t i onRay As
Ray3d, P1 aneO As P1 ane3d, P1 a n e l As P1 a n e 3 d ) As
Boo1 ean
Function
Plane3dIntersectsRay3d(IntersectionPo n t As
P o i n t 3 d , P a r a m e t e r A s Doubl e , P1 ane A s P1 ane3d,
Ray A s Ray3d) As B o o l e a n
F u n c t i o n P o i n t Z d A d d ( P o i n t 1 As P o i n t 2 d P o i n t 2
As P o i n t 2 d ) As P o i n t 2 d
F u n c t i o n PointZdAddZScaled(0rigin As P o i n t Z d ,
V e c t o r l A s P o i n t Z d , S c a l e l As D o u b l e , V e c t o r 2
A s P o i n t 2 d , S c a l e 2 As D o u b l e ) As P o i n t 2 d
F u n c t i o n PointZdAdd3Scaled(Origin As P o i n t Z d ,
V e c t o r l As P o i n t Z d , S c a l e l As D o u b l e , V e c t o r 2
I The Microstation Object Model I 205
As P o i n t Z d , S c a l e 2 As D o u b l e , V e c t o r 3 As
P o i n t Z d , S c a l e 3 As D o u b l e ) As P o i n t 2 d
F u n c t i o n P o i n t 2 d A d d S c a l e d ( O r i g i n As P o i n t 2 d ,
V e c t o r As P o i n t 2 d , S c a l e As D o u b l e ) As P o i n t 2 d
F u n c t i o n P o i n t 2 d A r e V e c t o r s P a r a l l e l ( V e c t o r l As
P o i n t Z d , V e c t o r 2 As P o i n t 2 d ) As B o o l e a n
Function
PointZdAreVectorsPerpendicular(Vector1 As
P o i n t Z d , V e c t o r 2 As P o i n t 2 d ) As B o o l e a n
F u n c t i o n P o i n t Z d C r o s s P r o d u c t ( V e c t o r 1 As
P o i n t Z d , V e c t o r 2 As P o i n t 2 d ) As D o u b l e
F u n c t i o n P o i n t 2 d C r o s s P r o d u c t 3 P o i n t s ( O r i g i n As
P o i n t Z d , T a r g e t l As P o i n t 2 d , T a r g e t 2 As
P o i n t 2 d ) As D o u b l e
F u n c t i o n P o i n t Z d D i s t a n c e ( P o i n t 0 As P o i n t Z d ,
P o i n t l As P o i n t 2 d ) As D o u b l e
F u n c t i o n P o i n t 2 d D i s t a n c e S q u a r e d ( P o i n t l As
P o i n t Z d , P o i n t 2 As P o i n t 2 d ) As D o u b l e
F u n c t i o n P o i n t Z d D o t D i f f e r e n c e ( T a r g e t P 0 i n t As
P o i n t Z d , O r i g i n As P o i n t Z d , V e c t o r As P o i n t 2 d )
As D o u b l e
F u n c t i o n P o i n t Z d D o t P r o d u c t ( V e c t o r 1 As P o i n t 2 d ,
V e c t o r 2 As P o i n t 2 d ) As D o u b l e
F u n c t i o n P o i n t 2 d D o t P r o d u c t 3 P o i n t s ( O r i g i n As
P o i n t Z d , T a r g e t l As P o i n t Z d , T a r g e t 2 As
P o i n t 2 d ) As D o u b l e
F u n c t i o n P o i n t 2 d E q u a l ( V e c t o r l As P o i n t 2 d ,
V e c t o r 2 As P o i n t 2 d ) As B o o l e a n
F u n c t i o n P o i n t 2 d E q u a l To1 e r a n c e ( V e c t o r 1 As
P o i n t Z d , V e c t o r 2 As P o i n t Z d , T o l e r a n c e As
D o u b l e ) As B o o l e a n
F u n c t i o n P o i n t 2 d F r o m X Y ( X As D o u b l e , Y As
D o u b l e ) As P o i n t 2 d
F u n c t i o n PointZdGetComponent(Point As P o i n t Z d ,
I n d e x As L o n g ) As D o u b l e
F u n c t i o n PointZdInterpolate(Point0 As P o i n t 2 d ,
S As D o u b l e , P o i n t l As P o i n t 2 d ) As P o i n t 2 d
F u n c t i o n P o i n t Z d M a g n i t u d e ( V e c t o r As P o i n t 2 d )
As D o u b l e
206 I Chapter 11 :The Microstation Object Model - Objects I
F u n c t i o n PointZdMagnitudeSquared(Vector As
P o i n t 2 d ) As D o u b l e
F u n c t i o n P o i n t Z d M a x A b s ( V e c t o r As P o i n t 2 d ) As
Doubl e
F u n c t i o n P o i n t Z d N e g a t e ( V e c t 0 r As P o i n t 2 d ) As
Poi n t 2 d
F u n c t i o n P o i n t Z d N o r m a l i z e ( V e c t o r As P o i n t 2 d )
As P o i n t 2 d
F u n c t i o n P o i n t Z d O n e O As P o i n t 2 d
Function
Point2dSignedAngleBetweenVectors(Vectorl As
P o i n t 2 d , V e c t o r 2 As P o i n t 2 d ) As D o u b l e
F u n c t i o n P o i n t Z d S u b t r a c t ( P o i n t 1 As P o i n t Z d ,
P o i n t 2 As P o i n t 2 d ) As P o i n t 2 d
F u n c t i o n P o i n t Z d Z e r o O As P o i n t 2 d
F u n c t i o n P o i n t 3 d A d d ( P o i n t l As P o i n t 3 d , P o i n t 2
As P o i n t 3 d ) As P o i n t 3 d
F u n c t i o n Point3dAddZScaled(Origin As P o i n t 3 d ,
V e c t o r l As P o i n t 3 d , S c a l e l As D o u b l e , V e c t o r 2
As P o i n t 3 d , S c a l e 2 As D o u b l e ) As P o i n t 3 d
F u n c t i o n Point3dAddZScaledVector3d(Origin As
P o i n t 3 d , V e c t o r l As V e c t o r 3 d , S c a l e l As D o u b l e ,
V e c t o r 2 As V e c t o r 3 d , S c a l e 2 As D o u b l e ) As
Poi n t 3 d
F u n c t i o n Point3dAdd3Scaled(Origin As P o i n t 3 d ,
V e c t o r l As P o i n t 3 d , S c a l e l As D o u b l e , V e c t o r 2
As P o i n t 3 d , S c a l e 2 As D o u b l e , V e c t o r 3 As
P o i n t 3 d , S c a l e 3 As D o u b l e ) As P o i n t 3 d
F u n c t i o n Point3dAdd3ScaledVector3d(Origin As
P o i n t 3 d , V e c t o r l As V e c t o r 3 d , S c a l e l As D o u b l e ,
V e c t o r 2 As V e c t o r 3 d , S c a l e 2 As D o u b l e , V e c t o r 3
As V e c t o r 3 d , S c a l e 3 As D o u b l e ) As P o i n t 3 d
F u n c t i o n Point3dAddAngleDistance(Pointl As
P o i n t 3 d , A n g l e R a d i a n s As D o u b l e , D i s t a n c e X Y As
D o u b l e , Dz As D o u b l e ) As P o i n t 3 d
F u n c t i o n Point3dAddPoint3dVector3d(Base As
P o i n t 3 d , V e c t o r As V e c t o r 3 d ) As P o i n t 3 d
F u n c t i o n P o i n t 3 d A d d S c a l e d ( O r i g i n As P o i n t 3 d ,
V e c t o r As P o i n t 3 d , S c a l e As D o u b l e ) As P o i n t 3 d
I The Microstation Object Model I 207
F u n c t i o n P o i n t 3 d A d d S c a l e d V e c t o r 3 d ( O r i g i n As
P o i n t 3 d , V e c t o r As V e c t o r 3 d , S c a l e As D o u b l e )
As P o i n t 3 d
F u n c t i o n Point3dAngleBetweenVectors(Vectorl As
P o i n t 3 d , V e c t o r 2 As P o i n t 3 d ) As D o u b l e
Function Point3dAngleBetweenVectorsXY(Vectorl
As P o i n t 3 d , V e c t o r 2 As P o i n t 3 d ) As D o u b l e
F u n c t i o n P o i n t 3 d A r e V e c t o r s P a r a l l e l ( V e c t o r 1 As
P o i n t 3 d , V e c t o r 2 As P o i n t 3 d ) As B o o l e a n
Function
Point3dAreVectorsPerpendicular(Vectorl As
P o i n t 3 d , V e c t o r 2 As P o i n t 3 d ) As B o o l e a n
F u n c t i o n Point3dCrossProduct(Vectorl As
P o i n t 3 d , V e c t o r 2 As P o i n t 3 d ) As P o i n t 3 d
F u n c t i o n P o i n t 3 d C r o s s P r o d u c t 3 P o i n t s ( O r i g i n As
P o i n t 3 d , T a r g e t l As P o i n t 3 d , T a r g e t 2 As
P o i n t 3 d ) As P o i n t 3 d
Function Point3dCrossProduct3PointsXY(Origin
As P o i n t 3 d , T a r g e t l As P o i n t 3 d , T a r g e t 2 As
P o i n t 3 d ) As D o u b l e
F u n c t i o n P o i n t 3 d C r o s s P r o d u c t X Y ( V e c t o r l As
P o i n t 3 d , V e c t o r 2 As P o i n t 3 d ) As D o u b l e
F u n c t i o n P o i n t 3 d D i s t a n c e ( P o i n t l As P o i n t 3 d ,
P o i n t 2 As P o i n t 3 d ) As D o u b l e
F u n c t i o n P o i n t 3 d D i s t a n c e S q u a r e d ( P o i n t l As
P o i n t 3 d , P o i n t 2 As P o i n t 3 d ) As D o u b l e
F u n c t i o n P o i n t 3 d D i s t a n c e S q u a r e d X Y ( P o i n t l As
P o i n t 3 d , P o i n t 2 As P o i n t 3 d ) As D o u b l e
F u n c t i o n P o i n t 3 d D i s t a n c e X Y ( P o i n t l As P o i n t 3 d ,
P o i n t 2 As P o i n t 3 d ) As D o u b l e
F u n c t i o n P o i n t 3 d D o t D i f f e r e n c e ( T a r g e t As
P o i n t 3 d , O r i g i n As P o i n t 3 d , V e c t o r As P o i n t 3 d )
As D o u b l e
Function Point3dDotDifferenceVector3d(Target
As P o i n t 3 d , O r i g i n As P o i n t 3 d , V e c t o r As
V e c t o r 3 d ) As D o u b l e
F u n c t i o n Point3dDotProduct(Vectorl As P o i n t 3 d ,
V e c t o r 2 As P o i n t 3 d ) As D o u b l e
F u n c t i o n P o i n t 3 d D o t P r o d u c t 3 P o i n t s ( O r i g i n As
P o i n t 3 d , T a r g e t l As P o i n t 3 d , T a r g e t 2 As
P o i n t 3 d ) As D o u b l e
208 I Chapter 11 :The Microstation Object Model - Objects I
F u n c t i o n Point3dDotProduct3PointsXY(Origin As
P o i n t 3 d , T a r g e t 1 As P o i n t 3 d , T a r g e t 2 As
P o i n t 3 d ) As D o u b l e
F u n c t i o n Point3dDotProductXY(Vectorl As
P o i n t 3 d , V e c t o r 2 As P o i n t 3 d ) As D o u b l e
F u n c t i o n P o i n t 3 d E q u a l ( V e c t o r 1 As P o i n t 3 d ,
V e c t o r 2 As P o i n t 3 d ) As B o o l e a n
F u n c t i o n P o i n t 3 d E q u a l T o l e r a n c e ( V e c t o r 1 As
P o i n t 3 d , V e c t o r 2 As P o i n t 3 d , T o l e r a n c e As
Double) As Boolean
F u n c t i o n P o i n t 3 d F r o m A n g l e D i s t a n c e ( A n g 1 eRadi ans
As D o u b l e , D i s t a n c e X Y As D o u b l e , Z As D o u b l e )
As P o i n t 3 d
F u n c t i o n P o i n t 3 d F r o m M a t r i x 3 d C o l u m n ( M a t r i x As
M a t r i x 3 d , Col As Long) As P o i n t 3 d
Function
Point3dFromMatrix3dInverseTimesPoint3d(Matrix
As M a t r i x 3 d , P o i n t As P o i n t 3 d ) As P o i n t 3 d
Function
Point3dFromMatrix3dInverseTransposeTimesPoint3
d ( M a t r i x A s M a t r i x 3 d , P o i n t As P o i n t 3 d ) As
Poi n t 3 d
F u n c t i o n Point3dFromMatrix3dRow(Matrix As
M a t r i x 3 d , Row As Long) As P o i n t 3 d
Function
P o i n t 3 d FromMat r i x 3 d T i mes P o i n t 3 d ( M a t r i x As
M a t r i x 3 d , P o i n t As P o i n t 3 d ) As P o i n t 3 d
F u n c t i o n Point3dFromMatrix3dTimesXYZ(Matrix As
M a t r i x 3 d , X As D o u b l e , Y As D o u b l e , Z As
D o u b l e ) As P o i n t 3 d
Function
Point3dFromMatrix3dTransposeTimesPoint3d(Matri
x A s M a t r i x 3 d , P o i n t As P o i n t 3 d ) As P o i n t 3 d
Function
Point3dFromMatrix3dTransposeTimesXYZ(Matrix As
M a t r i x 3 d , X As D o u b l e , Y As D o u b l e , Z As
D o u b l e ) As P o i n t 3 d
F u n c t i o n Point3dFromRay3dFractionParameter(Ray
A s Ray3d, F r a c t i o n As D o u b l e ) As P o i n t 3 d
F u n c t i o n Point3dFromRay3dTangent(Ray As Ray3d)
As P o i n t 3 d
I The Microstation Object Model I 209
Function
Point3dFromSegment3dFractionParameter(Segment
As Segment3d, Fraction 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
Point3dFromTransform3dTimesXYZ(Transform As
Transform3d, X As Double, Y As Double, Z As
Double) 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, A z As Double) As Point3d
Function Point3dGetComponent(Point As Point3d,
Index As Long) As Double
Function P o i n t 3 d I n P o l y g o n X Y ( P o i n t As Point3d,
P o l y g o n V e r t i c e s O As Point3d, [Tolerance As
Double = -11) As Long
Function Point3dInterpolate(PointO As Point3d,
Fracti onparameter As Double, Poi ntl As Point3d)
As Point3d
Function Point3dIsPointInCCWSector(TestPoint
As Point3d, Origin As Point3d, Target0 As
Point3d, Targetl As Point3d, UpVector As
Point3d) As Boolean
Function
Point3dIsPointInSmallerSector(TestPoint As
Point3d, Origin As Point3d, Targetl As Point3d,
Target2 As Point3d) As Boolean
Function Point3dIsVectorInCCWSector(TestVector
As Point3d, Vector0 As Point3d, Vector1 As
Point3d, UpVector As Point3d) As Boolean
Function
Point3dIsVectorInSmallerSector(TestVector As
210 I Chapter 11 :The Microstation Object Model - Objects I
Point3d, Vector0 As Point3d, Vector1 As
Point3d) As Boolean
Function Point3dMagnitude(Vector As Point3d)
A s Double
Function Po nt3dMagnitudeSquared(Vector As
Point3d) As Double
Function Po nt3dMaxAbs(Vector As Point3d) As
Doubl e
Function Po nt3dNegate(Vector As Point3d) As
Poi nt3d
unction Point3dNormalize(Vector As Point3d) A s
Poi nt3d
Function P o i n t 3 d O n e O As Point3d
Function
Point3dPlanarAngleBetweenVectors(Vectorl As
Point3d, Vector2 As Point3d, PlaneNorma1 As
Point3d) A s Double
Function Point3dPolarAngle(Vector As Point3d)
As Double
Function Point3dProjectToPlane3d(Point As
Poi nt3d, P1 ane As P1 ane3d, CVi ewSpeci f i er As
Variant], [UseAuxiliaryCoordinateSystem As
Boolean = False]) As Point3d
Function Point3dProjectToRay3d(Parameter As
Double, Point As Point3d, Ray As Ray3d,
[Viewspecifier As Variant],
[UseAuxiliaryCoordinateSystem As Boolean =
False]) As Point3d
Function Point3dRotateXY(Vector As Point3d,
Theta A s Double) As Point3d
Function Point3dScale(Vector As Point3d, Scale
As Double) As Point3d
S u b Point3dSetComponent(Point As Point3d,
Index A s Long, Value As Double)
Function
Point3dSignedAngleBetweenVectors(Vectorl As
Point3d, Vector2 As Point3d, Orientationvector
As Point3d) As Double
Function
Point3dSmallerAngleBetweenUnorientedVectors~Ve
ctorl A s Point3d, Vector2 A s Point3d) As Double
I The Microstation Object Model I 21 1
Function
Point3dSmallerAngleBetweenUnorientedVectorsXY~
V e c t o r 1 As P o i n t 3 d , V e c t o r 2 As P o i n t 3 d ) As
Double
F u n c t i o n P o i n t 3 d S u b t r a c t ( P o i n t l As P o i n t 3 d ,
P o i n t 2 As P o i n t 3 d ) As P o i n t 3 d
Function Point3dSubtractPoint3dVector3d(Base
As P o i n t 3 d , V e c t o r As V e c t o r 3 d ) As P o i n t 3 d
F u n c t i o n P o i n t 3 d r i p l e P r o d u c t ( V e c t o r 1 As
P o i n t 3 d , V e c t o r 2 As P o i n t 3 d , V e c t o r 3 As
P o i n t 3 d ) As Doub e
F u n c t i on P o i n t 3 d r i p l e P r o d u c t 4 P o i n t s (Orig i n As
P o i n t 3 d , T a r g e t 1 As P o i n t 3 d , T a r g e t 2 As
P o i n t 3 d , T a r g e t 3 As P o i n t 3 d ) As D o u b l e
F u n c t i o n P o i n t 3 d Z e r o ( ) As P o i n t 3 d
F u n c t i o n P o i n t s T o P i x e l s X ( P o i n t C o o r d i n a t e As
D o u b l e ) As Long
F u n c t i o n P o i n t s T o P i x e l s Y ( P o i n t C o o r d i n a t e As
D o u b l e ) As Long
P r o p e r t y P r o c e s s I D As Long {read-only}
Sub Q u i t 0
F u n c t i o n R a d i a n s ( D e g r e e s As D o u b l e ) As D o u b l e
F u n c t i o n R a n g e 3 d C o n t a i n s P o i n t 3 d ( R a n g e As
Range3d, P o i n t As P o i n t 3 d ) As B o o l e a n
F u n c t i o n Range3dContainsXYZ(Range As Range3d,
X As D o u b l e , Y As D o u b l e , Z As D o u b l e ) As
Boo1 ean
F u n c t i o n Range3dEqual ( R a n g e l As Range3d,
Range2 As Range3d) As B o o l e a n
F u n c t i o n Range3dEqual To1 e r a n c e ( Range0 As
Range3d, R a n g e l As Range3d, T o l e r a n c e As
D o u b l e ) As B o o l e a n
F u n c t i o n Range3dExtentSquared(Range As
Range3d) As D o u b l e
F u n c t i o n R a n g e 3 d F r o m P o i n t 3 d ( P o i n t As P o i n t 3 d )
As Range3d
F u n c t i o n R a n g e 3 d F r o m P o i n t 3 d P o i n t 3 d ( P o i n t O As
P o i n t 3 d , P o i n t 1 As P o i n t 3 d ) As Range3d
Function
R a n g e 3 d F r o m P o i n t 3 d P o i n t 3 d P o i n t 3 d ( P o i n t O As
212 I Chapter 11 :The Microstation Object Model - Objects I
P o i n t 3 d , P o i n t 1 As P o i n t 3 d , P o i n t 2 As P o i n t 3 d )
As Range3d
F u n c t i o n Range3dFromRange3dMargi n (Range As
Range3d, M a r g i n As D o u b l e ) As Range3d
F u n c t i o n Range3dFromXYZ(X As D o u b l e , Y As
D o u b l e , Z As D o u b l e ) As Range3d
F u n c t i o n Range3dFromXYZXYZ(Xl As D o u b l e , Y 1 As
D o u b l e , Z 1 As D o u b l e , X2 As D o u b l e , Y2 As
D o u b l e , 22 As D o u b l e ) As Range3d
F u n c t i o n R a n g e 3 d I n i t O As Range3d
F u n c t i o n R a n g e 3 d I n t e r s e c t ( R a n g e l As Range3d,
Range2 A s Range3d) As Range3d
F u n c t i o n R a n g e 3 d I n t e r s e c t 2 ( Resul t R a n g e As
Range3d, R a n g e l As Range3d, Range2 As Range3d)
As B o o l e a n
Function
Range3dIsContainedInRange3d(InnerRange As
Range3d, O u t e r R a n g e As Range3d) As B o o l e a n
F u n c t i o n R a n g e 3 d I s N u l l (Range As Range3d) As
Boo1 ean
F u n c t i o n Range3dScaleAboutCenter(RangeIn As
Range3d, S c a l e As D o u b l e ) As Range3d
F u n c t i o n Range3dUni on (Range0 As Range3d,
R a n g e l As Range3d) As Range3d
F u n c t i o n Range3dUnionPoint3d(Range As Range3d,
P o i n t A s P o i n t 3 d ) As Range3d
F u n c t i o n Range3dUni onXY Z ( Range As Range3d, X As
D o u b l e , Y As D o u b l e , Z As D o u b l e ) As Range3d
P r o p e r t y R a s t e r M a n a g e r As R a s t e r M a n a g e r
{read-only}
Sub Ray3dC1 o s e s t P o i n t (Ray As Ray3d, S p a c e P o i n t
As P o i n t 3 d , C1 o s e P o i n t As P o i n t 3 d ,
C1 o s e F r a c t i o n As D o u b l e )
Sub Ray3dC1 o s e s t P o i n t B o u n d e d ( Ray As Ray3d,
S p a c e P o i n t As P o i n t 3 d , C1 o s e P o i n t As P o i n t 3 d ,
C1 o s e F r a c t i o n As D o u b l e )
Sub Ray3dC1 o s e s t P o i ntBoundedXY (Ray As Ray3d,
S p a c e P o i n t As P o i n t 3 d , C1 o s e P o i n t As P o i n t 3 d ,
C1 o s e F r a c t i o n As D o u b l e )
I The Microstation Object Model I 213
Sub R e g i s t e r V 8 T o V 7 F i l t e r ( H a n d 1 e r As
IConvertV8ToV7)
Sub R e m o v e A t t a c h m e n t E v e n t s H a n d l e r ( E v e n t H a n d 1 e r
As I A t t a c h m e n t E v e n t s )
Sub
R e m o v e C h a n g e T r a c k E v e n t s H a n d l e r ( E v e n t H a n d e r As
IChangeTrackEvents)
Sub
R e m o v e L e v e l C h a n g e E v e n t s H a n d l e r ( E v e n t H a n d e r As
IL e v e l C h a n g e E v e n t s )
214 I Chapter 11:The Microstation Object Model - Objects I
Sub
RemoveModalDialogEventsHandler(EventHand1er As
IModal Dial ogEvents)
Sub
RemoveModelActivateEventsHandler(EventHand1er
As IModel Acti vateEvents)
Sub
RemoveModelChangeEventsHandler(EventHand1er As
IModel ChangeEvents)
Sub RemoveSaveAsEventsHandler(EventsHand1er As
ISaveAsEvents)
Sub RemoveViewUpdateEventsHandler(EventHand1er
As IViewUpdateEvents)
Sub ResetDisplaySet(Sh0wEverything As Boolean)
Sub Savesettingso
Sub Segment3dClosestPoint(Segment As
Segment3d, SpacePoi nt As Poi nt3d, C1 osePoi nt As
Point3d, C1 oseFracti on As Doubl e)
Sub Segment3dClosestPointBounded(Segment As
Segment3d, SpacePoi nt As Poi nt3d, C1 osePoi nt As
Point3d, C1 oseFracti on As Doubl e)
Sub Segment3dClosestPointBoundedXY(Segment As
Segment3d, SpacePoi nt As Poi nt3d, C1 osePoi nt As
Point3d, C1 oseFracti on As Doubl e)
Sub Segment3dClosestPointXY(Segment As
Segment3d, SpacePoi nt As Poi nt3d, C1 osePoi nt As
Point3d, C1 oseFracti on As Doubl e)
Function Segment3dFromPoint3dStartEnd(PointO
As Point3d, Point1 As Point3d) As Segment3d
Function
Segment3dFromPoint3dStartTangent(PointO As
Point3d, Tangent As Point3d) As Segment3d
Function Segment3dFromRay3d( Ray As Ray3d) As
Segment3d
Function
Segment3dFromTransform3dTimesSegment3d(Transfo
rm As Transform3d, Segment As Segment3d) As
Segment3d
Function Segment3dFromXYZXYZStartEnd(XO As
Double, Y O As Double, ZO As Double, X 1 As
I The Microstation Object Model I 21 5
Function Vector3dAngleBetweenVectors(Vectorl
As V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d ) As D o u b l e
Function Vector3dAngleBetweenVectorsXY(Vectorl
As V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d ) As D o u b l e
F u n c t i o n Vector3dAreVectorsParall e l ( V e c t o r 1 As
V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d ) As B o o l e a n
Function
Vector3dAreVectorsPerpendicular(Vectorl As
V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d ) As B o o l e a n
F u n c t i o n V e c t o r 3 d C r o s s P r o d u c t ( V e c t o r l As
V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d ) As V e c t o r 3 d
F u n c t i o n V e c t o r 3 d C r o s s P r o d u c t 3 P o i n t s ( O r i g i n As
P o i n t 3 d , T a r g e t 1 As P o i n t 3 d , T a r g e t 2 As
P o i n t 3 d ) As V e c t o r 3 d
F u n c t i o n V e c t o r 3 d C r o s s P r o d u c t X Y ( V e c t o r l As
V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d ) As D o u b l e
F u n c t i o n Vector3dDistance(Vectorl As V e c t o r 3 d ,
V e c t o r 2 As V e c t o r 3 d ) As D o u b l e
F u n c t i o n V e c t o r 3 d D i s t a n c e S q u a r e d ( V e c t o r l As
V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d ) As D o u b l e
F u n c t i o n V e c t o r 3 d D i s t a n c e S q u a r e d X Y ( V e c t o r l As
V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d ) As D o u b l e
F u n c t i o n V e c t o r 3 d D i s t a n c e X Y ( V e c t o r l As
V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d ) As D o u b l e
F u n c t i o n V e c t o r 3 d D o t P r o d u c t ( V e c t o r l As
V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d ) As D o u b l e
F u n c t i o n V e c t o r 3 d D o t P r o d u c t X Y ( V e c t o r l As
V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d ) As D o u b l e
F u n c t i o n V e c t o r 3 d D o t P r o d u c t X Y Z ( V e c t o r As
V e c t o r 3 d , Ax As D o u b l e , Ay As D o u b l e , Az As
D o u b l e ) As D o u b l e
F u n c t i o n V e c t o r 3 d E q u a l ( V e c t o r l As V e c t o r 3 d ,
V e c t o r 2 As V e c t o r 3 d ) As B o o l e a n
F u n c t i o n V e c t o r 3 d E q u a l T o l e r a n c e ( V e c t o r l As
V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d , T o l e r a n c e As
D o u b l e ) As B o o l e a n
F u n c t i o n V e c t o r 3 d F r o m M a t r i x 3 d C o l u m n ( M a t r i x As
M a t r i x 3 d , Col As L o n g ) As V e c t o r 3 d
F u n c t i o n V e c t o r 3 d F r o m M a t r i x 3 d R o w ( M a t r i x As
M a t r i x 3 d , Row As L o n g ) As V e c t o r 3 d
220 I Chapter 11 :The Microstation Object Model - Objects I
Function
Vector3dFromMatrix3dTimesVector3d(Matrix As
Matrix3d, Vector As Vector3d) As Vector3d
Function Vector3dFromMatrix3dTimesXYZ(Matrix
A s Matrix3d, X As Double, Y As Double, Z As
Double) A s Vector3d
Function
Vector3dFromMatrix3dTransposeTimesVector3d~Mat
rix As Matrix3d, Vector As Vector3d) As
Vector3d
Function
Vector3dFromMatrix3dTransposeTimesXYZ(Matrix
A s Matrix3d, X As Double, Y As Double, Z As
Double) A s Vector3d
Function Vector3dFromPoint3d(Point As Point3d)
As Vector3d
Function
Vector3dFromTransform3dColumn(Transform As
Transform3d, Col As Long) As Vector3d
Function Vector3dFromTransform3dRow(Transform
As Transform3d, Row As Long) As Vector3d
Function
Vector3dFromTransform3dTimesVector3d(Transform
A s Transform3d, Vector As Vector3d) As Vector3d
Function
Vector3dFromTransform3dTimesXYZ(Transform As
Transform3d, X As Double, Y As Double, Z As
Double) As Vector3d
Function
Vector3dFromTransform3dTranslation(Transform
A s Transform3d) As Vector3d
Function
Vector3dFromTransform3dTransposeTimesVector3d~
Transform As Transform3d, Vector As Vector3d)
As Vector3d
Function
Vector3dFromTransform3dTransposeTimesXYZ~Trans
form As Transform3d, X As Double, Y A s Double,
Z A s Double) As Vector3d
Function Vector3dFromXY(Ax As Double, Ay As
Double) As Vector3d
I The Microstation Object Model I 221
Function V e c t o r 3 d F r o m X Y A n g l e A n d M a g n i t u d e ( T h e t a
As Double, Magnitude As Double) As Vector3d
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
Vector3dIsVectorInCCWSector(TestVector As
Vector3d, Vector0 As Vector3d, Vectorl As
Vector3d, UpVector As Vector3d) As Boolean
Function
Vector3dIsVectorInCCWXYSector(TestVector As
Vector3d, Vector0 As Vector3d, Vectorl As
Vector3d) As Bool ean
Function
Vector3dIsVectorInSmallerSector(TestVector As
Vector3d, Vector0 As Vector3d, Vectorl As
Vector3d) As Bool ean
Function Vector3dMagnitude(Vector As Vector3d)
As Double
Function Vector3dMagnitudeSquared(Vector As
Vector3d) As Double
Function Vector3dMagnitudeSquaredXY(Vector As
Vector3d) As Double
Function Vector3dMagnitudeXY(Vector As
Vector3d) As Double
Function Vector3dMaxAbs(Vector As Vector3d) As
Double
Function Vector3dMaxAbsDifference(Vectorl As
Vector3d, Vector2 As Vector3d) As Double
Function Vector3dNegate(Vector As Vector3d) As
Vector3d
Function Vector3dNormalize(Vector As Vector3d)
As Vector3d
Function Vector3dOneO As Vector3d
Function
Vector3dPlanarAngleBetweenVectors(Vectorl As
222 I Chapter 11:The Microstation Object Model - Objects I
V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d , PlaneNorma1 As
V e c t o r 3 d ) As D o u b l e
F u n c t i o n V e c t o r 3 d P o l a r A n g l e ( V e c t o r As
V e c t o r 3 d ) As D o u b l e
F u n c t i o n V e c t o r 3 d R o t a t e X Y ( V e c t o r As V e c t o r 3 d ,
T h e t a As D o u b l e ) As V e c t o r 3 d
El F u n c t i o n V e c t o r 3 d S c a l e ( V e c t o r As V e c t o r 3 d ,
S c a l e As D o u b l e ) As V e c t o r 3 d
Function
Vector3dSignedAngleBetweenVectors(Vectorl As
V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d ,
O r i e n t a t i o n v e c t o r As V e c t o r 3 d ) As D o u b l e
Function
Vector3dSmallerAngleBetweenUnorientedVectors~V
e c t o r l As V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d ) As
Doubl e
Function
Vector3dSmallerAngleBetweenUnorientedVectorsXY
( V e c t o r 1 As V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d ) As
Doubl e
F u n c t i o n Vector3dSubtract(Vectorl As V e c t o r 3 d ,
V e c t o r 2 As V e c t o r 3 d ) As V e c t o r 3 d
F u n c t i o n Vector3dSubtractPoint3dPoint3d(Target
As P o i n t 3 d , Base As P o i n t 3 d ) As V e c t o r 3 d
F u n c t i o n Vector3dTripleProduct(Vectorl As
V e c t o r 3 d , V e c t o r 2 As V e c t o r 3 d , V e c t o r 3 As
V e c t o r 3 d ) As D o u b l e
F u n c t i o n Vector3dUnitPerpendicularXY(Vector As
V e c t o r 3 d ) As V e c t o r 3 d
F u n c t i o n V e c t o r 3 d Z e r o O As V e c t o r 3 d
P r o p e r t y V e r s i o n As S t r i n g { r e a d - o n l y }
P r o p e r t y V i s i b l e As B o o l e a n
P r o p e r t y W i d t h As Long
ApplicationObjectConnector
P r o p e r t y A p p l ic a t i on As A p p l ic a t i on { r e a d -
only1
Attachment
Sub A c t i v a t e 0
I The Microstation Object Model I 223
P r o p e r t y I s L o c k e d As B o o l e a n
P r o p e r t y I s M i s s i n g F i l e As B o o l e a n {read-only}
P r o p e r t y I s M i s s i n g M o d e l As B o o l e a n {read-
only}
P r o p e r t y I s R e a d O n l y As B o o l ean {read-only}
P r o p e r t y I s T r u e S c a l e As B o o l e a n {read-only}
P r o p e r t y L e v e l As L e v e l
P r o p e r t y L e v e l s As L e v e l s {read-only}
P r o p e r t y L i n e S t y l e s S c a l ed As B o o l ean
P r o p e r t y L o g i c a l D e s c r i p t i o n As S t r i n g
P r o p e r t y L o g i c a l N a m e As S t r i n g
P r o p e r t y M a n i p u l a t e A s E l ement As B o o l ean
P r o p e r t y M a s t e r o r i g i n As P o i n t 3 d {read-only}
P r o p e r t y M a s t e r u n i t As M e a s u r e m e n t u n i t
F u n c t i o n MdlModel R e f P ( ) As Long
Sub M o v e ( 0 f f s e t As P o i n t 3 d , A p p l y T o C l i p E l e m e n t
As B o o l e a n )
P r o p e r t y Name As S t r i n g
P r o p e r t y NamedGroup As S t r i n g
P r o p e r t y N e s t L e v e l As Long
P r o p e r t y N e s t o v e r r i d e s As M s d N e s t O v e r r i d e s
P r o p e r t y NewLevel D i s p l ay As MsdNewLevel D i s p l ay
P r o p e r t y P a r e n t M o d e l R e f e r e n c e As
Model R e f e r e n c e {read-only}
P r o p e r t y P l o t 3 d As B o o l e a n
P r o p e r t y P r e s e n t a t i o n As MsdRenderingMode
P r o p e r t y P r i n t C o l o r A d j u s t m e n t As B o o l e a n
Sub PropagateAnnotationScaleO
F u n c t i o n Range( I n c l u d e A t t a c h m e n t s As B o o l e a n )
As Range3d
F u n c t i o n R e a t t a c h ( F i 1 e N a m e As S t r i n g ,
ModelName As S t r i n g ) As A t t a c h m e n t
Sub Redraw(CDrawMode As MsdDrawingMode =
msdDrawi ngModeNormal1)
Sub RemoveEl e m e n t ( E l ement As E l ement
Sub Rep1 a c e E l e m e n t ( 0 l d E l ement As E l e m e n t ,
NewEl ement As E l e m e n t )
226 I Chapter 11:The Microstation Object Model - Objects I
El P r o p e r t y R e v i s i onNumber As S t r i n g
El Sub R e w r i t e 0
Sub R o t a t e ( P i v o t As P o i n t 3 d , AboutX As D o u b l e ,
AboutY As D o u b l e , A b o u t Z As D o u b l e ,
V i e w s p e c i f i e r As V a r i a n t )
El P r o p e r t y R o t a t i o n As M a t r i x 3 d { r e a d - o n l y }
El P r o p e r t y S c a l e F a c t o r As D o u b l e
El P r o p e r t y S c a l e M a s t e r U n i t s As Doubl e { r e a d -
only1
Sub S c a l e U n i f o r m ( 0 r i g i n As P o i n t 3 d ,
S c a l e F a c t o r As Doubl e , A p p l y T o C l ip E l ement As
B o o l ean
El F u n c t i o n Scan( [ S c a n C r i t e r i a As
E l e m e n t S c a n C r i t e r i a l ) As E l e m e n t E n u m e r a t o r
Sub S e l e c t E l ement ( E l ement As E l e m e n t ,
[ D i s p l ayAsSel e c t e d As B o o l ean = T r u e ] )
El Sub S e t A t t a c h N a m e D e f e r r e d ( F i 1 e S p e c i f i c a t i on As
String)
El Sub S e t S h e e t D e f i n i t i o n ( N e w D e f i n i t i 0 n As
SheetDefinition)
El Sub SetXData(App1icationName As S t r i n g ,
NewXDataO As XDatum)
P r o p e r t y S t o r a g e u n i t As M e a s u r e m e n t u n i t
El P r o p e r t y S u b u n i t As M e a s u r e m e n t u n i t
tN P r o p e r t y S u b U n i t s P e r M a s t e r U n i t As D o u b l e
{read-only}
El P r o p e r t y T r a n s p a r e n c y As D o u b l e
P r o p e r t y Type As MsdModelType
El Sub U n s e l e c t A l 1 E l ements ( )
Sub U n s e l e c t E l ement ( E l ement As E l e m e n t )
El P r o p e r t y UORsPerMasterUni t As Doubl e { r e a d -
only1
P r o p e r t y UORsPerStorageUni t As D o u b l e
El P r o p e r t y UORsPerSubUnit As D o u b l e { r e a d - o n l y }
P r o p e r t y U p d a t e o r d e r As Long
El P r o p e r t y U s e s L i g h t s As B o o l e a n
F u n c t i o n W o r k i n g U n i t s T o D o u b l e ( V a 1 ue As S t r i n g )
As D o u b l e
I The Microstation Object Model I 227
Attachments
F u n c t i o n A d d ( F i 1 e S p e c i f i c a t i o n As S t r i n g ,
Model Name As S t r i n g , L o g i c a l Name As S t r i n g ,
D e s c r i p t i o n As S t r i n g , M a s t e r o r i g i n As P o i n t 3 d ,
R e f e r e n c e o r i g i n As P o i n t 3 d , [ T r u e s c a l e As
Bool ean = T r u e ] , [ D i s p l ay Immedi a t e l y As Bool ean
= T r u e ] ) As A t t a c h m e n t
F u n c t i o n A d d C o i n c i d e n t ( F i 1 e S p e c i f i c a t i o n As
S t r i n g , ModelName As S t r i n g , LogicalName As
S t r i n g , D e s c r i p t i o n As S t r i n g ,
C D i s p l ay Immedi a t e l y As Bool ean = T r u e ] ) As
Attachment
F u n c t i on AddCoi n c i d e n t 1 ( F i 1 eSpec f i c a t i o n As
S t r i n g , ModelName As S t r i n g , Log calName As
S t r i n g , D e s c r i p t i o n As S t r i n g , F ags As
M s d A d d A t t a c h m e n t F l a g s ) As A t t a c h m e n t
F u n c t i o n AddUsingNamedView(Fi1eSpecification
As S t r i n g , LogicalName As S t r i n g , D e s c r i p t i o n
As S t r i n g , ViewName As S t r i n g , C e n t e r p o i n t As
Poi n t 3 d , C D i s p l ay Immedi a t e l y As Bool ean =
T r u e ] ) As A t t a c h m e n t
F u n c t i o n AddUsingNamedViewl(Fi1eSpecification
As S t r i n g , ModelName As S t r i n g , LogicalName As
S t r i n g , D e s c r i p t i o n As S t r i n g , ViewName As
S t r i n g , C e n t e r p o i n t As P o i n t 3 d , F l a g s As
M s d A d d A t t a c h m e n t F l a g s ) As A t t a c h m e n t
P r o p e r t y Count As Long {read only}
F u n c t i o n F i ndByLogi c a l Name( Log calName As
S t r i n g ) As A t t a c h m e n t
P r o p e r t y I t e m As A t t a c h m e n t read-only}
Sub Remove(AttachmentSpecifier As V a r i a n t )
CadInputMessage
Property CommandKeyin As S t r i n g { r e a d - o n l y }
Property C u r s o r B u t t o n As Long { r e a d - o n l y }
Property I n p u t T y p e As MsdCadInputType { r e a d -
only}
Property K e y i n As S t r i n g { r e a d - o n l y }
Property P o i n t As P o i n t 3 d { r e a d - o n l y }
Property S c r e e n p o i n t As P o i n t 3 d { r e a d - o n l y }
Property View As View { r e a d - o n l y }
228 I Chapter 11:The Microstation Object Model - Objects I
CadInputQueue
F u n c t i o n G e t I n p u t ( C T y p e 1 As M s d C a d I n p u t T y p e =
m s d C a d I n p u t T y p e A n y 1 , [ T y p e 2 As
M s d C a d I n p u t T y p e l , [ T y p e 3 As M s d C a d I n p u t T y p e l ,
[ T y p e 4 As M s d C a d I n p u t T y p e l ) As C a d I n p u t M e s s a g e
Sub SendCommand(Command As S t r i n g ,
[ T r e a t L i k e K e y b o a r d I n p u t As B o o l e a n ] )
Sub S e n d D a t a P o i n t ( D a t a P 0 i n t As P o i n t 3 d ,
[ V i e w s p e c i f i e r As V a r i a n t ] , [ Q u a l i f i e r As
Long])
Sub S e n d D a t a P o i n t F o r L o c a t e ( E 1 e m e n t T o L o c a t e As
E l e m e n t , D a t a P o i n t As P o i n t 3 d , [ V i ewSpeci f i e r
As V a r i a n t ] , [ Q u a l i f i e r As L o n g ] )
Sub SendDragPoints(DownPoint As P o i n t 3 d ,
U p P o i n t As P o i n t 3 d , [ V i e w s p e c i f i e r As V a r i a n t ] ,
[ Q u a l i f i e r As L o n g ] )
Sub S e n d K e y i n ( K e y i n As S t r i n g )
Sub S e n d L a s t I n p u t O
Sub SendMessageToApplication(MdlApp1ication As
S t r i n g , Message As S t r i n g )
Sub S e n d R e s e t O
Sub S e n d T e n t a t i v e P o i n t ( D a t a P 0 i n t As P o i n t 3 d ,
V i e w s p e c i f i e r As V a r i a n t )
DesignFile
F u n c t i o n AddNewLevel(Leve1Name As S t r i n g ) As
Level
Sub A t t a c h C o l o r T a b l e(Co1 o r T a b l e As Col o r T a b l e )
P r o p e r t y A u t h o r As S t r i n g
P r o p e r t y C l i e n t As S t r i n g
Sub C l o s e 0
P r o p e r t y Comments As S t r i n g
P r o p e r t y Company As S t r i n g
l l F u n c t i o n CustomPropertyExists(Name As S t r i n g )
As B o o l e a n
P r o p e r t y D a t e c r e a t e d As D a t e { r e a d - o n l y }
P r o p e r t y D a t e L a s t P l o t t e d As D a t e { r e a d - o n l y }
P r o p e r t y D a t e L a s t S a v e d As D a t e { r e a d - o n l y }
I The Microstation Object Model I 229
P r o p e r t y D e f a u l t M o d e l R e f e r e n c e As
Model R e f e r e n c e {read-only}
Sub D e l e t e L e v e l ( L e v e l As L e v e l )
P r o p e r t y D i m e n s i o n s t y l e s As D i m e n s i o n s t y l e s
{ read-on1y 1
P r o p e r t y E d i t o r As S t r i n g { r e a d - o n l y }
F u n c t i o n E x t r a c t C o l o r T a b l e ( As Col o r T a b l e
P r o p e r t y Fence As Fence { r e a d - o n l y }
F u n c t i o n FindSavedView(NamePattern As S t r i n g ,
C P r e v i o u s l y F o u n d S a v e d V i e w As
S a v e d V i e w E l e m e n t l , [Namespace As S t r i n g ] ) As
SavedVi ewEl ement
P r o p e r t y F o n t s As F o n t s { r e a d - o n l y }
P r o p e r t y F o r m a t As M s d D e s i g n F i l e F o r m a t { r e a d -
only}
P r o p e r t y F o r m a t M a j o r V e r s i o n As Long { r e a d -
only}
P r o p e r t y F o r m a t M i n o r V e r s i o n As Long { r e a d -
only}
P r o p e r t y F u l l N a m e As S t r i n g { r e a d - o n l y }
F u n c t i o n G e t C u s t o m P r o p e r t y ( N a m e As S t r i n g ) As
Variant
F u n c t i o n GetElementByID(Element1D As DLong) As
Element
F u n c t i o n GetElementByID64(ElementID64 As
Empty) As E l e m e n t
F u n c t i o n G e t L a r g e s t E l e m e n t I D ( ) As DLong
Sub G e t L a r g e s t E l e m e n t I D 6 4 0
P r o p e r t y I s A c t i v e As B o o l e a n {read-only}
P r o p e r t y Keywords As S t r i n g
P r o p e r t y L a s t S a v e d B y As S t r i n g
P r o p e r t y L e v e l s As L e v e l s {read-only}
P r o p e r t y L i n e s t y l e s As L i n e s t y l e s {read-only}
P r o p e r t y Manager As S t r i n g
F u n c t i o n M d l F i l e O b j P O As Long
F u n c t i o n MdlModel R e f P ( As Long
P r o p e r t y M o d e l s As M o d e l R e f e r e n c e s { r e a d - o n l y }
P r o p e r t y Name As S t r i n g { r e a d - o n l y }
230 I Chapter 11 :The Microstation Object Model - Objects I
P r o p e r t y NonModel E l e m e n t c a c h e As E l e m e n t c a c h e
{read-only}
P r o p e r t y P a t h As S t r i n g { r e a d - o n l y }
P r o p e r t y R e v i s i o n N u m b e r As S t r i n g { r e a d - o n l y }
Sub R e w r i t e L e v e l s ( )
Sub S a v e 0
Sub SaveAs ( NewFi 1 eName As S t r i n g , [ O v e r w r i t e As
B o o l e a n = F a l s e ] , CNewFormat As
MsdDesignFileFormat =
msdDesignFileFormatCurrentl)
Sub S e t C u s t o m P r o p e r t y ( N a m e As S t r i n g , V a l u e As
Variant
P r o p e r t y S u b j e c t As S t r i n g
P r o p e r t y T a g S e t s As T a g S e t s { r e a d - o n l y }
P r o p e r t y T e x t s t y l e s As T e x t s t y l e s { r e a d - o n l y }
P r o p e r t y T i t l e As S t r i n g
P r o p e r t y T o t a l E d i t i n g T i m e As Long { r e a d - o n l y }
P r o p e r t y ViewGroups As ViewGroups { r e a d - o n l y }
P r o p e r t y Views As Views { r e a d - o n l y }
Element
Sub A d d D a t a b a s e L i n k ( L i n k T 0 A d d As D a t a b a s e L i n k )
F u n c t i o n A d d T a g ( T a g D e f i n i t i 0 n As
T a g D e f i n i t i o n ) As T a g E l e m e n t
F u n c t i o n A d d T a g s ( T a g S e t As T a g S e t ) As
TagEl e m e n t (
Sub A d d U s e r A t t r i b u t e D a t a ( A t t r i b u t e 1 D As Long,
A t t r i b u t e D a t a As D a t a B l o c k )
F u n c t i o n A p p a r e n t C o l o r ( V i e w As V i e w ) As Long
F u n c t i o n A p p a r e n t L i n e S t y l e ( V i e w As V i e w ) As
L ineStyl e
F u n c t i o n A p p a r e n t L i n e W e i g h t ( V i e w As V i e w ) As
Long
P r o p e r t y AsAppl ic a t i o n E l ement As
Appl i c a t i o n E l e m e n t { r e a d - o n l y }
P r o p e r t y A s A r c E l e m e n t As A r c E l e m e n t { r e a d -
only}
P r o p e r t y AsAuxiliaryCoordinateSystemElement As
AuxiliaryCoordinateSystemElement { r e a d - o n l y }
I The Microstation Object Model I 231
Property AsBsplineCurveElement As
BsplineCurveElement {read-only}
Property AsBsplineSurfaceElement A s
BsplineSurfaceElement {read-only}
Property AsCell Element As Cell Element {read-
only}
Property AsChainabl eEl ement A s
ChainableElement {read-only}
Property AsCl osedEl ement As C1 osedEl ement
{ read-on1y 1
Property AsCompl exEl ement A s Compl exEl ement
{ read-on1y }
Property AsCompl exShapeEl ement As
Compl exShapeEl ement {read-only}
Property AsCompl exStringEl ement A s
Compl exStringEl ement {read-only}
Property AsConeElement As ConeElement {read-
only}
Property AsCurveElement A s CurveElement
{ read-on1y }
Property AsDimensionElement As
Di mensi onEl ement {read -on1y}
Property AsDroppabl eEl ement A s
Droppabl eEl ement {read-only}
Property AsEllipseElement As EllipseElement
{ read-on1y 1
Property AsEll ipti cal El ement A s
Elliptical Element {read-only}
Property AsIntersectabl eEl ement As
Intersectabl eEl ement {read-only}
Property AsLineEl ement A s LineEl ement {read-
only}
Property AsMul ti LineEl ement As
Multi LineElement {read-only}
Property AsNamedGroupElement A s
NamedGroupElement {read-only}
Property AsPl anarEl ement As P1 anarEl ement
{ read-on1y 1
Property AsPointStringElement A s
PointStringElement {read-only}
232 I Chapter 11 :The Microstation Object Model - Objects I
Property AsPossi blyPl anarEl ement As
PossiblyPl anarElement {read-only}
Property AsSavedVi ewEl ement As
SavedViewElement {read-only}
Property AsShapeElement As ShapeElement
{read-only}
Property AsSharedCell Defi ni ti onEl ement As
SharedCell DefinitionElement {read-only}
Property AsSharedCell Element As
SharedCell Element {read-only}
Property AsTagElement As TagElement {read-
only1
Property AsTextElement As TextElement {read-
only1
Property AsTextNodeEl ement As TextNodeEl ement
{read-only}
Property AsTraversabl eEl ement As
Traversabl eEl ement {read-only}
Property AsVertexList As VertexList {read-
only1
Property Cache As Elementcache {read-only}
Property CacheIndex As Long {read-only}
Property C1 ass As MsdEl ementCl ass
Function C l o n e 0 As Element
Property Color As Long
Function ConstructVertexList(To1erance As
Double) As P o i n t 3 d O
Property DateLastModified As Date {read-only}
S u b Del eteAl1 Tags ( )
S u b Del eteAl1 XData ( )
S u b DeleteTag(Tag As TagElement)
S u b DeleteTagSet(TagSet As TagSet)
Function DeleteUserAttributeData(Attribute1D
As Long, Index As Integer) As Integer
S u b DeleteXData(App1icationName As String)
S u b DrawToFile(Fi1eName As String, Width As
Long, Height As Long, CDrawBackGround As
Boolean = False])
Property Fi 1 ePosi ti on As Long {read-only}
I The Microstation Object Model I 233
I4 F u n c t i o n GetContainingNamedGroupsO As
NamedGroupEl e m e n t ( 1
I4 F u n c t i o n GetDatabaseLinks([DatabaseType As
M s d D a t a b a s e L i n k a g e l , [ E n t i t y N u m b e r As L o n g ] )
As D a t a b a s e L i n k ( )
I4 F u n c t i o n G e t P i c t u r e ( W i d t h As Long, He g h t As
Long, CDrawBackGround As B o o l e a n = Fa s e l l As
Unknown
I4 F u n c t i o n G e t R e l a t e d E l e m e n t s ( L o c k e d As 3001 e a n ,
C T r a v e r s e T y p e As MsdMemberTraverseType =
msdMemberTraverseCopy1, [ N e w T r a v e r s a l As
B o o l ean = T r u e 1 ) As E l e m e n t E n u m e r a t o r
I4 F u n c t i o n G e t T a g ( T a g S e t As T a g S e t , TagName As
S t r i n g ) As T a g E l e m e n t
I4 F u n c t i o n G e t T a g s O As T a g E l e m e n t O
I4 F u n c t i o n G e t U s e r A t t r i b u t e D a t a ( A t t r i b u t e 1 D As
L o n g ) As D a t a B l o c k (
I4 F u n c t i o n GetXData(App1icationName As S t r i n g )
As XDatumO
I4 F u n c t i o n GetXDataApplicationNamesO As
String00
I4 P r o p e r t y G r a p h i c G r o u p As Long
I4 F u n c t i o n H a s A n y D a t a b a s e L i n k s ( [ D a t a b a s e T y p e As
M s d D a t a b a s e L i n k a g e l , [ E n t i t y N u m b e r As L o n g ] )
As B o o l e a n
I4 P r o p e r t y HasAnyTags As B o o l e a n { r e a d - o n l y }
I4 F u n c t i o n H a s A n y X D a t a O As B o o l e a n
I4 F u n c t i o n HasXData(App1icationName As S t r i n g )
As B o o l e a n
I4 P r o p e r t y I D As DLong { r e a d - o n l y }
I4 P r o p e r t y I D 6 4 As Empty { r e a d - o n l y }
I4 P r o p e r t y I n D i s p l a y S e t As B o o l ean
I4 P r o p e r t y I s A p p l i c a t i o n E 1 ement As B o o l ean
{read-on Y }
I4 P r o p e r t y I s A r c E l ement As B o o l ean { r e a d - o n l y }
I4 P r o p e r t y IsAuxiliaryCoordinateSystemElement As
B o o l ean r e a d - o n 1 y }
I4 P r o p e r t y I s B s p l i n e C u r v e E l ement As B o o l ean
{read-on Y }
234 I Chapter 11:The Microstation Object Model - Objects I
El P r o p e r t y I s B s p l i n e S u r f a c e E l e m e n t As Boolean
{read-only}
P r o p e r t y I s C e l l Element As Boolean { r e a d - o n l y }
P r o p e r t y I s C h a i n a b l e E l e m e n t As Boolean { r e a d -
only}
P r o p e r t y IsCl osedEl ement As Bool e a n { r e a d -
only1
P r o p e r t y IsCompl exEl ement As Bool e a n { r e a d -
only}
P r o p e r t y IsCompl exShapeEl ement As Bool e a n
{read-only}
P r o p e r t y IsCompl e x S t r i ngEl ement As Bool e a n
{ read-on1y }
P r o p e r t y IsComponentElement As Boo e a n { r e a d -
only1
P r o p e r t y IsConeEl ement As Bool e a n r e a d - o n 1 y }
P r o p e r t y I s C u r v e E l ement As Bool e a n { r e a d - o n 1 y }
P r o p e r t y I s D i m e n s i o n E l e m e n t As Boo e a n { r e a d -
only}
P r o p e r t y I s D r o p p a b l eEl ement As Bool e a n { r e a d -
only1
P r o p e r t y I s E l 1 i pseEl ement As Bool e a n { r e a d -
only}
P r o p e r t y I s E l 1 i p t i c a l El ement As Bool e a n
{read-on Y}
P r o p e r t y I s G r a p h i c a l As Boolean { r e a d - o n l y }
P r o p e r t y I s H i d d e n As Boolean
P r o p e r t y I s I n t e r s e c t a b l eEl ement As Bool e a n
{ r e a d - o n Yl
P r o p e r t y I s L i n e E l e m e n t As Boolean { r e a d - o n l y }
P r o p e r t y I s L o c k e d As Boolean
P r o p e r t y I s M o d i f i e d As Boolean { r e a d - o n l y }
P r o p e r t y IsMul t i Li neEl ement As Bool e a n { r e a d -
only}
P r o p e r t y IsNamedGroupEl ement As Bool e a n
{read-only}
P r o p e r t y IsNew As Boolean { r e a d - o n l y }
P r o p e r t y I s P l a n a r E l ement As Bool e a n { r e a d -
only1
I The Microstation Object Model I 235
ElementEnumerator
F u n c t i o n B u i l d A r r a y F r o m C o n t e n t s O As E l e m e n t 0
F u n c t i o n C l o n e 0 As E l e m e n t E n u m e r a t o r
P r o p e r t y C u r r e n t As E l e m e n t { r e a d - o n l y }
F u n c t i o n M o v e N e x t O As B o o l e a n
Sub R e s e t ( )
Elementscancriteria
Sub E x c l u d e A l 1 C1 a s s e s ( )
Sub E x c l u d e A l 1 Col o r s ( )
Sub Excl udeAl1 Level s ( )
Sub E x c l u d e A l 1 L i n e S t y l es ( )
Sub E x c l u d e A l 1 L i neWei g h t s ( )
Sub Excl udeAl1Subtypes ( )
Sub E x c l u d e A l 1 Types ( )
Sub Excl udeGraphi c a l ( )
I The Microstation Object Model I 237
Level
Sub AddUserAttributeData(Attribute1D A s Long,
Attri buteData A s DataBl ock)
238 I Chapter 11 :The Microstation Object Model - Objects I
Function DeleteUserAttributeData(Attribute1D
As Long, Index As Integer) As Integer
Property Description As String
Property El ementAccess As
MsdLevel El ementAccess
Property ElementColor As Long
Property El ementLi neStyl e As Li neStyl e
Property El ement Li neWei g h t As Long
Function GetUserAttr buteData(Attribute1D As
Long) As DataBl ock()
Property ID As Long read-on1 y }
Property IsActive As Bool ean
Property IsDi spl ayed As Boolean
Property IsDi spl ayedInVi e w As Bool ean
Property IsEffectivelyDisplayedInView As
Boolean {read-only}
Property IsFromLevel Library As Boolean {read-
only1
Property IsFrozen As Boolean
Property IsInUse As Boolean {read-only}
Function IsInUseWithinModel (Model As
Model Reference) As Bool ean
Property IsLocked As Boolean
Property Name As String
Property Number As Long
Property OverrideColor As Long
Property OverrideLi neStyl e As Li neStyl e
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 Activate0
S u b AddEl ement (El ement As El ement)
I The Microstation Object Model I 239
Sub A d d E l e m e n t s ( E l e m e n t s 0 As - E l e m e n t )
F u n c t i o n AddNewNamedGroup([Name As S t r i n g ] ,
[ D e s c r i p t i o n As S t r i n g ] ) As NamedGroupElement
Sub A d d U s e r A t t r i b u t e D a t a ( A t t r i b u t e 1 D As Long,
A t t r i b u t e D a t a As D a t a B l o c k )
P r o p e r t y AnyEl e m e n t s S e l e c t e d As B o o l ean
{ read-on1y }
P r o p e r t y A s A t t a c h m e n t As A t t a c h m e n t { r e a d -
only}
P r o p e r t y A t t a c h m e n t s As A t t a c h m e n t s { r e a d -
only}
P r o p e r t y CanBePl acedAsCel1 As B o o l ean
P r o p e r t y C e l l Type As MsdCel 1 Type
P r o p e r t y C o n t r o l E l e m e n t C a c h e As E l e m e n t c a c h e
{ read-on1y 1
F u n c t i o n CopyEl e m e n t ( E l ement As E l e m e n t ,
CCopyContext As C o p y C o n t e x t l ) As E l e m e n t
Sub Del e t e A l 1 X D a t a (
F u n c t i o n DeleteUserAttributeData(Attribute1D
As Long, I n d e x As I n t e g e r ) As I n t e g e r
Sub DeleteXData(App1icationName As S t r i n g )
P r o p e r t y D e s c r i p t i o n As S t r i n g
P r o p e r t y D e s i g n F i l e As D e s i g n F i l e { r e a d - o n l y }
F u n c t i o n D o u b l e T o W o r k i n g U n i t s ( V a 1 u e As D o u b l e )
As S t r i n g
Function
E l ementCacheContainingFi1ePosi t i o n ( F i l ePosi t i o
n As Long, CCacheIndex As L o n g ] ) As
E l ementcache
F u n c t i o n GetElementByID(Element1D As DLong) As
Element
F u n c t i o n GetElementByID64(ElementID64 As
Empty) As E l e m e n t
F u n c t i o n G e t L a s t V a l i d G r a p h i c a 1 E l e m e n t ( ) As
Element
F u n c t i o n GetNamedGroup(GroupName As S t r i n g ) As
NamedGroupEl ement
F u n c t i o n G e t S e l e c t e d E l e m e n t s ( ) As
E l ementEnumerator
240 I Chapter 11:The Microstation Object Model - Objects I
El F u n c t i o n G e t S h e e t D e f i n i t i o n O As
SheetDefinition
El F u n c t i o n G e t U s e r A t t r i b u t e D a t a ( A t t r i b u t e 1 D As
L o n g ) As D a t a B l o c k ( )
F u n c t i o n GetXData(App1icationName As S t r i n g )
As XDatumO
El F u n c t i o n GetXDataApplicationNamesO As
String( )( )
P r o p e r t y G1 o b a l Ori g i n As P o i n t 3 d { r e a d - o n l y }
El P r o p e r t y G r a p h i c a l E l e m e n t c a c h e As E l e m e n t c a c h e
{read-only}
F u n c t i o n H a s A n y X D a t a O As B o o l e a n
El F u n c t i o n HasXData(App1 ic a t i onName As S t r i n g )
As B o o l e a n
P r o p e r t y I s 3 D As B o o l e a n { r e a d - o n l y }
El P r o p e r t y I s A c t i v e As B o o l e a n { r e a d - o n l y }
P r o p e r t y I s A t t a c h m e n t As B o o l e a n { r e a d - o n l y }
El P r o p e r t y I s E l ementSel e c t e d As B o o l ean { r e a d -
only}
P r o p e r t y I s L o c k e d As B o o l e a n
El P r o p e r t y I s R e a d O n l y As B o o l e a n { r e a d - o n l y }
P r o p e r t y L e v e l s As L e v e l s { r e a d - o n l y }
El P r o p e r t y M a s t e r u n i t As M e a s u r e m e n t u n i t
El F u n c t i o n M d l M o d e l R e f P O As Long
El P r o p e r t y Name As S t r i n g
P r o p e r t y P a r e n t M o d e l R e f e r e n c e As
Model R e f e r e n c e {read-only}
El Sub PropagateAnnotationScaleO
F u n c t i o n Range( I n c l u d e A t t a c h m e n t s As Boo e a n )
As Range3d
El Sub RemoveEl ement ( E l ement As E l e m e n t )
Sub Rep1 a c e E l ement ( 0 1 d E l ement As E l ement
NewEl ement As E l e m e n t )
El F u n c t i o n S c a n ( [ S c a n C r i t e r i a As
E l e m e n t S c a n C r i t e r i a l ) As E l e m e n t E n u m e r a t o r
tN Sub S e l e c t E l ement ( E l ement As E l ement ,
[ D i s p l ayAsSel e c t e d As B o o l ean = T r u e ] )
I Review I 241
Sub S e t S h e e t D e f i n i t i o n ( N e w D e f i n i t i 0 n As
SheetDefi n ition)
Sub SetXData(App1icationName As S t r i n g ,
NewXDataO As XDatum)
P r o p e r t y S t o r a g e u n i t As MeasurementUni
P r o p e r t y S u b u n i t As M e a s u r e m e n t u n i t
P r o p e r t y S u b U n i t s P e r M a s t e r U n i t As Doub e
{ read-on1y 1
P r o p e r t y Type As MsdModelType
Sub U n s e l e c t A l 1 E l e m e n t s (
Sub U n s e l e c t E l e m e n t ( E l ement As E l e m e n t )
P r o p e r t y UORsPerMasterUni t As D o u b l e { r e a d -
only}
P r o p e r t y UORsPerStorageUni t As D o u b l e
P r o p e r t y UORsPerSubUnit As D o u b l e { r e a d - o n l y }
F u n c t i o n W o r k i n g U n i t s T o D o u b l e ( V a 1 u e As S t r i n g )
As D o u b l e
MSDDESIGNFILEFORMAT
msdDesi g n F i 1 e F o r m a t C u r r e n t = 0
msdDesi g n F i 1 eFormatDWG = 3
msdDesi g n F i 1 eFormatDXF = 4
msdDesignFileFormatUnknown = -1
msdDesi g n F i 1 eFormatV7 = 1
msdDesi g n F i 1 eFormatV8 = 2
The enumeration name is MsdDesignFileFormat.It has six members
with values ranging from - 1 to 4. Each member in an enumeration has a
name and a value. The enumeration member
msdDesignFileFormatCurrent has a value of 0. As we saw in the
previous chapter, some properties and methods make use of these
enumerations. For example,
243
244 I Chapter 12: The Microstation Object Model - Enums I
S u b SaveAs(NewFi1eName As String, [Overwrite As -
= msdDesignFileFormatCurrent1)
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 over-
written, 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. S P Y ~ A S test.dgn, True,
A c t i v e D e s i g n F i 1e.SaveAs t e s t . d g n , T r u e , ~
MsdDesignFileFormat.msdDesignFi1eFormatDWG
The other way is to use the member name without the enumerator
name:
THEENUMERATIONLIST
MsdACSType
msdACSTypeCy1 i ndri cal = 2
msdACSTypeNone = 0
msdACSTypeRectangu1 ar = 1
msdACSTypeSpheri cal = 3
MsdAddAttachmentFlags
msdAddAttachmentElementsVisible = 4
msdAddAttachmentFlagCoincidentWorld = 2
msdAddAttachmentFlagNone = 0
msdAddAttachmentFlagTrueScale = 1
MsdAngleAccuracy
msdAngl eAccuracyO = 0
msdAngl eAccuracy1 = 1
msdAngl eAccuracy2 = 2
msdAngl eAccuracy3 = 3
msdAngl eAccuracy4 = 4
msdAngl eAccuracy5 = 5
msdAngl eAccuracy6 = 6
msdAngl eAccuracy7 = 7
msdAngl eAccuracy8 = 8
MsdAngleFormat
msdFormatDD-0000 = 0
msdFormatDD-MM-SS = 1
msdFormatGradians = 2
msdFormatRadi ans = 3
246 I Chapter 12: The Microstation Object Model - Enums I
MsdAngleMode
msdAngl eModeAzimuth = 1
msdAngl eModeBeari ng = 2
msdAngl eModeConventi onal = 0
MsdAttachMode
msdAttachNone = 1
msdAttachReference = 3
MsdBsplineCurveOffsetCuspType
msdBsplineCurveOffsetCuspArc = 4
msdBsplineCurveOffsetCuspChamfer = 1
msdBsplineCurveOffsetCuspJump = 0
msdBsplineCurveOffsetCuspParabola = 3
msdBsplineCurveOffsetCuspPoint = 2
MsdBsplineCurveType
msdBspl i neCurveCi rcl e = 3
msdBspl i neCurveCi rcul arArc = 2
msdBsplineCurveEllipse = 5
msdBspl i neCurveEl1 i pti cal Arc = 4
msdBsplineCurveGenera1 = 0
msdBsplineCurveHyperbolicArc = 7
msdBsplineCurveLine = 1
msdBsplineCurveParabolicArc = 6
MsdBsplineParametrizationType
msdBspl ineparametri zati onCentri petal = 2
msdBsplineParametrizationChordLength = 1
msdBsplineParametrizationInherited = 3
msdBspl ineparametri zati onUni form = 0
MsdBsplineSurfaceDirection
msdBsplineSurfaceU = 0
msdBsplineSurfaceV = 1
MsdBsplineSurfaceType
msdBsplineSurfaceCone = 3
msdBsplineSurfaceGenera1 = 0
I The Enumeration List I 247
MsdCadInputType
msdCadInputTypeAny = 5
msdCadInputTypeCommand = 1
msdCadInputTypeDataPoint = 3
msdCadInputTypeKeyin = 4
msdCadInputTypeReset = 2
msdCadInputTypeUnassignedCB = 6
MsdCelIType
msdCel1 TypeGraphi c = 0
msdCellTypeMenu = 1
msdCel1 TypePoi n t = 7
MsdChangePropagation
msdChangePropagationAlways = 2
msdChangePropagationGroupLock = 0
msdChangePropagationNever = 1
MsdChangeTrackAction
msdChangeTrackActionAdd = 2
msdChangeTrackActionAppData = 8
msdChangeTrackActionDelete = 1
msdChangeTrackActionDrop = 6
msdChangeTrackActionMark = 7
msdChangeTrackActionModelAdd = 9
m s d C h a n g e T r a c k A c t i o n M o d e 1 Delete = 10
msdChangeTrackActionModify = 3
msdChangeTrackActionModifyFence = 5
msdChangeTrackActionNewFilePositionAndModify = 4
248 I Chapter 12: The Microstation Object Model - Enums I
MsdCom mandResuIt
msdCommandResu1 t3dLi brary2dFi 1 e = 50
msdCommandResu1 t3dOnly = 39
msdCommandResultAcceptQuery = 68
msdCommandResu1 tBadCel1 Name = 47
msdCommandResu1 tCel1 Deleted = 59
msdCommandResu1 tCel1 Exists = 55
msdCommandResu1 tCel1 Li braryNotFound = 1 2
msdCommandResultCellNestingError = 43
msdCommandResultCellNotFound = 44
msdCommandResu1 tEl ementNotFound = 21
msdCommandResu1 tEmptyFence = 27
msdCommandResu1 tFi 1 eReadOnly = 287
msdCommandResultIllegalDefinition = 23
msdCommandResultInvalidReferenceOperation = 481
msdCommandResultNeedCharacters = 27
msdCommandResultNoActiveCel1 = 19
msdCommandResu1 tNoCel1 Library = 54
msdCommandResultNoFenceActive = 15
msdCommandResu1 tNoOri gi n = 56
msdCommandResu1 tOffDesignP1 ane = 22
msdCommandResultReferenceNotFound = 7
msdCommandResultSuccess = 0
msdCommandResultUnknownCommand = 16
msdCommandResu1 tVi ewNotFound = 1 8
MsdConversionMode
msdConversionModeAlways = 1
msdConversionModeNever = 0
msdConversionModePrompt = 2
MsdCoordinateAccuracy
msdAccuracy0 = 1
msdAccuracyl = 2
msdAccuracyl6th = 56
msdAccuracy2 = 3
msdAccuracy3 = 4
I The Enumeration List I 249
msdAccuracy32nd = 120
msdAccuracy4 = 5
msdAccuracy4th = 8
msdAccuracy5 = 6
msdAccuracy6 = 7
m s d A c c u r a c y 6 4 t h = 248
m s d A c c u r a c y 8 t h = 24
msdAccuracyHa1 f = 0
MsdCoordinateFormat
msdMasterUnits = 1
msdSubUnits = 0
msdWorkingUnits = 2
MsdCopyContextLevelOption
msdCopyContextLeve1 AlreadyRemapped = 4
msdCopyContextLeve1 B y U s e r P r e f e r e n c e = 0
msdCopyContextLevelCopyAlways = 3
msdCopyContextLevelCopyIfDifferent = 2
msdCopyContextLevelCopyIfNotFound = 1
MsdCopyViewPort
msdCopyViewPortApplyAspectRatio = 2
msdCopyViewPortApplySize = 3
msdCopyViewPortApplySizeAndPosition = 4
msdCopyViewPortKeepCurrent = 0
MsdDatabaseLinkage
msdDatabaseLinkageInformix = 1
m s d D a t a b a s e L i n k a g e I n g r e s = 32
msdDatabaseLinkageOdbc = 1 2 8
msdDatabaseLinkageO1 eDb = 256
msdDatabaseLinkageOrac1e = 8
msdDatabaseLinkageXBase = 4
MsdDataEntryRegionJustification
msdDataEntryRegionJustificationCenter = 0
msdData E n t r y Regi o n J u s t i f ica t i on L e f t = - 1
msdData E n t r y Regi on J u s t if ic a t ion R i g h t = 1
250 I Chapter 12: The Microstation Object Model - Enums I
MsdDesignFileFormat
msdDesignFileFormatCurrent = 0
msdDesignFileFormatDWG = 3
msdDesignFileFormatDXF = 4
msdDesignFileFormatUnknown = -1
msdDesignFileFormatV7 = 1
msdDesignFileFormatV8 = 2
MsdDevelopableElementOutputType
msdDevel o p a b l eCones = 4
msdDevel o p a b l eConesPl a n a r = 5
msdDevel o p a b l eRul e L i n e s = 0
msdDevel o p a b l eRul e L i n e s P l a n a r = 1
msdDevel o p a b l eShapes = 2
msdDevel o p a b l eShapesPl a n a r = 3
MsdDialogBoxResult
msdDi a1 ogBoxResul t A p p l y = 1
msdDi a1 ogBoxResul t C a n c e l = 4
msdDi a1 ogBoxResul t D e f a u l t = 5
msdDi a1 ogBoxResul t H e l p = 1 0
msdDi a1 ogBoxResul t N o = 7
msdDi a1 ogBoxResul t O K = 3
msdDi a1 ogBoxResul t R e s e t = 2
msdDi a1 ogBoxResul t R e t r y = 8
msdDi a1 ogBoxResul t S t o p = 9
msdDi a1 ogBoxResul t Y e s = 6
msdDi a1 ogBoxResul t Y e s T o A l 1 = 11
MsdDimAccuracy
msdDimAccuracy0 = 0
msdDimAccuracy1 = 129
msdDimAccuracyl6th = 8
msdDimAccuracy2 = 1 3 0
msdDimAccuracy3 = 1 3 2
msdDimAccuracy32nd = 1 6
msdDimAccuracy4 = 1 3 6
msdDimAccuracy4th = 2
I The Enumeration List I 251
msdDimAccuracy5 = 144
msdDimAccuracy6 = 160
msdDimAccuracy64th = 32
msdDimAccuracy7 = 192
msdDimAccuracy8 = 128
msdDimAccuracy8th = 4
msdDimAccuracyHa1 f = 1
msdDimAccuracySci 1 = 64
msdDimAccuracySci2 = 65
msdDimAccuracySci3 = 66
msdDimAccuracySci4 = 67
msdDimAccuracySci5 = 68
msdDimAccuracySci 6 = 69
msdDimAccuracySci7 = 70
msdDimAccuracySci8 = 7 1
MsdDimAlignment
msdDimAl ig n m e n t A r b i t r a r y = 3
msdDimAlignmentDrawing = 1
msdDimAlignmentTrue = 2
msdDimAlignmentView = 0
MsdDimAlternateThresholdComparison
MsdDimAlternateThresholdComparisonGreater = 1
MsdDimAl t e r n a t e T h r e s h o l d C o m p a r i s o n G r e a t e r O r E q u a 1 = 3
MsdDimAlternateThresholdComparisonLess = 0
MsdDimAl t e r n a t e T h r e s h o 1 dCompari sonLessOrEqual = 2
MsdDimAngleMeasure
MsdDimAngl eMeasureAng1 e = 1
MsdDimAngl e M e a s u r e A r c L e n g t h = 0
MsdDimBallAndChainAlignment
msdDimBallAndChainAlignmentAuto = 0
msdDimBallAndChainAlignmentLeft 1 =
msdDimBallAndChainA1 i g n m e n t R i g h t = 2
252 I Chapter 12: The Microstation Object Model - Enums I
MsdDimBallAndChainChainType
msdDimBall AndChainChainTypeArc = 2
m s d D i m B a l l AndChainChainTypeBSpline = 3
msdDimBallAndChainChainTypeLine = 1
m s d D i m B a l l AndChainChainTypeNone = 0
MsdDimCustomSymbol
msdDimCustomSymbolCharacter = 1
msdDimCustomSymbo1 D e f a u l t = 0
MsdDimDMSPrecisionMode
MsdDimDMSPreci s i onModeFixed = 0
MsdDimDMSPreci s i onModeFl o a t i n g = 1
MsdDimLabe1LineFormat
MsdDimLabel L i neFormatAng1 eAbove = 3
MsdDimLabel L i neFormatAng1 e B e l ow = 5
MsdDimLabel L i neFormatAng1 e O v e r L e n g t h = 1
MsdDimLabel L i n e F o r m a t L e n g t h A b o v e = 2
MsdDimLabel L i n e F o r m a t L e n g t h A n g 1 eAbove = 6
MsdDimLabel L i n e F o r m a t L e n g t h A n g 1 e B e l ow = 7
MsdDimLabel L i n e F o r m a t L e n g t h B e l ow = 4
MsdDimLabel L i n e F o r m a t S t a n d a r d = 0
MsdDimMLNoteFrameType
msdDimMLNoteFrameTypeBox = 2
msdDimMLNoteFrameTypeLine = 1
msdDimMLNoteFrameTypeNone = 0
MsdDimMLNoteJustification
msdDimMLNoteJusti f i c a t i o n c e n t e r = 3
msdDimMLNoteJusti f i c a t i onDynami c = 2
msdDimMLNoteJustificationLeft = 0
msdDimMLNoteJustificationRight = 1
MsdDimNoteHorizontalAttachment
msdDimNoteHorizontalAttachmentAuto = 0
msdDimNoteHorizontalAttachmentLeft = 1
I The Enumeration List I 253
msdDimNoteHorizontalAttachmentRight = 2
MsdDimNoteLeaderType
MsdDimNoteLeaderTypeCurve = 1
MsdDimNoteLeaderTypeLine = 0
MsdDimNoteTextRotation
msdDimNoteTextRotationHorizonta1 = 0
msdDimNoteTextRotationInline = 2
msdDirnNoteTextRotationVertica1 = 1
MsdDimNoteVerticaIAttachment
msdDimNoteVertica1AttachmentBottom = 4
msdDimNoteVertica1AttachmentBottomLine = 3
msdDimNoteVerticalAttachmentDynamicCorner = 6
msdDimNoteVerticalAttachmentDynamicLine = 5
msdDimNoteVerti c a l AttachmentMiddl e = 2
msdDimNoteVertica1AttachmentTop = 0
msdDimNoteVerticalAttachmentTopLine = 1
msdDimNoteVertica1Attachmentunderline = 7
MsdDimNoteVerticalJustification
msdDimNoteVertica1 J u s t i f i c a t i o n B o t t o m = 2
msdDimNoteVerti c a l J u s t i f i c a t oncenter = 1
m s d D i m N o t e V e r t i c a 1 J u s t i f i c a t onDynamic = 3
m s d D i m N o t e V e r t i c a l J u s t i f i c a t onTop = 0
MsdDimPlacementTextPosition
msdDimPlacementTextPos t i o n A u t o = 2
msdDimPlacementTextPos t i o n M a n u a l = 0
msdDimPlacementTextPos t i o n S e m i A u t o = 1
MsdDimRadialMode
msdDimRadi a1 M o d e c e n t e r M a r k = 0
msdDimRadi a1 ModeDi a m e t e r = 3
msdDimRadi a1 ModeDi a m e t e r E x t e n d e d = 4
msdDimRadi a1 ModeRadi us = 1
msdDimRadi a1 ModeRadi u s E x t e n d e d = 2
254 I Chapter 12: The Microstation Object Model - Enums I
MsdDimStackedFractionAlignment
MsdDimStackedFractionAlignmentBottom = 2
MsdDimStackedFractionAlignmentCenter = 1
MsdDimStackedFractionAlignmentTop = 0
MsdDimStackedFractionType
MsdDimStackedFractionTypeDiagonal = 2
MsdDimStackedFractionTypeFromFont = 0
MsdDimStackedFractionTypeHorizontal = I
MsdDimStyleProp
msdDimStylePropBal1 AndChainAlignment = 101
msdDimStylePropBallAndChainChainTerminator = 1 0 2
msdDimStylePropBal1 AndChainChainType = 1 0 3
msdDimStylePropBallAndChainIsActive = 1 0 4
msdDimSty1 ePropBal1 AndChai nNoDockOnDimLi ne = 1 0 6
msdDimStylePropBal1 AndChainShowTextLeader = 1 0 5
msdDimSty1 ePropExtensi onLi neAngl eChordAl i gn = 2 1 3
msdDimStyl ePropExtensi onLi neCol or = 201
msdDimStylePropExtensionLineExtend = 202
msdDimStyl ePropExtensi onLi neJoi n = 203
msdDimSty1 ePropExtensi onLi neLeft = 204
msdDimStyl ePropExtensi onLi neLi neStyl e = 205
msdDimStylePropExtensionLineOffset = 206
msdDimStyl ePropExtensi onLi neOverrideCo1 or = 207
msdDimSty1 ePropExtensi onLi neOverrideLi neStyl e = 208
msdDimStyl ePropExtensi onLi neOverri deWei ght = 209
msdDimSty1 ePropExtensi onLi neRi ght = 2 1 0
msdDimStyl ePropExtensi onLi neShowAny = 211
msdDimSty1 ePropExtensi onLi neWei ght = 2 1 2
msdDimStylePropGenera1 Alignment = 3 0 1
msdDimStylePropGenera1 CenterMarkSize = 302
msdDimStylePropGenera1 Color = 303
msdDimStylePropGenera1 Dimensionscale = 304
msdDimStyl ePropGenera1 DimStyl eDescri pti on = 305
msdDimStylePropGenera1 DimStyleName = 306
msdDimStylePropGeneralFont = 307
I The Enumeration List I 255
msdDimStylePropSymbo1 D i a m e t e r F o n t 602 =
msdDimStylePropSymbolDiameterType = 603
msdDimStylePropSymbo1 L o w e r P r e f i x C h a r = 604
msdDimStylePropSymbo1 L o w e r S u f f i x C h a r = 605
256 I Chapter 12: The Microstation Object Model - Enums I
msdDimStylePropSymbolMainPrefixChar = 606
msdDimSty1 ePropSymbo1 Mai nSuffixChar = 607
msdDimStyl ePropSymbo1 P1 usMi nusChar = 608
msdDimSty1 ePropSymbo1 P1 usMi nusType = 609
msdDimStyl ePropSymbo1 Prefix = 610
msdDimStylePropSymbolPrefixCellName = 611
msdDimStyl ePropSymbo1 Prefixchar = 612
msdDimSty1 ePropSymbo1 Prefi xFont = 613
msdDimStylePropSymbo1PrefixType = 614
msdDimStylePropSymbo1Suffix = 615
msdDimStylePropSymbo1Suffixcell Name = 616
msdDimStylePropSymbolSuffixChar = 617
msdDimStyl ePropSymbo1 Suffi xFont = 618
msdDimSty1 ePropSymbo1 Suffi xType = 619
msdDimStyl ePropSymbo1 To1 Prefixchar = 620
msdDimSty1 ePropSymbo1 To1 Suffixchar = 621
msdDimStylePropSymbolUpperPrefixChar = 622
msdDimStylePropSymbo1UpperSuffixChar = 623
msdDimStylePropTerminatorArrowCellName = 701
msdDimStylePropTerminatorArrowChar = 702
msdDimStylePropTerminatorArrowFont = 703
msdDimStylePropTerminatorArrowhead = 729
msdDimStylePropTerminatorArrowType = 704
msdDimStylePropTerminatorColor = 705
msdDimStylePropTerminatorDotCellName = 706
msdDimStylePropTerminatorDotChar = 707
msdDimStylePropTerminatorDotFont = 708
msdDimStylePropTerminatorDotType = 709
msdDimStylePropTerminatorFirst = 710
msdDimStylePropTerminatorHeight = 711
msdDimStylePropTerminatorJoint = 712
msdDimStylePropTerminatorLeft = 713
msdDimStylePropTerminatorLineStyle = 714
msdDimStylePropTerminatorMinLeader = 715
msdDimStylePropTerminatorMode = 716
msdDimStylePropTerminatorNoLineThruArrow = 717
I The Enumeration List I 257
m s d D i m S t y l e P r o p T e r m i n a t o r N o L neThruDot = 718
m s d D i m S t y l e P r o p T e r m i n a t o r N o L neThruOrigin = 719
m s d D i m S t y l e P r o p T e r m i n a t o r N o L neThruStroke = 720
msdDimStylePropTerminatorNote = 736
msdDimStylePropTerminatorNoteCel1 Name = 738
msdDimStylePropTerminatorNoteChar = 739
m s d D i m S t y l e P r o p T e r m i n a t o r N o t e F o n t = 740
msdDimStylePropTerminatorNoteType = 737
msdDimStylePropTerminatorOriginCellName = 721
m s d D i m S t y l e P r o p T e r m i n a t o r O r i g i n C h a r = 722
m s d D i m S t y l e P r o p T e r m i n a t o r O r i g i n F o n t = 723
msdDimStylePropTerminatorOriginType = 724
msdDimStylePropTerminatorOverrideColor = 725
msdDimStylePropTerminatorOverrideLineStyle = 726
m s d D i m S t y l e P r o p T e r m i n a t o r O v e r r i d e W e i g h t = 727
m s d D i m S t y l e P r o p T e r m i n a t o r R i g h t = 728
msdDimStylePropTerminatorStrokeCellName = 730
m s d D i m S t y l e P r o p T e r m i n a t o r S t r o k e C h a r = 731
m s d D i m S t y l e P r o p T e r m i n a t o r S t r o k e F o n t = 732
msdDimStylePropTerminatorStrokeType = 733
m s d D i m S t y l e P r o p T e r m i n a t o r W e i g h t = 734
msdDimStylePropTerminatorWidth = 735
msdDimStylePropTextArcLengthSymbo1 = 801
m s d D i m S t y l e P r o p T e x t A u t o L i f t = 802
msdDimStylePropTextCapsule = 804
msdDimStyl ePropTextCo1 or = 805
msdDimStylePropTextDecimalComma = 806
msdDimStylePropTextFont = 808
msdDimStylePropTextFrameType = 837
msdDimStylePropTextHeight = 809
msdDimStyl ePropTextHorizonta1 = 810
msdDimStylePropTextHorizontalMargin = 811
msdDimStylePropTextInlineTextLift = 838
msdDimStylePropTextJustification = 812
msdDimStylePropTextLeadingZero = 813
msdDimStylePropTextLocation = 835
258 I Chapter 12: The Microstation Object Model - Enums I
msdDimStyl ePropTextOmi tLeadi ngDel imi ter = 815
msdDimStylePropTextOverrideColor = 8 1 6
msdDimStylePropTextOverrideHeight = 8 1 7
msdDimStylePropTextOverrideStackedFractions = 833
msdDimStylePropTextOverrideUnderline = 8 3 4
msdDimStylePropTextOverrideWeight = 818
msdDimStylePropTextOverrideWidth = 8 1 9
msdDimStylePropTextSecLeadingZero = 8 2 0
msdDimStylePropTextShowSecondary = 8 2 1
msdDimStylePropTextStackedFractionAlignment = 8 2 9
msdDimStylePropTextStackedFractions = 8 3 0
msdDimStylePropTextStackedFractionScale = 8 3 2
msdDimStylePropTextStackedFractionType = 831
msdDimStylePropTextSuperscriptMode = 8 3 9
msdDimStylePropTextTextStyle = 8 2 7
msdDimStylePropTextTextStyleID = 8 2 8
msdDimStylePropTextUnderline = 8 2 2
msdDimStylePropTextVerticalMargin = 8 2 4
msdDimStylePropTextVerticalOpts = 8 3 6
msdDimStylePropTextWeight = 8 2 5
msdDimStylePropTextWidth = 8 2 6
msdDimStylePropToleranceAccuracy = 9 1 0
msdDimStylePropToleranceLowerValue = 9 0 1
msdDimStylePropToleranceMode = 9 0 2
msdDimStylePropToleranceSecAccuracy = 9 1 1
msdDimStylePropToleranceShow = 9 0 3
msdDimStylePropToleranceStackEqua1 = 9 0 4
msdDimSty1 ePropTol eranceTextHori zontal Margi n = 9 0 5
msdDimStylePropToleranceTextScale = 9 0 6
msdDimStylePropToleranceTextVerticalMargin = 9 0 7
msdDimStyl ePropTol eranceTextVerti cal Separation = 908
msdDimStylePropToleranceUpperValue = 9 0 9
msdDimStylePropValueAccuracy = 1 0 0 1
msdDimStylePropValueAltAccuracy = 1 0 0 2
msdDimStyl ePropVal ueAl tFormat = 1 0 6 7
msdDimStylePropValueAltIsActive = 1 0 0 3
I The Enumeration List I 259
m s d D i m S t y l e P r o p V a l u e A l t S e c A c c u r a c y = 1004
m s d D i m S t y l e P r o p V a l u e A l t S e c F o r m a t = 1069
m s d D i m S t y l e P r o p V a l u e A l t S e c I s A c t i ve = 1005
msdDimStylePropValueAltSecShowZeroMasterUnit = 1012
msdDimStylePropValueAltSecShowZeroSubUnit = 1081
msdDimStyl ePropVal ueAl tSecThresho1 d = 1013
msdDimStylePropValueAltSecThresholdComparison = 1071
msdDimStylePropValueAltShowZeroMasterUnit = 1020
msdDimStylePropValueAltShowZeroSubUnit = 1079
m s d D i m S t y l e P r o p V a l u e A l t T h r e s h o l d = 1021
m s d D i m S t y l e P r o p V a l u e A l t T h r e s h o l dCompari s o n = 1070
m s d D i m S t y l e P r o p V a l ueAngl e F o r m a t = 1023
msdDimStylePropValueAngleLeadingZero = 1024
m s d D i m S t y l e P r o p V a l ueAngl eMeasure = 1025
m s d D i m S t y l e P r o p V a l ueAngl e P r e c i s i o n = 1026
msdDimStylePropValueAngleTrailingZeros = 1027
msdDimStylePropValueDMSPrecisionMode = 1082
msdDimStylePropValueFormat = 1066
m s d D i m S t y l e P r o p V a l u e L a b e l L i n e F o r m a t = 1077
m s d D i m S t y l e P r o p V a l u e N o R e d u c e A l t F r a c t i o n = 1043
msdDimStylePropValueNoReduceAltSecFraction = 1062
m s d D i m S t y l e P r o p V a l u e N o R e d u c e F r a c t i o n = 1042
m s d D i m S t y l e P r o p V a l u e N o R e d u c e S e c F r a c t i on = 1061
m s d D i m S t y l e P r o p V a l u e N o R e d u c e T o l F r a c t i o n = 1044
m s d D i m S t y l e P r o p V a l ueNoReduceTo1 S e c F r a c t i on = 1063
m s d D i m S t y l e P r o p V a l ueOrdDatumVa1 ue = 1057
m s d D i m S t y l e P r o p V a l u e O r d D e c r e m e n t R e v e r s e = 1055
m s d D i m S t y l e P r o p V a l u e O r d F r e e L o c a t i o n = 1065
msdDimStylePropValueOrdUseDatumValue = 1056
m s d D i m S t y l e P r o p V a l ueRoundLSD = 1028
msdDimStylePropValueSecAccuracy = 1029
msdDimStylePropValueSecFormat = 1068
msdDimStylePropValueSecShowTrailingZeros = 1033
msdDimStylePropValueSecShowZeroMasterUnit = 1035
msdDimStylePropValueSecShowZeroSubUnit = 1080
msdDimStylePropValueSecUnitMaster = 1075
260 I Chapter 12: The Microstation Object Model - Enums I
msdDimStylePropValueSecUnitSub = 1076
msdDimStylePropValueShowTrailingZeros = 1039
msdDimStylePropValueShowZeroMasterUnit = 1041
msdDimStylePropValueShowZeroSubUnit = 1078
msdDimStylePropValueSuperscriptLSD = 1045
msdDimStylePropValueThousandsOpts = 1072
msdDimStylePropValueUnit = 1048
msdDimStylePropValueUnitLabelMaster = 1049
msdDimStylePropValueUnitLabe1 SecMaster = 1050
msdDimStylePropValueUnitLabelSecSub = 1051
msdDimStyl ePropVal ueUni tLabel Sub = 1052
msdDimStylePropValueUnitMaster = 1073
msdDimStylePropValueUnitSec = 1053
msdDimStylePropValueUnitSub = 1074
msdDimStylePropValueUseWorkingUnits = 1054
MsdDimSuperscriptMode
MsdDimSuperScriptModeFromFont = 0
MsdDimSuperScriptModeGenerated = 1
MsdDimSymbolType
msdDimSymbol TypeCell = 2
msdDimSymbolTypeCharacter = 1
msdDimSymbolTypeDefau1 t = 0
MsdDimTerminatorArrowhead
msdDimTerminatorArrowheadClosed = 1
msdDimTerminatorArrowheadFilled = 2
msdDimTerminatorArrowheadOpen = 0
MsdDimTerminatorMode
msdDimTerminatorModeAuto = 0
msdDimTerminatorModeInside = 2
msdDimTerminatorModeOutside = 3
msdDimTerminatorModeReversed = 1
MsdDimTerminatorType
msdDimTerminatorTypeArrow = 1
I The Enumeration List I 261
msdDimTermi n a t o r T y p e C i r c l e = 3
msdDimTerminatorTypeDot = 4
msdDimTerminatorTypeNone = 0
msdDimTerminatorTypeNote = 5
msdDimTerminatorTypeOrigin = 3
msdDimTerminatorTypeStroke = 2
MsdDimTextField
msdDimTextFie1dLowerLimi t = 1
msdDi mText F i e l dMai n = 0
msdDi mText F i e l dMi n u s = 2
m s d D i m T e x t F i e 1 dP1 us = 1
msdDimTextFie1dUpperLimi t = 0
MsdDimTextFormat
MsdDimTextFormatMU = 0
MsdDimTextFormatMU-dash-SU = 4
MsdDimTextFormatMU-Label = 1
Msd D i mText Forma tMU-La be1 -da s h-SU-La be1 = 6
MsdDimText FormatMU-Label-SU-Label = 5
MsdDimTextFormatSU = 2
MsdDimTextFormatSU-Label = 3
MsdDimTextFrameType
MsdDimTextFrameTypeBox = 1
MsdDimTextFrameTypeCapsule = 2
MsdDimTextFrameTypeNone = 0
MsdDimTextJustification
msdDimTextJustificationCenter = 2
msdDi m T e x t J u s t i f ic a t i on L e f t = 1
msdDi m T e x t J u s t i f ic a t i o n R i g h t = 3
MsdDimTextLocation
MsdDimTextLocationAbove = 1
MsdDimTextLocationInline = 0
MsdDimTextLocationOutside = 2
MsdDimTextLocationTopLeft = 3
262 I Chapter 12: The Microstation Object Model - Enums I
MsdDimTextOrientation
MsdDimTextOri entati onAl i gned = 0
MsdDimTextOri entati onHori zontal = 1
MsdDimThousandsOpts
MsdDimThousandsOptsComma = 2
MsdDimThousandsOptsNone = 0
MsdDimThousandsOptsSpace = 1
MsdDimToleranceType
MsdDimTol eranceTypeLimi t = 1
MsdDimTol eranceTypeP1 usMi nus = 0
MsdDimType
msdDimTypeAng1 eAxi s = 10
msdDimTypeAng1 eAxi sX = 50
msdDimTypeAng1 eAxi sY = 51
msdDimTypeAng1 eLi nes = 9
msdDimTypeAng1 eLocati on = 7
msdDimTypeAng1 eSi z e = 5
msdDimTypeArcLocation = 8
msdDimTypeArcSi z e = 6
msdDimTypeCenter = 19
msdDimTypeCustomLinear = 1 5
msdDimTypeDiameter = 1 2
msdDimTypeDiameterExtended = 18
msdDimTypeDiameterPara = 1 3
msdDimTypeDiameterPerp = 1 4
msdDimTypeLabe1 Line = 52
msdDimTypeLocateSi ngl e = 3
msdDimTypeLocateStacked = 4
msdDimTypeNone = 0
msdDimTypeNote = 53
msdDimTypeOrdinate = 1 6
msdDimTypeRadi us = 11
msdDimTypeRadiusExtended = 17
msdDimTypeSizeArrow = 1
I The Enumeration List I 263
msdDimTypeSizeStroke = 2
msdDimTypeUseActive = - 1
MsdDimValueAngleFormat
msdDimVal ueAngl eFormatCentesima1 = 2
msdDimVa1 ueAngl eFormatDegMinSec = 1
msdDimVal ueAngl eFormatDegrees = 0
msdDimVa1 ueAngl eFormatRadi ans = 3
MsdDimValueAnglePrecision
msdDimVal u e A n g l e P r e c i s i o n l P 1 ace = 1
msdDimVa1 u e A n g l e P r e c i s i o n Z P 1 ace = 2
msdDimVal u e A n g l e P r e c i s i o n 3 P l ace = 3
msdDimVa1 u e A n g l e P r e c i s i o n 4 P l ace = 4
msdDimVal u e A n g l e P r e c i s i o n 5 P l ace = 5
msdDimVa1 u e A n g l e P r e c i s i o n G P 1 ace = 6
msdDimVal u e A n g l e P r e c i s i o n W h o l e = 0
MsdDimVerticalTextOptions
MsdDimVerti c a l TextOptionsAl ways = 1
MsdDimVertical TextOptionsNever = 0
MsdDimVertical TextOptionsNoFi t = 2
MsdDrawingMode
msdDrawingModeErase = 1
msdDrawingModeHilite = 2
msdDrawi ngModeNorma1 = 0
msdDrawingModeTemporary = 3
msdDrawingModeTemporaryErase = 4
msdDrawingModeXor = 6
MsdElementCachePurpose
msdElementCachePurposeContro1 = 2
msdEl ementCachePurposeGraphi c a l = 4
msdElementCachePurposeNonMode1 = 1
MsdElementclass
msdElementClassConstruction = 2
264 I Chapter 12: The Microstation Object Model - Enums I
msdEl ementCl asscontructi onRul e = 6
msdEl ementCl assDimensi on = 3
msdEl ementCl assLinearPatterned = 5
msdElementClassPatternComponent = 1
msdEl ementCl assprimary = 0
msdEl ementCl assPrimaryRul e = 4
MsdElementSubtype
msdEl ementSubtypeApp1 i cati onEl ement = 20
msdElementSubtypeAuxiliaryCoordinateSystem = 3
msdElementSubtypeNone = -1
msdElementSubtypeUpdateSequenceElement = 33
MsdElementType
msdEl ementType44 = 44
msdElementTypeArc = 1 6
msdEl ementTypeBsp1 i neBoundary = 25
msdElementTypeBsplineCurve = 27
msdEl ementTypeBsp1 i neKnot = 26
msdEl ementTypeBsp1 i nePol e = 21
msdElementTypeBsplineSurface = 2 4
msdEl ementTypeBsp1 i neWei ght = 28
msdEl ementTypeCel1 Header = 2
msdEl ementTypeCel1 Li braryHeader = 1
msdEl ementTypeComp1 exshape = 1 4
msdEl ementTypeComp1 exStri ng = 1 2
msdElementTypeCone = 23
msdEl ementTypeConi c = 1 3
msdElementTypeCurve = 11
msdEl ementTypeDesi gnFi 1 eHeader = 9
msdElementTypeDgnStoreComponent = 38
msdElementTypeDgnStoreHeader = 39
msdEl ementTypeDi gSetData = 8
msdEl ementTypeDimensi on = 33
msdEl ementTypeEl1 i pse = 1 5
msdElementTypeGroupData = 5
msdEl ementTypeLeve1 Mask = 99
4
0-l
r
.
II co
4 0 r
.
4 c,o 0
N 4 S 4 C O N m
m o 0 a, 0 N 4
0 4 II 4 S II 4 r.m
4 4 CO 0 0 d C O
II c, CO 0-l Q c, II 4 4
II 4 S II E S N N
0 m co a, II r
. II 0 a, a, I r .
m 4 c, Lo co S L N d CO 0 E u d
c, m 0 0 a, N c, 0-l a, a, 1 .r u) II 4
d m II n 4 II co Q u S II 0 0 0 L 0-l r
. N
n L m E m II
a, II S S m L 0-l L I
r
. a,
II c, II
II Lo
.r C O Q m s
4 aJ0-l s z o
u 0 u .r
II 0 II L N c ,
z m a , m
L L c, c, c, c,
E .r Lo v, Lo v, Lo cc Lc m m
a, a, n c, c , 3 3 0 -
c, c, Q Q O , X X a J a , S O
m m m o m m m m m a, a, 1 C m m m a, a, .r .r a, .r
z E
a, a,
z a a a a a a a a v, v,
a J a J a , a, a, a, a, a, a, a, a,
+ + + + + > >
a J a J a , a, a J a J a ,
L >
aJv,
Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q L c v ,
h h h h h h h h h h h h h h h h h h h h a J w
+
Q
+ Q
+ + + + + + + + + + +
4 3 4 3 4 3 Q Q Q Q Q Q Q Q
+ + + + + + +
4 3 4 3 4 3 Q 4 3 4 3 4 3
c f 0
- 0 0
s s s s S S s s s s s s s S S S S S S S S s s s s s s S s s s m a
a J w a J w aJ a, a J w a J w a J w a J a, aJ a, aJ a, aJ a, aJ W a J W a J W a J a, a J w a J L L
E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E 0 0
a J w a J w aJ a, a J w a J w a J w a J a, aJ a, aJ a, aJ a, aJ W a J W a J W a J a, a J w a J L L
7 - 7 7 7 7 7 - 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 L L
w w w w W W w w w w w w w W W W W W W W W w w w w w w W w w w w w
-0-0-0-0 -0 -0 -0-0-0-0 -0-0-0 -0 -0 -0 -0 -0 -0 -0 -0 -0-0-0 -0-0-0 -0 -0-0-0 -0-0
C n c n c n v , v, v, C n c n c n v , C n c n v , v, v, v, v, v, v, v, v, C n c n v , C n c n v , v, C n c n v , W v ,
E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E
266 I Chapter 12: The Microstation Object Model - Enums I
msdErrorAcsNotFound = -2147220744
msdErrorAcsReplaced = -2147220745
msdErrorAddressNotKnown = -2147220784
msdErrorAddressNotValid = -2147220779
msdErrorAlreadyExists = -2147218310
msdErrorAlreadyInUse = -2147220804
msdErrorAlreadyOpen = -2147218312
msdErrorBadBSplineElement = -2147217996
msdErrorBadCharacterConstant = -2147220794
msdErrorBadContinuity = -2147217986
msdErrorBadElement = -2147218399
msdErrorBadFile = -2147218304
msdErrorBadFloat = -2147220796
msdErrorBadFormat = -2147218309
msdErrorBadHexNumber = -2147220799
msdErrorBadIndex = -2147218370
msdErrorBadKnots = -2147217991
msdErrorBadLineWeights = -2147217990
msdErrorBadModelId = -2147218334
m s d E r r o r B a d M o d e l R e f e r e n c e = -2147218397
msdErrorBadName = -2147218316
msdErrorBadNumber = -2147220800
msdErrorBadOcta1 = -2147220797
msdErrorBadOrder = -2147217994
msdErrorBadParameter = -2147217995
msdErrorBadPeriodicity = -2147217993
msdErrorBadPoles = -2147217992
msdErrorBadRasterFormat = -2147218350
msdErrorBadResourceType = -2147220772
msdErrorBadScanList = -2147218389
msdErrorBadSpi ral Definition = -2147217989
msdErrorBadString = -2147220795
msdErrorBadType = -2147220803
msdErrorBadVersion = -2147218308
msdErrorBadWordsToFollow = -2147218311
msdErrorCacheInUse = -2147218318
I The Enumeration List I 267
msdErrorCacheLoadError = -2147218291
msdErrorCacheNotEnabled = -2147218320
msdErrorCacheNotFilled = -2147218288
msdErrorCacheNotFound = -2147218317
msdErrorCannotCreateFile = - 2 1 4 7 2 1 8 3 2 9
msdErrorCannotDereference = - 2 1 4 7 2 2 0 7 8 7
msdErrorCannotImportSeed = - 2 1 4 7 2 1 8 2 9 2
msdErrorCannotOpenFile = -2147218391
msdErrorCannotOpenSeed = -2147218303
msdErrorCannotSaveFile = -2147218328
m s d E r r o r C e l l E x i s t s = -2147218372
msdErrorCel1 L i b r a r y I s 2 d = -2147218365
msdErrorCellNotFound = -2147218373
msdErrorCellTooLarge = -2147218369
msdErrorCircularDependency = - 2 1 4 7 2 1 9 6 0 4
msdErrorCommandReceived = - 2 1 4 7 2 2 0 7 0 4
msdErrorComplexHeaderRequired = - 2 1 4 7 2 1 8 3 8 7
msdErrorCompressionError = - 2 1 4 7 2 1 8 2 9 6
msdErrorCopyError = -2147218289
msdErrorDiskFul1 = -2147218395
msdErrorDivideByZero = -2147220780
msdErrorDup1 i c a t e L o g i c a l = - 2 1 4 7 2 2 0 7 6 6
msdErrorDuplicateTaskId = -2147218353
m s d E r r o r E l e m e n t F i l l e d = -2147220756
msdErrorElementFrozen = -2147218359
msdErrorElementNotFilled = - 2 1 4 7 2 2 0 7 5 5
msdErrorElementNotFound = -2147218323
msdErrorElementNotPlanar = - 2 1 4 7 2 2 0 7 5 3
msdErrorElementTooLarge = -2147220754
msdErrorEndOfFile = -2147218390
msdErrorException = -2147219504
m s d E r r o r F i l e E x i s t s = -2147218326
msdErrorFileNotFound = -2147218338
msdErrorHasChanges = -2147218298
m s d E r r o r I d E x i s t s = -2147218321
msdErrorIdNotFound = -2147218322
268 I Chapter 12: The Microstation Object Model - Enums I
msdErrorIllegalCharacter = -2147220793
msdErrorInsufficientInformation = -2147218401
msdErrorInsufficientMemory = -2147218388
msdErrorIntegralNeeded = -2147220777
msdErrorInvalidACSType = -2147220770
msdErrorInvalidButton = -2147220769
msdErrorInvalidCel1 = -2147218371
msdErrorInvalidClip = -2147220761
msdErrorInvalidForFloat = -2147220781
msdErrorInvalidForFunction = -2147220771
msdErrorInvalidForStructure = -2147220782
msdErrorInvalidForType = -2147220778
msdErrorInva1 idLi brary = -2147218368
msdErrorInvalidMaterOrigin = -2147220763
msdErrorInvalidOperationForNested = -2147218302
msdErrorInvalidOperationForNonNested = -2147218301
msdErrorInvalidPatternSpace = -2147220760
msdErrorInvalidReference = -2147220762
msdErrorInvalidReferenceOrigin = -2147220764
msdErrorInvalidSymbo1 = -2147220789
msdErrorLinkageNotFound = -2147218344
msdErrorLoadingInterface = -2147218297
m s d E r r o r M o d e l e r N o t L o a d e d = -2147219703
msdErrorMode1 IdExists = -2147218332
msdErrorModelNameExists = -2147218333
msdErrorMode1 NotEmpty = -2147218342
msdErrorModifyComplex = -2147218392
msdErrorNameNotUnique = -2147218343
msdErrorNameTooLong = -2147218335
msdErrorNeedExponent = -2147220798
msdErrorNeedInteger = -2147220802
msdErrorNoAcsDefined = -2147220746
msdErrorNoBounds = -2147218001
msdErrorNoBSplineHeader = -2147217999
msdErrorNoCel1 Library = -2147218375
msdErrorNoClipVolume = -2147218336
IThe Enumeration List I 269
msdErrorNoFence = -2147218337
msdErrorNoGraphicGroup = -2147220742
msdErrorNoKnots = -2147218003
msdErrorNoLevelMask = -2147220740
msdErrorNoLineWeights = -2147218002
msdErrorNoMatch = -2147218381
msdErrorNoMode1 = -2147218299
msdErrorNoMode1 Information = -2147218331
msdErrorNonClosedElement = -2147220757
msdErrorNonClosedPatternElement = -2147220759
msdErrorNonCoplanarShapes = -2147220750
msdErrorNonSolidPatternElement = -2147220758
msdErrorNoNumberBounds = -2147218000
msdErrorNoOffsetIntersection = -2147217987
msdErrorNoParentMode1 = -2147218330
msdErrorNoPoles = -2147218004
msdErrorNoReferenceSlots = -2147220747
msdErrorNoselectionSet = -2147220748
msdErrorNoSuchMode1 = -2147218294
msdErrorNoSymbo1 = -2147220791
msdErrorNotDesignFIle = -2147220768
msdErrorNotDirectAttachment = -2147220739
msdErrorNotFunction = -2147220775
msdErrorNotLoaded = -2147218300
msdErrorNotLocked = -2147218293
msdErrorNotMember = -2147220785
msdErrorNotOpen = -2147218315
msdErrorNotSingleView = -2147220765
msdErrorNotStructure = -2147220786
msdErrorNotSupported = -2147218348
msdErrorNotValidExpression = -2147220776
msdErrorNu1 lSol ution = -2147220752
msdErrorOldMaterialTable = -2147220749
msdErrorOperationCanceled = -2147218306
msdErrorParasolidError = -2147219703
msdErrorReadOnly = -2147218396
270 I Chapter 12: The Microstation Object Model - Enums I
m s d E r r o r R e c u r s e L i m i t = -2147217985
msdErrorRenameError = -2147218290
m s d E r r o r R e q u i r e s 3 d F i l e = -2147218400
m s d E r r o r R e s o u r c e N o t F o u n d = -2147218376
m s d E r r o r S h a r i n g V i o l a t i o n = -2147218314
m s d E r r o r S t r u c t u r e N e e d e d = -2147220801
msdErrorSymbo1 N o t R e s o l v e d = - 2 1 4 7 2 1 9 7 0 4
m s d E r r o r S y n t a x E r r o r = -2147220790
m s d E r r o r S y s t e m E r r o r = -2147218363
m s d E r r o r T a g B a d A s s o c i a t i o n = -2147220096
m s d E r r o r T a g B a d R e p o r t F i l e = -2147220098
m s d E r r o r T a g B a d R e p o r t K e y w o r d = -2147220097
msdErrorTagNameTooLong = - 2 1 4 7 2 2 0 0 9 2
m s d E r r o r T a g N o T a r g e t = -2147220095
m s d E r r o r T a g N o t F o u n d = -2147220093
msdErrorTagNotInSet = -2147220101
m s d E r r o r T a g P r e v i o u s l y D e f i n e d = -2147220094
msdErrorTagSetNameLong = -2147220103
msdErrorTagSetNotFound = -2147220102
m s d E r r o r T a g S e t P r e v i o u s l y D e f i n e d = -2147220100
m s d E r r o r T a g S e t T o o B i g = -2147220099
msdErrorTagUndefinedType = -2147220104
msdErrorTimeout = -2147218362
m s d E r r o r T o o C o m p l e x = -2147220783
m s d E r r o r T o o F e w A r g u m e n t s = -2147220773
msdErrorTooFewPoles = -2147217998
msdErrorTooManyArguments = - 2 1 4 7 2 2 0 7 7 4
msdErrorTooManyKnots = -2147217988
msdErrorTooManyOpenFiles = -2147218307
m s d E r r o r T o o M a n y P o l e s = -2147217997
msdErrorTooManySurfaceElements = -2147218346
msdErrorTypesIncompatible = - 2 1 4 7 2 2 0 7 8 8
msdErrorUnboundedSolution = -2147220751
m s d E r r o r U n k n o w n E r r o r = -2147218305
m s d E r r o r U n k n o w n F o r m a t = -2147218295
msdErrorUnsupported = -2147220792
I The Enumeration List I 271
msdErrorUserCanceledAction = -2147218382
msdErrorV7CellLibrary = -2147218327
msdErrorViewGroupNotFound = -2147220741
msdErrorViewNotDisplayed = -2147218374
msdErrorViewNotFound = -2147220743
msdErrorWriteFailed = -2147218393
msdErrorWriteInhibited = -2147218394
msdErrorWrongElementID = -2147218319
MsdFileAccessMode
msdFileAccessModeRead = 1
msdFileAccessModeReadWrite = 3
MsdFillMode
msdFi 1 1 ModeFi 1 1 ed = 1
msdFi 1 1 ModeNotFi 1 1 ed = 0
msdFi 1 1 ModeOutl i ned = 2
msdFillModeUseActive = - 1
MsdFontType
msdFontTypeMicroStation = 0
msdFontTypeSHX = 1
msdFontTypeUnknown = 3
msdFontTypeWindowsTrueType = 2
MsdGeoReferenceSisterFileType
msdGeoReferenceSisterFileTypeHgr = 1
msdGeoReferenceSisterFileTypeNone = 0
msdGeoReferenceSisterFileTypeTwf = 2
MsdGlobalLineStyleScale
msdGl obal Li neStyl eScal eBoth = 3
msdGl obal Li neStyl eScal eMaster = 0
msdGl obal Li neStyl eScal eNone = 1
msdGl obal Li neStyl eScal eReference = 2
MsdLevelChangeType
msdLevelChangeAfterChangeActive = 9
272 I Chapter 12: The Microstation Object Model - Enums I
msdLevelChangeAfterCreate = 2
msdLevel ChangeAfterDel ete = 3
msdLevelChangeBeforeChangeActive = 17
msdLevel ChangeBeforeDel ete = 18
msdLevelChangeChangeAttribute = 8
msdLevelChangeChangeCode = 5
msdLevel ChangeChangeDi spl ay = 7
msdLevelChangeChangeName = 4
msdLevelChangeChangeParent = 6
msdLevel ChangeTabl eRedo = 15
msdLevel ChangeTabl eUndo = 14
MsdLevelElementAccess
msdLevel El ementAccessAl1 = 0
msdLevel El ementAccessLocked = 1
msdLevel El ementAccessReadOnly = 2
msdLevel El ementAccessVi ewOnly = 3
MsdLimits
msdLimitsMaxVertices = 5000
msdLimi tsMaxVi ews = 8
MsdMeasurementBase
msdMeasurementBaseDegree = 2
msdMeasurementBaseMeter = 1
msdMeasurementBaseNone = 0
MsdMeasurementSystem
msdMeasurementSystemEnglish = 1
msdMeasurementSystemMetric = 2
msdMeasurementSystemUndefined = 0
MsdMem berTraverseType
msdMemberTraverseCopy = 2
msdMemberTraverseDirectMembers = 4
msdMemberTraverseEnumerate = 3
msdMemberTraverseManipulate = 1
msdMemberTraverseSimple = 0
I The Enumeration List I 273
MsdMessageCenterPriority
m s d M e s s a g e C e n t e r P r i o r i t y D e b u g = 13
m s d M e s s a g e C e n t e r P r i o r i t y E r r o r = 10
m s d M e s s a g e C e n t e r P r i o r i t y I n f o = 12
m s d M e s s a g e C e n t e r P r i o r i t y N o n e = 14
m s d M e s s a g e C e n t e r P r i o r i t y W a r n i n g = 11
MsdModelChangeType
mdl Model C h a n g e A c t i ve = 5
mdlModel C h a n g e B e f o r e A c t i v e = 11
mdlModelChangeBeforeCreate = 15
mdl Model ChangeBeforeDel e t e = 6
mdlModelChangeBeforeName = 12
mdlModelChangeBeforeProperties = 14
mdlModel C h a n g e B e f o r e S e t t i n g s = 13
mdlModelChangeBeforeUnCreate = 9
mdlModelChangeBeforeUnDelete = 16
mdlModelChangeCreate = 1
mdl Model ChangeDel e t e = 2
mdlModelChangeName = 10
mdlModelChangePropagateAnnotationScale = 17
mdl Model C h a n g e p r o p e r t i es = 3
mdlModelChangeSettings = 4
mdlModelChangeUnCreate = 7
mdl Model ChangeUnDel e t e = 8
MsdModelType
msdModel T y p e D e f a u l t = - 1
msdModelTypeExtraction = 2
msdModel TypeNorma1 = 0
msdModel T y p e S h e e t = 1
MsdNestoverrides
msdNestOverridesAlways = 1
msdNestOverridesAsRequired = 0
msdNestOverridesNever = 2
274 I Chapter 12: The Microstation Object Model - Enums I
MsdNewLevelDisplay
msdNewLeve1 D i s p l a y A l ways = 1
msdNewLeve1 D i s p l a y F r o m C o n f i g = 0
msdNewLeve1 D i s p l a y N e v e r = 2
MsdRasterBlockType
msdRasterB1 ockTypeImage = 4
msdRasterBlockTypeLine = 1
msdRasterBlockTypeStrip = 3
msdRasterB1 o c k T y p e T i 1 e = 2
MsdRasterDisplayOrderCommand
msdRasterDisplayOrderCommandBackward = 3
msdRasterDisplayOrderCommandForward = 2
msdRasterDisplayOrderCommandToBack = 1
msdRasterDisplayOrderCommandToFront = 0
MsdRasterDisplayPriorityPlane
m s d R a s t e r D i s p l a y P r i o r i t y P l aneBack = 1
msdRasterDi s p l a y P r i o r i tyPl aneFront = 3
msdRasterDi s p l a y P r i o r i tyPl aneVector = 2
MsdRasterModificationType
m s d R a s t e r M o d i f i c a t i onTypecC1 ip B o u n d a r y = 5
m s d R a s t e r M o d i f i c a t i onTypecC1 ipMask = 4
msdRasterModificationType-ExtendedInformation = 0
msdRasterModificationType-GeoReferenceInformation = 1
msdRasterModificationType-RasterInformation = 3
m s d R a s t e r M o d i f i c a t i onType-Re1 oad = 6
m s d R a s t e r M o d i f i c a t i onType-Renderi n g I n f o r m a t i on = 2
MsdRasterworldFile
msdRasterWor1 d F i 1 eHgr = 1
msdRasterWor1 d F i 1 eNone = 0
msdRasterWor1 d F i 1 eWorl d F i 1 e = 2
MsdReferencesystem
msdReferenceSystemDgn = 2
I The Enumeration List I 275
msdReferenceSystemRaster = 1
MsdRenderingMode
msdRenderingModeConstantShade = 5
msdRenderingModeCrossSection = 1
msdRenderingModeHiddenLine = 3
msdRenderingModeParticleTrace = 11
m s d R e n d e r i ngModePhong = 7
m s d R e n d e r i n g M o d e R a d i o s i t y = 10
m s d R e n d e r i ngModeRayTrace = 8
msdRenderingModeRenderWireFrame = 9
msdRenderingModeSmoothShade = 6
m s d R e n d e r i ngModeSol i d F i 11 = 4
msdRenderingModeWireFrame = 0
m s d R e n d e r i ngModeWi reMesh = 2
MsdStandardsCheckerReplaceChoice
msdStandardsCheckerReplaceChoiceAbort = 4
msdStandardsCheckerReplaceChoiceFix = 1
msdStandardsCheckerReplaceChoiceMarkIgnored = 2
msdStandardsCheckerReplaceChoiceMarkNotIgnored = 3
msdStandardsCheckerReplaceChoiceSkip = 0
MsdStandardsCheckerReplaceoptions
msdStandardsCheckerReplaceOptionCanFix = 2
msdStandardsCheckerReplaceOptionCanIgnore = 1
MsdStatusBarArea
msdStatusBarAreaLeft = 16
msdStatusBarAreaMiddle = 15
MsdTagType
msdTagTypeBi n a r y = 5
msdTagTypeCharacter = 1
msdTagTypeDoub1 e = 4
msdTagTypeLongInteger = 3
msdTagTypeShortInteger = 2
276 I Chapter 12: The Microstation Object Model - Enums I
MsdTangentElementOutputType
msdTangentArcs = 1
msdTangentCi r c l es = 0
msdTangentTri angl es = 2
MsdTangentlnterpolationType
msdTangentFromCi r c l e F i t = 1
msdTangentFromCubicFit = 2
msdTangentFromCurve = 0
MsdTextDirection
msdTextDi r e c t i onHori z o n t a l = 0
msdTextDi r e c t i onRi g h t T o L e f t = 8
msdTextDi r e c t i o n V e r t i c a l = 4
m s d T e x t D i r e c t i o n V e r t i c a l Mu1 t i L i n e R i g h t T o L e f t = 2
MsdTextJustification
msdTextJustificationCenterBottom = 8
msdTextJustificationCenterCenter = 7
msdTextJustificationCenterTop = 6
msdTextJustificationLeftBottom = 2
msdTextJustificationLeftCenter = 1
m s d T e x t J u s t i f ic a t i onLeftTop = 0
msdTextJustificationRightBottom = 14
msdTextJustificationRightCenter = 13
msdTextJustificationRightTop = 12
MsdTextNodeLineSpacingType
msdTextNodeLineSpacingTypeAtLeast = 3
msdTextNodeLineSpacingTypeAutomatic = 1
msdTextNodeLineSpacingTypeExact = 0
msdTextNodeLineSpacingTypeExactFromLineTop = 2
MsdV7Action
msdV7ActionAskUser = 0
msdV7ActionUpgradeToV8 = 1
msdV7ActionWorkmode = 3
I Review I 277
MsdViews
msdViewl = 1
msdView2 = 2
msdView3 = 4
msdView4 = 8
msdView5 = 16
msdView6 = 32
msdView7 = 64
msdView8 = 128
msdViewAll = 255
msdViewNone = 0
MsdXDatumType
msdXDatumTypeBinaryData = 1004
m s d X D a t u m T y p e C o n t r o l S t r i n g = 1002
msdXDatumTypeDatabaseHandle = 1005
msdXDatumTypeDistance = 1041
msdXDatumTypeIntl6 = 1070
msdXDatumTypeInt32 = 1071
msdXDatumTypeLeve1 = 1003
msdXDatumTypePoint = 1010
msdXDatumTypeRea1 = 1040
m s d X D a t u m T y p e S c a l e F a c t o r = 1042
msdXDatumTypeString = 1000
msdXDatumTypeUnsupported = 0
m s d X D a t u m T y p e W o r l d D i r e c t i o n = 1013
msdXDatumTypeWorldSpaceDisplacement = 1012
msdXDatumTypeWorldSpacePosition = 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.
13 The Microstation Object
Model - Types
Type P o i n t 3 d
X As D o u b l e
Y As D o u b l e
Z As D o u b l e
End T y p e
Sub T e s t P o i n t 3 d ( 1
D i m S t a r t p o i n t As P o i n t 3 d
D i m E n d p o i n t As P o i n t 3 d
D i m MyLine As LineElement
StartP0int.X = 1.5
StartP0int.Y = 2.5
279
280 I Chapter 13: The Microstation Object Model - Types I
StartP0int.Z = 3.5
EndP0int.X = 4
EndP0int.Y = 0
EndP0int.Z = 0
= CreateLineElement2(Nothing, S t a r t p o i n t , E n d p o i n t )
S e t MyLine
ActiveModelReference.AddElement M y L i n e
End Sub
S u b C r e a t e L i n e E l e m e n t 2 ( T e m p l a t e As E l e m e n t , S t a r t p o i n t As P o i n t 3 d ,
E n d p o i n t As P o i n t 3 d ) as L i n e E l e m e n t
Notice how this method is asking for two Point3d Types - one for the
Start Point and the other for the End Point.
Here is a list of the types we have available to us in Microstation VBA:
T y p e MsdACSType
J u s t i f i c a t i o n As M s d D a t a E n t r y R e g i o n J u s t i f i c a t i on
L e n g t h As L o n g
S t a r t P o s i t i o n As L o n g
End T y p e
T y p e MsdACSType
H i g h As L o n g
Low As L o n g
End T y p e
Type MsdAddAttachmentFlags
C e n t e r As P o i n t 3 d
S t a r t As D o u b l e
Sweep As D o u b l e
V e c t o r 0 As P o i n t 3 d
V e c t o r 9 0 As P o i n t 3 d
End T y p e
Type MsdAngleAccuracy
Du As P o i n t 3 d
Dv As P o i n t 3 d
End T y p e
I The Microstation Object Model -Types I 281
Type MsdAngleAccuracy
RowX As P o i n t 3 d
RowY As P o i n t 3 d
RowZ As P o i n t 3 d
End T y p e
Type MsdAngleAccuracy
B a s e As M s d M e a s u r e m e n t B a s e
L a b e l As S t r i n g
S y s t e m As M s d M e a s u r e m e n t S y s t e m
U n i t s P e r B a s e D e n o m i n a t o r As D o u b l e
U n i t s P e r B a s e N u m e r a t o r As D o u b l e
End T y p e
T y p e MsdAngl e F o r m a t
D e t a i l s As S t r i n g
Msg As S t r i n g
P r i o r i t y As M s d M e s s a g e C e n t e r P r i o r i t y
End T y p e
T y p e MsdAngl eMode
N o r m a l As P o i n t 3 d
O r i g i n As P o i n t 3 d
End T y p e
Type MsdAttachMode
X As D o u b l e
Y As D o u b l e
End T y p e
Type MsdBsplineCurveOffsetCuspType
X As D o u b l e
Y As D o u b l e
Z As D o u b l e
End T y p e
Type MsdBsplineCurveOffsetCuspType
H i g h As P o i n t 3 d
Low As P o i n t 3 d
End T y p e
282 I Chapter 13: The Microstation Object Model - Types I
Type MsdBsplineCurveType
D i r e c t i on As P o i n t 3 d
O r i g i n As P o i n t 3 d
End T y p e
Type MsdBsplineCurveType
Duu A s P o i n t 3 d
Duv A s P o i n t 3 d
Dvu A s P o i n t 3 d
Dvv A s P o i n t 3 d
End T y p e
Type MsdBsplineCurveType
E n d p o i n t As P o i n t 3 d
S t a r t P o i n t As P o i n t 3 d
End T y p e
Type M s d B s p l i n e P a r a m e t r i z a t i o n T y p e
RowX A s P o i n t 3 d
RowY A s P o i n t 3 d
RowZ A s P o i n t 3 d
T r a n s l a t i o n X As D o u b l e
T r a n s l a t i onY As D o u b l e
T r a n s l a t i o n Z As D o u b l e
End T y p e
Type MsdBsplineSurfaceType
X As Double
Y As Double
Z As Double
End T y p e
Type MsdBsplineSurfaceType
T y p e A s MsdXDatumType
Value As V a r i a n t
End T y p e
Type P o i n t 4 d
X As D o u b l e
Y As D o u b l e
Z As D o u b l e
A As D o u b l e
End Type
Dim MyPoint A s P o i n t 4 d
285
286 I Chapter 14: The Microstation Object Model - Events I
events are triggered as a worksheets 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: OnDesi gnFi 1 eOpened and OnDesi gnFi 1 eC1 osed. 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.
0NDESIGN
FILEOPENED AND 0NDESIGNFILECLOSED
Here is a small example of how the OnDesignFileOpened and
OnDesignFi 1 eC1 osed 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.
Heres the program as it is running after a couple of files have been
opened (the previous file closes when the new file is opened).
....C:\M/cr.ostation
..... VBAZ\filel .dgn
C:\Microstation VBAZ\Mathcad Model.dgn
Sub ShowEventsO
frmEvents.Show vbModeless
End Sub
The Procedure ShowEvents is placed inside a code module.
We can use the OnDesignFi 1 eOpened and OnDesignFi 1 eC1 osed 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 Appl ication .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
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.
F u n c t i o n CreateLineElementl(Temp1ate As E l e m e n t ,
V e r t i c e s 0 A s P o i n t 3 d ) As L i n e E l e m e n t
F u n c t i o n CreateLineElement2(Template As E l e m e n t ,
S t a r t p o i n t As P o i n t 3 d , E n d p o i n t As P o i n t 3 d ) As
L i neEl ement
Sub T e s t C r e a t e L i n e A ( )
D i m S t P t As P o i n t 3 d
D i m EnPt As P o i n t 3 d
D i m m y L i n e As L i n e E l e m e n t
EnPt.X = 4 : EnPt.Y = 6 : EnPt.Z = 8
Set myLine = CreateLineElementZ(Nothing, S t P t , E n P t )
ActiveModel Reference.AddElement myLine
End Sub
Sub T e s t C r e a t e L i n e B ( )
D i m S t P t As P o i n t 3 d
D i m EnPt As P o i n t 3 d
D i m m y L i n e As L i n e E l e m e n t
'Line 1
StPt.X = 0: S t P t . Y = 0: S t P t . Z = 0
EnPt.X = 4 : EnPt.Y = 0 : EnPt.Z = 0
Set myLine = CreateLineElementZ(Nothing, S t P t , E n P t )
ActiveModel Reference.AddElement myLine
'Line 2
StPt.X = 4: S t P t . Y = 0: S t P t . Z = 0
EnPt.X = 4 : EnPt.Y = 4 : EnPt.Z = 0
Set myLine = CreateLineElementZ(Nothing, S t P t , E n P t )
ActiveModel Reference.AddElement myLine
'Line 3
StPt.X = 4: S t P t . Y = 4: S t P t . Z = 0
EnPt.X = 0 : EnPt.Y = 4 : EnPt.Z = 0
Set myLine = CreateLineElementZ(Nothing, S t P t , E n P t )
A c t i v e M o d e l R e f e r e n c e . A d d E l e m e n t myLi ne
'Line 4
StPt.X = 0: S t P t . Y = 4: S t P t . Z = 0
EnPt.X = 0 : EnPt.Y = 0 : EnPt.Z = 0
I Graphical Elements I 291
Sub TestCreateLineC( 1
D i m L i n e P o i n t s ( 0 To 4 ) As P o i n t 3 d
D i m m y L i n e As L i n e E l e m e n t
LinePoints(O).X = 0: LinePoints(O).Y = 0
LinePoints(l).X = 4: L i n e P o i n t s ( l ) . Y = 0
LinePoints(Z).X = 4: L i n e P o i n t s ( Z ) . Y = 4
LinePoints(3).X = 0: L i n e P o i n t s ( 3 ) . Y = 4
LinePoints(4).X = 0: L i n e P o i n t s ( 4 ) . Y = 0
S e t myLi ne = C r e a t e L i neEl e m e n t l ( N o t h i ng, L i nePoi n t s )
ActiveModel Reference.AddElement myLine
End Sub
Sub Create3dLines(ParamArray P o i n t E l e m s O As V a r i a n t )
I f (UBound(PointE1ems) + 1) Mod 3 <> 0 Then
MsgBox " I n v a l i d number o f p o i n t e l e m e n t s . " , vbcritical
E x i t Sub
I Chapter 15: Adding To Documents I
End I f
I f UBound(PointE1ems) + 1 < 5 Then
MsgBox A minimum o f 2 X , Y , Z p o i n t s m u s t b e p r o v i d e d . , v b c r i t i c a l
E x i t Sub
End I f
D i m L i n e P o i n t s O As P o i n t 3 d
ReDim L i n e P o i n t s ( 0 To ( U B o u n d ( P o i n t E 1 e m s ) + 1) \ 3 ) As P o i n t 3 d
D i m I As L o n g
D i m P o i n t c o u n t e r As L o n g
D i m m y L i n e As L i n e E l e m e n t
F o r I = L B o u n d ( P o i n t E 1 e m s ) To U B o u n d ( P o i n t E 1 e m s ) S t e p 3
LinePoints(PointCounter).X = PointElems(1)
LinePoints(PointCounter).Y = PointElems(1 + 1)
LinePoints(PointCounter).Z = PointElems(1 + 2)
Pointcounter = Pointcounter + 1
Next I
Set myLine = CreateLineElementl(Nothing, L i n e P o i n t s )
A c t i v e M o d e l Reference.AddE1 ement m y L i n e
End S u b
Sub TestCreate3dLines(1
C r e a t e 3 d L i n e s 0 , 0 , 0 , 4, 0, 0, 4, 4, 0, 0, 4, 0, 0 , 0 , 0
C r e a t e 3 d L i n e s 0, 0, 0, 4, 4, 0
C r e a t e 3 d L i n e s 0, 4, 0, 4, 0, 0
C r e a t e 3 d L i n e s 0, 4, 0, 4, 0
C r e a t e 3 d L i n e s 0, 4, 0
End S u b
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
In Microstations 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).
Lets draw a couple of lines and change their color to red (255,0,0).
Sub T e s t C r e a t e L i n e D ( )
Dim LinePoints(0 To 11 As Point3d
Dim myLine As LineElement
LinePoints(O1.X = 0: LinePoints(O1.Y = 0
LinePoints(l1.X = 4: LinePoints(l1.Y = 4
Set myLine = CreateLineElementl(Nothing, LinePoints)
myLine.Color = 3
ActiveModel Reference.AddElement myLine
LinePoints(O1.X = 0: LinePoints(O1.Y = 4
LinePoints(l1.X = 4: LinePoints(l1.Y = 0
Set myLine = CreateLineElementl(Nothing, LinePoints)
myLine.Color = 3
ActiveModel Reference.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 T e s t C r e a t e L i n e E ( )
Dim LinePoints(0 To 11 As Point3d
Dim myLine As LineElement
Dim myLine2 As LineElement
LinePoints(O1.X = 0: LinePoints(O1.Y = 0
LinePoints(l1.X = 4: LinePoints(l1.Y = 4
Set myLine = CreateLineElementl(Nothing, LinePoints)
I Graphical Elements I 295
myLine.Color = 3
ActiveModel Reference.AddElement myLine
LinePoints(O).X = 0: LinePoints(O).Y = 4
LinePoints(l).X = 4: LinePoints(l).Y = 0
Set myLine2 = CreateLineElementl(myLine, LinePoints)
ActiveModel Reference.AddElement myLine2
End S u b
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:
Creating Shapes
A shape is a series of lines that are joined together into one element.
Here is the declaration for CreateShapeElementl:
i
l Function CreateShapeElementl(Temp1ate As Element,
V e r t i c e s 0 As Point3d, CFillMode As MsdFillMode =
msdFillModeUseActive1) As ShapeElement
Here is a procedure that creates a triangle.
Sub TestCreateShapeA( )
Dim myshape As ShapeElement
Dim ShapePoints(0 T o 2) As Point3d
ShapePoints(O).X = 0: ShapePoints(O).Y = 0
ShapePoints(l).X = 2: ShapePoints(l).Y = 0
ShapePoints(Z).X = 1: ShapePoints(Z).Y = 1
Set myshape = CreateShapeElementl(Nothing, ShapePoints)
ActiveModel Reference.AddElement myshape
End S u b
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 (O,O, 0). Shapes are always closed.
296 I Chapter 15: Adding To Documents I
A comparison of the declaration and the use of C r e a t e S h a p e E l e r n e n t l
reveals that we did not use the optional FillMode parameter. By default,
the FillMode parameter uses the active setting in Microstation. Lets
copy and paste TestCreateShapeA, rename the new procedure to
T e s t C r e a t e S h a p e B and supply a FillMode parameter:
Sub T e s t C r e a t e S h a p e B O
D i m myshape As S h a p e E l e m e n t
D i m S h a p e P o i n t s ( 0 To 2) As P o i n t 3 d
ShapePoints(O1.X = 0: ShapePoints(O1.Y = 0
ShapePoints(l1.X = 2: S h a p e P o i n t s ( l 1 . Y = 0
ShapePoints(2l.X = 1: S h a p e P o i n t s ( 2 l . Y = 1
S e t myshape = CreateShapeElementl(Nothing, S h a p e P o i n t s , -
m s d F i 11 ModeFi 11 e d )
ActiveModelReference.AddElement myshape
End S u b
F u n c t i o n C r e a t e P o l y g o n ( C e n t e r P o i n t As Poi n t 3 d . -
NumOfSides As L o n g , R a d i u s As D o u b l e ) As S h a p e E l e m e n t
D i m myshape As S h a p e E l e m e n t
D i m S h a p e P o i n t s O As P o i n t 3 d
ReDim S h a p e P o i n t s ( 0 To NumOfSides - 1) As P o i n t 3 d
D i m P o i n t I n d e x As L o n g
D i m I n c A n g l e As D o u b l e
IncAngle = 3 6 0 / NumOfSides
For P o i n t I n d e x = L B o u n d ( S h a p e P o i n t s ) To U B o u n d ( S h a p e P 0 i n t s )
ShapePoints(Point1ndex) = -
Point3dAddAngleDistance(CenterPoint, ~
R a d i a n s ( I n c A n g 1 e * P o i n t I n d e x ) , R a d i u s , 0)
Next
Set CreatePolygon = CreateShapeElementl(Nothing, S h a p e P o i n t s )
End F u n c t i o n
S u b TestCreatePolygon( 1
D i m C P o i n t As P o i n t 3 d
D i m m y s h a p e As S h a p e E l e m e n t
S e t myshape = C r e a t e P o l y g o n ( C P o i n t , 6 , 1)
A c t i v e M o d e l Reference.AddElement myshape
End S u b
Sub T e s t C r e a t e C i r c l eA( )
D i m C P o i n t As P o i n t 3 d
D i m m y E l l i p s e As E l l i p s e E l e m e n t
D i m r o t M a t r i x As M a t r i x 3 d
CP0int.X = 2.5: C P 0 i n t . Y = 2.5
S e t myEllipse = CreateEllipseElementZ(Nothing, CPoint, 0 . 5 , 0 . 5 ,
rotMatrix)
A c t i v e M o d e l R e f e r e n c e . A d d E l e m e n t myEl1 i p s e
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 T e s t C r e a t e C i r c l eB( )
D i m C P o i n t As P o i n t 3 d
D i m m y E l l i p s e As E l l i p s e E l e m e n t
D i m r o t M a t r i x As M a t r i x 3 d
D i m C i r R a d As D o u b l e
CP0int.X =2.5: C P 0 i n t . Y = 2.5
F o r CirRad = 0.5 To 2 S t e p 0.125
S e t m y E l l i p s e = CreateEllipseElementZ(Nothing, C P o i n t , -
CirRad, CirRad, r o t l v l a t r i x )
A c t i v e M o d e l R e f e r e n c e . A d d E l e m e n t myEl1 i p s e
N e x t C i rRad
End Sub
Sub T e s t C r e a t e C i r c l eC( )
D i m C P o i n t As P o i n t 3 d
D i m m y E l l i p s e As E l l i p s e E l e m e n t
D i m r o t M a t r i x As M a t r i x 3 d
D i m i n p u t Q u e u e As CadInputQueue
D i m i n p u t M e s s a g e As CadInputMessage
Set inputQueue = CadInputQueue
I Graphical Elements I 299
Set inputMessage = -
inputQueue.GetInput(msdCadInputTypeDataPoint, ~
msdCadInputTypeAny)
Do
S e l e c t Case i n p u t M e s s a g e . I n p u t T y p e
Case m s d C a d I n p u t T y p e D a t a P o i n t
CPoint = inputMessage.point
S e t m y E l l i p s e = CreateEllipseElement2(Nothing, -
CPoint, 0.5, 0.5, r o t M a t r i x )
ActiveModelReference.AddElement m y E l l i p s e
E x i t Do
Case m s d C a d I n p u t T y p e R e s e t
E x i t Do
End S e l e c t
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.
inputQueue.GetInput(msdCad1nputTypeDataPoint. -
msdCadInputTypeAny)
Do
S e l e c t Case i n p u t M e s s a g e . 1 n p u t T y p e
Case m s d C a d I n p u t T y p e D a t a P o i n t
StPoint = inputMessage.point
E x i t Do
Case m s d C a d I n p u t T y p e R e s e t
E x i t Sub
End S e l e c t
Loop
I Chapter 15: Adding To Documents I
Set inputMessage = inputQueue.GetInput(rnsdCadInputTypeDataPoint, ~
msdCadInputTypeAny)
Do
S e l e c t Case i n p u t M e s s a g e . I n p u t T y p e
Case msdCadInputTypeDataPoint
EnPoint = inputMessage.point
E x i t Do
Case m s d C a d I n p u t T y p e R e s e t
E x i t Sub
End S e l e c t
Loop
CP0int.X = StP0int.X + (EnP0int.X - StP0int.X) / 2
CP0int.Y = StP0int.Y + (EnP0int.Y - StP0int.Y) / 2
CP0int.Z = StP0int.Z + (EnP0int.Z - StP0int.Z) / 2
C i r R a d = Point3dDistance(StPoint, E n P o i n t ) / 2
S e t m y E l l i p s e = CreateEllipseElementZ(Nothing, CPoint, -
CirRad, CirRad, r o t M a t r i x )
A c t i v e M o d e l Reference.AddElement myEl1i p s e
End Sub
We calculate the center point of the circle by using the selected points.
We also use the Microstation VBA P o i n t 3 d D i s t a n c e 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. Lets
look at three examples of creating ellipses.
D i m M i n o r A l As P o i n t 3 d
D i m m y E l l i p s e As E l l i p s e E l e m e n t
MajorA1.X = 1: M a j o r A 1 . Y = 1
MajorA2.X = 5: MajorA2.Y = 5
MinorA1.X = 3: MinorA1.Y = 2
Set myEllipse = CreateEllipseElementl(Nothing, M a j o r A l , MajorAP, -
M inorAl)
ActiveModel Reference.AddElement myEl1 i p s e
End S u b
S u b TestCreateEl 1 ipseC()
D i m C P o i n t As P o i n t 3 d
D i m m y E l l i p s e As E l l i p s e E l e m e n t
D i m r o t M a t r i x As M a t r i x 3 d
CP0int.X = 2.5: CP0int.Y = 2.5
rotMatrix.RowX.X = 2
rotMatrix.RowY.X = 4: r0tMatrix.RowY.Y = 5
S e t m y E l l i p s e = CreateEllipseElementZ(Nothing, C P o i n t , 1, 0 . 5 ,
rotMatrix)
A c t i v e M o d e l Reference.AddElement myEl1 i p s e
End S u b
After running the above procedures, what do we find? Two of the three
procedures shown above create ellipses. However, the procedure
TestCreateEllipseB createdacircle. Themethod CreateEllipseElementl
always creates a circle through the three points provided.
Creating Arcs
We have five different ways we can create arcs in Microstation VBA.
Function CreateArcElement 1(Template As Element, StartPoint As
Point3d, Centerpoint As Point3d, Endpoint As Point3d) As
ArcElement
Function CreateArcElement2(TemplateAs Element, Centerpoint As
Point3d, PrimaryRadius As Double, SecondaryRadius As Double,
Rotation As Matrix3d, StartAngle As Double, SweepAngle As
Double) As ArcElement
Function CreateArcElement3(Template As Element, StartPoint As
Point3d, PointOnCurve As Point3d, Endpoint As Point3d) As
ArcElement
I Chapter 15: Adding To Documents I
4 Function CreateArcElement4(TemplateAs Element, StartTangent
As Ray3d, Endpoint As Point3d) As ArcElement
5 Function CreateArcElement5(TemplateAs Element, Chord As
Segment3d, ArcLength As Double, PlanePoint As Point3d) As
ArcElement
Lets look at a few ways to use these methods.
Sub T e s t C r e a t e A r c A (
D i m C P o i n t As P o i n t 3 d
D i m S t P o i n t As P o i n t 3 d
D i m E n P o i n t As P o i n t 3 d
D i m m y A r c As A r c E l e m e n t
CP0int.X = 1: C P 0 i n t . Y = 1
StP0int.X = 4: S t P 0 i n t . Y = 1
EnP0int.X = 1: E n P 0 i n t . Y = 4
S e t myArc = CreateArcElementl(Nothing, S t P o i n t , C P o i n t , E n P o i n t )
ActiveModelReference.AddElement m y A r c
End Sub
Sub T e s t C r e a t e A r c B (
D i m C P o i n t As P o i n t 3 d
D i m r o t M a t r i x As M a t r i x 3 d
D i m m y A r c As A r c E l e m e n t
CP0int.X = 1: C P 0 i n t . Y = 1
S e t m y A r c = CreateArcElementZ(Nothing, CPoint, 0.5, 0.5, -
r o t M a t r i x , 0, P i )
ActiveModelReference.AddElement m y A r c
End Sub
Sub T e s t C r e a t e A r c C o
D i m P o i n t A As P o i n t 3 d
D i m P o i n t B As P o i n t 3 d
D i m P o i n t C As P o i n t 3 d
D i m m y A r c As A r c E l e m e n t
P0intA.X = 1: P 0 i n t A . Y = 1
P0intB.X = 2: P 0 i n t B . Y = 2
P0intC.X = 1: P 0 i n t C . Y = 3
= CreateArcElement3(Nothing,
S e t myArc PointA, PointB, PointC)
ActiveModelReference.AddElement m y A r c
End Sub
I Graphical Elements I 303
Sub T e s t C r e a t e A r c D ( )
D i m m y A r c As A r c E l e m e n t
D i m myRay As Ray3d
D i m E n d p o i n t As P o i n t 3 d
myRay.0rigin.X = 1
myRay.0rigin.Y = 1
myRay.Directi0n.X = 1
myRay. D i r e c t i o n . Y = 4
EndP0int.X = 0: EndP0int.Y = 2
S e t myArc = C r e a t e A r c E l e m e n t 4 ( N o t h i n g , myRay, E n d p o i n t )
A c t i v e M o d e l Reference.AddElement myArc
End Sub
Sub T e s t C r e a t e A r c E ( )
D i m m y A r c As A r c E l e m e n t
D i m mySeg As Segment3d
D i m m y p o i n t As P o i n t 3 d
mySeg.startP0int.X = 1: m y S e g . s t a r t P 0 i n t . Y = 1
mySeg.EndP0int.X = 4: m y S e g . E n d P 0 i n t . Y = 4
myP0int.X = 3.5: myP0int.Y = 3: myP0int.Z = 0
S e t myArc = CreateArcElement5(Nothing, mySeg, 8 . 5 , myPoint)
A c t i v e M o d e l Reference.AddElement myArc
End Sub
Creating Text
Text is easy to create by using the CreateTextElementl method.
I4 F u n c t i o n CreateTextElementl(Temp1ate As E l e m e n t ,
T e x t As S t r i n g , O r i g i n A s P o i n t 3 d , R o t a t i o n A s
M a t r i x 3 d ) As TextElement
Here is an example of creating nine text elements spaced 0.5 units away
from each other.
Sub T e s t C r e a t e T e x t A ( 1
D i m m y T e x t As T e x t E l e m e n t
D i m T e x t P t As P o i n t 3 d
D i m r o t M a t r i x As M a t r i x 3 d
D i m I As D o u b l e
For I = 1 To 9
TextPt.Y = TextPt.Y - 0.5
304 I Chapter 15: Adding To Documents I
S e t myText = CreateTextElementl(Nothing, " N o t e " & I & -
":", TextPt, rotMatrix)
ActiveModelReference.AddElement m y T e x t
Next I
End S u b
I
j
.+. _.
j . .
I
I !
INote 5:
I
i .
I
.
. . . . . . . . .
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 CreateCellElement1(Name As String, Elements() As
-Element, Origin As Point3d, [IsPointCellAs Boolean]) As
CellElement
2 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.
S u b TeStCreateCell A ( )
D i m m y C e l l As C e l l E l e m e n t
D i m BoxLines(0 To 3 ) As E l e m e n t
D i m O r i g i n p o i n t As P o i n t 3 d
Set BoxLines(0) = CreateLineElementZ(Nothing, ~
P o i n t 3 d F r o m X Y Z ( O , 4 , O ) , P o i n t 3 d F r o m X Y Z ( O , 0 , 0))
0riginPoint.X = 2: 0 r i g i n P o i n t . Y = 2
Set myCell = CreateCellElementl("Box", B o x L i n e s , O r i g i n p o i n t )
A c t i v e M o d e l Reference.AddElement myCel1
m y c e l l . Redraw
End S u b
S u b TestCreateCell B ( 1
D i m m y C e l l As C e l l E l e m e n t
D i m CellElements(0 To 6) As E l e m e n t
D i m O r i g i n p o i n t As P o i n t 3 d
D i m r o t M a t r i x As M a t r i x 3 d
Set CellElements(0) = CreateLineElementZ(Nothing, -
I Chapter 15: Adding To Documents I
Point3dFromXYZ(O, 0, 0). Point3dFromXYZ(4, 0, 0))
Set CellElements(1) = C r e a t e L i n e E l e m e n t Z ( N o t h i n g , -
Point3dFromXYZ(4, 0, 01, Point3dFromXYZ(4, 4, 0))
Set CellElements(2) = C r e a t e L i n e E l e m e n t Z ( N o t h i n g ,
~
Sub TestCreateCell C( )
Dim myCell As CellElement
Dim CellElements(0 To 6 ) As Element
Dim Originpoint As Point3d
Dim rotMatrix As Matrix3d
Set CellElements(0) = C r e a t e L i n e E l e m e n t Z ( N o t h i n g ,~
CREATINGNEWDOCUMENTS
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
2 Sub CopyDesignFile(ExistingDesignFileNameAs String,
NewDesignFileName As String, [Overwrite As Boolean])
308 I Chapter 15: Adding To Documents I
Here are two methods that create new design files. CreateDesignFile
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 T e s t C r e a t e D e s i g n F i l e A ( )
D i m m y F i l e As D e s i g n F i l e
A p p l i c a t i o n . A c t i v e D e s i g n F i 1 e.C1 o s e
S e t m y F i l e = CreateDesignFile("seedZd", ~
" C : \ M i c r o S t a t i o n V B A \ f i 1e a . d g n " , T r u e )
End Sub
Sub T e s t C r e a t e D e s i g n F i l e B O
D i m m y F i l e As D e s i g n F i l e
D i m m y F i l e N a m e As S t r i n g
myFileName = "C:\MicroStation VBA\filea.dgn"
I f Dir(myFileName1 = " " Then
Set myFile = CreateDesignFile("seed3d", m y F i l e N a m e , T r u e )
Else
MsgBox " T h e f i l e " & myFileName & " already exists.", ~
vbCri tical
End I f
End S u b
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 T e s t C r e a t e D e s i g n F i l e C O
D i m m y F i l e As D e s i g n F i l e
D i m I As Long
F o r I = 1 To 1 0
Set myFile = CreateDesignFile("seed2d","C:\MicroStation
VBA\file" & I & ".dgn", False)
Next I
End Sub
I Security Issues with Creating Data I 309
Our design files range in complexity from one or two elements to many
thousands. The number of elements can vary as well as the element
types (lines, circles, arcs, text) and colors. Levels, line styles and classes
can differ from element to element. Line weights and transparency can
also vary. As we begin searching in our files, we will learn how to
discover these properties we find in our files.
In this Chapter:
The Basics of Searching Files
Using Scancriteria
Multiple Combinations of Criteria
Reviewing Three Collection Methods
Scan Criteria Methods
S u b TestScanAl 1A ( )
Dim myElement A s Element
Dim myEnum A s ElementEnumerator
311
312 I Chapter 16: Searching In Files I
S e t myEnum = ActiveModelReference.Scan0
W h i l e myEnum.MoveNext
S e t myElement = myEnum.Current
D e b u g . P r i n t myElement.Type
Wend
End Sub
msdXDatumTypeWorldSpacePosition
msdEl ementType44 = 44
msdEl ementTypeArc = 16
msdElementTypeBsplineBoundary = 25
msdElementTypeBsplineCurve = 27
msdEl ementTypeBsp1 ineKnot = 26
msdEl ementTypeBsp1 inePol e = 21
msdElementTypeBsplineSurface = 2 4
msdElementTypeBsplineWeight = 28
msdEl ementTypeCel1 Header = 2
msdEl ementTypeCel1 Li braryHeader = 1
msdEl ementTypeComp1 exshape = 1 4
msdEl ementTypeComp1 exstring = 1 2
msdEl ementTypeCone = 23
msdElementTypeConic = 13
msdElementTypeCurve = 11
msdElementTypeDesignFileHeader = 9
msdElementTypeDgnStoreComponent = 38
msdElementTypeDgnStoreHeader = 39
msdElementTypeDigSetData = 8
I The Basics of Searching Files I 313
msdElementTypeDimension = 33
msdEl ementTypeEl1 ipse = 15
msdElementTypeGroupData = 5
msdEl ementTypeLeve1 Mas k = 99
msdEl ementTypeLeve1 Symbol ogy = 10
msdEl ementTypeLine = 3
msdElementTypeLineString = 4
msdEl ementTypeMatrixDoub1 eData = 103
msdElementTypeMatrixHeader = 101
msd El emen tTypeMa t r i x I n teger Da ta = 102
m s d E l e m e n t T y p e M e s h H e a d e r = 105
msdElementTypeMicroStation = 66
msdEl ementTypeMu1 ti Line = 36
msdElementTypeNamedGroupComponent = 111
msdElementTypeNamedGroupHeader = 110
msdEl ementTypePointString = 22
msdElementTypeRasterComponent = 88
msdElementTypeRasterFrame = 94
msdElementTypeRasterHeader = 87
m s d E l e m e n t T y p e R a s t e r R e f e r e n c e = 90
msdElementTypeRasterReferenceComponent = 91
m s d E l e m e n t T y p e R e f e r e n c e A t t a c h m e n t = 100
msdElementTypeReferenceOverride = 108
msdElementTypeShape = 6
msdEl ementTypeSharedCel1 = 35
msdElementTypeSharedCel1 Definition = 34
msdEl ementTypeSo1 id = 19
msdEl ementTypeSurface = 18
msdEl ementTypeTab1 e = 96
msdEl ementTypeTab1 eEntry = 95
msdElementTypeTag = 37
msdElementTypeText = 17
msdElementTypeTextNode = 7
msdEl ementTypeView = 98
msdEl ementTypeViewGroup = 97
A review of the Immediate window, shown previously, shows the first
three unique element types are 9, 96, and 97. Referring to the list above
tells us the first three element types found were:
314 I Chapter 16: Searching In Files I
msdElementTypeDesignFileHeader = 9
msdEl ementTypeTab1 e = 96
msdEl ementTypeViewGroup = 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? 6 6 , 6 , and 4.
Sub TestScanAll B ( 1
D i m m y E l e m e n t As E l e m e n t
D i m myEnum As E l e m e n t E n u m e r a t o r
S e t myEnum = ActiveModelReference.Scan0
W h i l e myEnum.MoveNext
S e t myElement = myEnum.Current
S e l e c t Case m y E l e m e n t . T y p e
Case m s d E l e m e n t T y p e A r c
D i m m y A r c As A r c E l e m e n t
S e t myArc = myElement
Case m s d E l e m e n t T y p e C u r v e
D i m m y c u r v e As C u r v e E l e m e n t
S e t mycurve = myEl e m e n t
Case msdEl e m e n t T y p e L i n e
D i m m y L i n e As L i n e E l e m e n t
Set myLine = myElement
Case m s d E l e m e n t T y p e T e x t
D i m m y T e x t As T e x t E l e m e n t
S e t myText = myEl e m e n t
Case E l s e
D e b u g . P r i n t myElement.Type
End S e l e c t
Wend
End Sub
We can make the use of a Sel ect ... Case statement to allow us to
perform actions based on the Element.Type property. As we cycle
I The Basics of Searching Files I 315
Subiype SetXData
Transform Subiype
Type Transform
URL Type
URLTitie URL
Vertex URLTitie
S u b TestScanAl 1 C ( )
D i m myElement As Element
D i m myEnum A s E l e m e n t E n u m e r a t o r
S e t myEnum = ActiveModelReference.Scan0
W h i l e myEnum.MoveNext
S e t myElement = myEnum.Current
S e l e c t Case m y E l e m e n t . T y p e
Case m s d E l e m e n t T y p e T e x t
D i m myText As T e x t E l e m e n t
S e t myText = myElement
myText.Text = UCase(myText.Text)
End S e l e c t
Wend
End S u b
Now, our procedure is only going to react to text elements. And what are
we doing to the text element? U C a 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. Lets take a look at
the next procedure and see if we can find what is missing.
Sub T e s t S c a n A l l D ( 1
D i m m y E l e m e n t As E l e m e n t
D i m myEnum As E l e m e n t E n u m e r a t o r
S e t myEnum = ActiveModelReference.Scan0
W h i l e myEnum.MoveNext
S e t myElement = myEnum.Current
S e l e c t Case m y E l e m e n t . T y p e
Case m s d E l e m e n t T y p e T e x t
D i m m y T e x t As T e x t E l e m e n t
S e t myText = myEl e m e n t
myText.Text = UCase(myText.Text)
myText. R e w r i t e
End S e l e c t
Wend
End Sub
If we dont 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.
USINGSCANCRITERIA
Now, lets 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. Lets make our code more efficient by working
only with text elements. We accomplish this through the use of an
Elementscancriteria object.
Sub T e s t S c a n F i 1 t e r A ( )
D i m myEnum As E l e m e n t E n u m e r a t o r
D i m myFi 1 t e r As New E l e m e n t S c a n C r i t e r i a
D i m E l e m e n t c o u n t e r As L o n g
myFi 1 t e r . I n c l u d e T y p e msdEl e m e n t T y p e T e x t
myFi 1 t e r . I n c l u d e T y p e msdEl e m e n t T y p e T e x t N o d e
I Using Scancriteria I 317
Set myEnum = A c t i v e M o d e l R e f e r e n c e . S c a n ( m y F i 1 t e r )
While myEnum.MoveNext
Elementcounter = Elementcounter + 1
Wend
MsgBox Elementcounter & elements found."
"
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 TestScanFi 1 t e r B ( 1
Dim myEnum As ElementEnumerator
Dim myFilter As New Elementscancriteria
Dim Elementcounter A s Long
myFilter.ExcludeA11Types
myFil ter.IncludeType msdElementTypeText
myFilter.IncludeType msdElementTypeTextNode
Set myEnum = A c t i v e M o d e l R e f e r e n c e . S c a n ( m y F i 1 t e r )
While myEnum.MoveNext
Elementcounter = Elementcounter + 1
Wend
MsgBox Elementcounter & elements found."
"
End Sub
Now, myEnum only contains Text and TextNode elements.
Let's build on T e s t S c a n F i 1 terB 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 TestScanFi 1 t e r C ( 1
Dim myEnum A s ElementEnumerator
Dim myFilter A s New ElementScanCriteria
Dim Elementcounter As Long
myFilter.ExcludeA11Types
myFi 1 ter. Excl udeAl1 Levels
myFil ter.IncludeType msdElementTypeText
myFilter.IncludeType msdElementTypeTextNode
I Chapter 16: Searching In Files I
myFilter.IncludeLeve1 ActiveDesignFile.Levels("SIDEWALK")
Set myEnum = ActiveModelReference.Scan(myFi1ter)
While myEnum.MoveNext
Elementcounter = Elementcounter + 1
Wend
MsgBox Elementcounter & elements found."
"
End Sub
Let's look over the macro "ScanFilterc': What is being counted here?
Text elements and TextNode elements on Level "SIDEWALK':
Sub T e s t S c a n F i 1 t e r D ( )
Dim myEnum As ElementEnumerator
Dim myFi 1 ter As New El ementScanCri teria
Dim Elementcounter As Long
myFi 1 ter . Excl udeAl1 Types
myFi 1 ter. Excl udeAl1 Levels
myFi 1 ter. Excl udeAl1 Colors
myFi 1 ter. Incl udeType msdEl ementTypeText
myFi 1 ter. Incl udeType msdEl ementTypeTextNode
myFi 1 ter. Incl udeLevel ActiveDesignFi le. Level s("S1DEWALK")
myFi 1 ter. Incl udeCol or 4
Set myEnum = ActiveModelReference.Scan(myFi1ter)
While myEnum.MoveNext
Elementcounter = Elementcounter + 1
Wend
MsgBox Elementcounter & " 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 T e s t S c a n F i 1 t e r F ( )
Dim myEnum As ElementEnumerator
Dim myFi 1 ter As New El ementScanCri teria
Dim Elementcounter As Long
Dim myCol orTabl e As Col orTabl e
Dim mycolor As Long
Set myColorTable = ActiveDesignFile.ExtractColorTable
I Using Scancriteria I 319
Sub T e s t S c a n F i 1 terG( 1
D i m myEnum As E l e m e n t E n u m e r a t o r
D i m m y F i l t e r As New E l e m e n t s c a n c r i t e r i a
D i m E l e m e n t c o u n t e r As L o n g
D i m m y C o l o r T a b l e As C o l o r T a b l e
D i m m y c o l o r As L o n g
Set myColorTable = ActiveDesignFile.ExtractColorTab1e
mycolor myColorTable.FindClosestColor(RGB(l92,
= 192, 192))
myFilter.ExcludeA11Types
myFi 1t e r . E x c l u d e A l 1 L e v e l s
myFilter.ExcludeA11Colors
myFi 1t e r . E x c l u d e A l 1 L i n e S t y l e s
myFilter.ExcludeA11Classes
m y F i l t e r . I n c l u d e T y p e msdElementTypeLineString
rnyFil t e r . I n c l u d e L i n e S t y l e A c t i v e D e s i g n F i l e . L i n e S t y l e s ( " ( Hidden ) " )
m y F i 1 t e r . I n c l u d e L e v e l A c t i v e D e s i g n F i l e . L e v e l s ( "SIDEWALK")
m y F i 1 t e r . I n c l u d e C o l o r myCol o r - 1
myFil t e r . I n c l u d e c l a s s msdElementClassPrimary
S e t myEnum = ActiveModelReference.Scan(myFi1ter)
W h i l e myEnum.MoveNext
Elementcounter = Elementcounter + 1
Wend
MsgBox E l e m e n t c o u n t e r & " elements found."
End Sub
320 I Chapter 16: Searching In Files I
Now we are adding the Classto 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.
Now, lets 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.
S u b T e s t S c a n F i 1t e r H ( )
D i m myEnum As E l e m e n t E n u m e r a t o r
D i m myFi 1 t e r As New E l e m e n t S c a n C r i t e r i a
D i m m y L e v e l As L e v e l
D i m E l e m e n t c o u n t e r As L o n g
myFi 1 t e r . E x c l u d e A l 1 L e v e l s
F o r Each m y L e v e l I n A c t i v e D e s i g n F i l e . L e v e 1 s
S e l e c t Case U C a s e ( m y L e v e l . N a m e )
C ase S ID EW A LK
Case E l s e
m y F i l t e r . I n c l udeLevel myLevel
End S e l e c t
Next
S e t myEnum = ActiveModelReference.Scan(myFi1ter)
W h i l e myEnum.MoveNext
Elementcounter = Elementcounter + 1
Wend
MsgBox E l e m e n t c o u n t e r & elements found.
End S u b
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 Columnsand all
text elements on level Marks?Here are three ways to accomplish the
same task.
S u b TestScanFi 1 t e r J ( 1
D i m myElem As E l e m e n t
D i m myEnum As E l e m e n t E n u m e r a t o r
D i m myEnum2 As E l e m e n t E n u m e r a t o r
D i m m y F i l t e r As New E l e m e n t s c a n c r i t e r i a
D i m m y f i l t e r 2 As New E l e m e n t s c a n c r i t e r i a
D i m E l e m e n t c o u n t e r As Long
myFilter.ExcludeA11Types
myFi 1t e r . E x c l u d e A l 1 L e v e l s
myFil ter.IncludeType msdElementTypeSharedCel1
m y F i 1 t e r . I n c l u d e L e v e l A c t i v e D e s i g n F i 1 e . L e v e l s ( COLUMNS)
S e t myEnum = ActiveModelReference.Scan(myFi1ter)
W h i l e myEnum.MoveNext
Elementcounter = Elementcounter + 1
Wend
myFilter2.ExcludeAllTypes
myFi 1t e r 2 . E x c l u d e A l 1 L e v e l s
m y F i l t e r 2 . I n c l u d e T y p e msdElementTypeText
m y F i 1 t e r 2 . I n c l u d e L e v e l A c t i v e D e s i g n F i 1 e . L e v e l s ( MARKS)
S e t myEnum2 = ActiveModelReference.Scan(myFilter2)
W h i l e myEnum2.MoveNext
Elementcounter = Elementcounter + 1
Wend
MsgBox E l e m e n t c o u n t e r & elements found.
End S u b
We can use 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?
S u b TestScanFi 1 t e r K ( 1
D i m myElem As E l e m e n t
D i m myEnum As E l e m e n t E n u m e r a t o r
322 I Chapter 16: Searching In Files I
D i m myFi 1 t e r As New E l e m e n t S c a n C r i t e r i a
D i m m y C o l l e c t i o n As New C o l l e c t i o n
myFi 1 t e r . E x c l u d e A l 1 T y p e s
myFi 1 t e r . E x c l u d e A l 1 L e v e l s
myFi 1 t e r . I n c l u d e T y p e msdEl e m e n t T y p e S h a r e d C e l 1
myFilter.IncludeLeve1 ActiveDesignFile.Levels("COLUMNS")
S e t myEnum = ActiveModelReference.Scan(myFi1ter)
W h i l e myEnum.MoveNext
S e t myElem = myEnum.Current
my C o 1 1 e c t io n . Add my E 1 em
Wend
myFi 1 t e r . R e s e t
myFi 1 t e r . E x c l u d e A l 1 T y p e s
myFi 1 t e r . E x c l u d e A l 1 L e v e l s
myFi 1 t e r . I n c l u d e T y p e msdEl e m e n t T y p e T e x t
myFilter.IncludeLeve1 ActiveDesignFile.Levels("MARKS")
S e t myEnum = ActiveModelReference.Scan(myFi1ter)
W h i l e myEnum.MoveNext
S e t myElem = myEnum.Current
my C o 1 1 e c t io n . Add my E 1 em
Wend
MsgBox m y C o l 1 e c t i o n . C o u n t & " e l ements f o u n d . "
End S u b
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
Cache ElementCachelElementCac
Cachelndex 18 Long
Class msdEIementCIassPrimary MsdEIementCIass
Color 0 Long
DateLastModified #9L?OL?OO55:26:39PM# Date
FilePosition 4000017 Long
GraphicGroup 0 Long
HasAnyTags False Boolean
Adding a watch to lnDisplay Set True Boolean
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 T e s t S c a n F i 1 t e r M (
Dim myEnum As ElementEnumerator
Dim myFilter As New Elementscancriteria
Dim myGroup As NamedGroupElement
Set myGroup = A c t i v e M o d e l R e f e r e n c e . A d d N e w N a m e d G r o u p ( " G r o u p A " )
myFilter.ExcludeA11Types
myFi 1 ter. Excl udeAl1 Levels
myFil ter.IncludeType msdElementTypeSharedCel1
myFil ter. IncludeLevel ActiveDesignFile Leve s ( CO LU M N S " "
.
REVIEWINGTHREE
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 Scancriteria object and places all
objects found into a single custom VBA collection. Doing so allows us to
use For 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
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 T e s t S c a n F i 1 t e r N ( )
D i m myEnum As E l e m e n t E n u m e r a t o r
D i m m y F i 1 t e r As New E l e m e n t S c a n C r i t e r i a
D i m m y G r o u p As N a m e d G r o u p E l e m e n t
D i m myRange As Range3d
S e t 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.High.Z = 0
myFilter.Include0nlyWithinRange myRange
S e t myEnum = ActiveModelReference.Scan(myFi1ter)
W h i l e myEnum.MoveNext
myGroup.AddMember m y E n u m . C u r r e n t
Wend
myGroup. Rewri t e
MsgBox m y G r o u p . M e m b e r s C o u n t & " elements 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 L0w.Z
and a High.Z value if we are working on 3D files.
I Review I 327
In this Chapter:
Giving users feedback and information
Working with selection sets
Getting user input
Using the send command
Employing modeless dialog boxes
Applying some real-world applications
Interacting with MDL applications
GIVINGUSERS FEEDBACK
AND lNFORMATlON
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.
~~~~~~~ ~F~~~~ ~~~~~~
Sub TestShowCommandO
ShowCommand D r a w a L i n e
ShowPrompt S e l e c t F i r s t P o i n t :
ShowStatus Draw L i n e b y s e l e c t i n g two p o i n t s .
End Sub
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 TestShowTempMessageO
ShowTempMessage r n s d S t a t u s B a r A r e a L e f t , Message L e f t .
ShowTempMessage r n s d S t a t u s B a r A r e a M i d d l e , M e s s a g e M i d d l e .
End Sub
I Giving Users Feedback and Information I 331
Sub TestShowTempMessageCenterO
ShowTempMessage m s d S t a t u s B a r A r e a M i d d l e , C h a n g e s made t o f i l e : , -
C h a n g e s w e r e made t o t h e f i l e C : \ t e s t a . d g n . & -
T h e s e c h a n g e s w e r e made by t h e m a c r o & -
T e s t S h ow Temp Me s s a g e C e n t e r .
End S u b
332 I Chapter 17: Interactive Modification I
The next feedback method we will look at is the Show E r r o r method. The
text we supply with this method displays in the command/prompt area.
Sub TestShowError()
ShowError "Selection o f Cell Failed."
End Sub
.
WORKING
WITHSELECTIONSETS
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 GetSel ectedEl ements method.
Sub TestSelectionSetB()
Dim myElement As Element
Dim myElemEnum As ElementEnumerator
Set myElemEnum = ActiveModelReference.GetSelectedElements
I Working With Selection Sets I 333
Sub TestSelectionSetC(1
Dim mysettings A s Settings
Set mysettings = Application.ActiveSettings
If MsgBox("Change Selection to Color & rnySettings.Color & " ? " .
" ~
GETTINGUSERINPUT
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 TestCadInputA()
Dim myCIQ As CadInputQueue
Dim myCIM As CadInputMessage
Dim I A s Long
Set myCIQ = CadInputQueue
For I = 1 T o 10
Set myCIM = myCIQ.GetInput
Debug.Print myCIM.InputType
Next 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 TestCadInputB()
Dim myCIQ As CadInputQueue
Dim myCIM As CadInputMessage
Dim I A s Long
Dim pt3Selection As Point3d
Set myCIQ = CadInputQueue
For I = 1 T o 10
Set myCIM = myCIQ.GetInput(msdCad1nputTypeDataPoint)
pt3Selection = myCIM.Point
Debug.Print pt3Selection.X & " , & pt3Selection.Y
"
Next I
End Sub
I Getting User Input I 335
Sub TestCadInputC( 1
Dim myCIQ A s CadInputQueue
Dim myCIM A s CadInputMessage
Dim I As Long
Dim pt3Selection A s Point3d
Set myCIQ = CadInputQueue
For I = 1 To 10
Set myCIM = myCIQ.GetInput(msdCadInputTypeDataPoint, -
msdCadInputTypeReset)
Select Case myCIM.InputType
Case msdCadInputTypeDataPoint
pt3Selection = myCIM.Point
Debug.Print pt3Selection.X & , & pt3Selection.Y
Case msdCadInputTypeReset
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 Exi t 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?
msdCadInputTypeCommand = 1
msdCadInputTypeReset = 2
msdCadInputTypeDataPoint = 3
msdCadInputTypeKeyin = 4
msdCadInputTypeAny = 5
msdCadInputTypeUnassignedCB = 6
I Chapter 17: Interactive Modification I
When we begin capturing input using the CadInputQueue, 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 TestCadInputD()
D i m m y C I Q As C a d I n p u t Q u e u e
D i m m y C I M As C a d I n p u t M e s s a g e
D i m I As Long
D i m p t 3 S e l e c t i o n As P o i n t 3 d
S e t myCIQ = CadInputQueue
F o r I = 1 To 1 0
S e t myCIM = myCIQ.GetInput
S e l e c t Case m y C I M . I n p u t T y p e
Case msdCadInputTypeCommand
D e b u g . P r i n t "Command" & v b T a b & rnyC1M.CornrnandKeyin
Case r n s d C a d I n p u t T y p e R e s e t
E x i t For
Case msdCadInputTypeDataPoint
pt3Selection = myCIM.Point
Debug.Print " P o i n t " & vbTab & pt3Selection.X & vbTab & -
myC1M.ScreenPoint.Z
Case r n s d C a d I n p u t T y p e K e y i n
Debug.Print " K e y i n " & vbTab & myCIM.Keyin
Case r n s d C a d I n p u t T y p e A n y
Debug.Print "Any"
Case msdCadInputTypeUnassignedCB
D e b u g . P r i n t "UnassignedCB" & vbTab & ~
myCIM.CursorButton
End S e l e c t
Next I
End Sub
I Getting User Input I 337
Points
The points selected gives us much more than the X, Y, and Z locations in
Microstation. 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.
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-WORLDAPPLICATIONS
Now that we have an understanding of how these inputs work, lets put
them to work in some real-world examples.
S u b TestCadInputE()
Dim myCIQ As CadInputQueue
Dim myCIM As CadInputMessage
Dim pt3Start As Point3d
Dim pt3End As Point3d
Dim myLine As LineElement
Set myCIQ = CadInputQueue
Set myCIM = rnyCIQ.GetInput(rnsdCadInputTypeDataPoint, -
rnsdCadInputTypeReset)
Select Case rnyCIM.InputType
Case msdCadInputTypeReset
Exit S u b
Case msdCadInputTypeDataPoint
pt3Start = rnyCIM.Point
End Select
Set myCIM = rnyCIQ.GetInput(rnsdCadInputTypeDataPoint, -
msdCadInputTypeReset)
Select Case myCIM.InputType
Case msdCadInputTypeReset
Exit S u b
Case msdCadInputTypeDataPoint
pt3End = rnyCIM.Point
End Select
Set myLine = CreateLineElementZ(Nothing, pt3Start, pt3End)
ActiveModel Reference.AddE1 ement myLine
myLi ne. Redraw
End S u b
TestCadInputE 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 TestCadInputF( 1
D i m myCICl As C a d I n p u t Q u e u e
D i m myCIM As C a d I n p u t M e s s a g e
D i m p t 3 S t a r t As P o i n t 3 d
D i m p t 3 E n d As P o i n t 3 d
D i m m y L i n e As L i n e E l e m e n t
S e t myCICl = CadInputQueue
ShowCommand " T w o - P o i n t L i n e "
ShowPrompt " S e l e c t F i r s t P o i n t : "
S e t myCIM = myCIQ.GetInput(msdCadInputTypeDataPoint, ~
msdCadInputTypeReset.1
S e l e c t Case m y C I M . I n p u t T y p e
Case m s d C a d I n p u t T y p e R e s e t
ShowPrompt " "
" "
S h ow C o mm a n d
ShowStatus "Two-Point L i n e Reset."
E x i t Sub
Case m s d C a d I n p u t T y p e D a t a P o i n t
pt3Start = myCIM.Point
End S e l e c t
ShowPrompt " S e l e c t Second P o i n t : "
S e t myCIM = myCIQ.GetInput(msdCadInputTypeDataPoint, ~
msdCadInputTypeReset.1
S e l e c t Case m y C I M . I n p u t T y p e
Case m s d C a d I n p u t T y p e R e s e t
ShowPrompt " "
" "
S h ow C o mm a n d
ShowStatus "Two-Point L i n e Reset."
E x i t Sub
Case m s d C a d I n p u t T y p e D a t a P o i n t
pt3End = myCIM.Point
End S e l e c t
Set myLine = CreateLineElementZ(Nothing, p t 3 S t a r t . pt3End1
ActiveModel Reference.AddElement myLine
m y l i n e . Redraw
340 I Chapter 17: Interactive Modification I
ShowPrompt " "
" "
S h ow C omm a n d
Showstatus "Two-Point Line Drawn."
End Sub
Now, when this macro is run, the user is prompted at each step.
The CadInputQueue can be used for more than just capturing user
input. We can use it to execute commands as well. Here is one example:
Sub TestCadInputH()
Dim myCIQ As CadInputQueue
Dim myCIM As CadInputMessage
Dim pt3Start As Point3d
Dim pt3End As Point3d
Dim myLine As LineElement
Dim S e l E l e m s O As Element
Set myCIQ = CadInputQueue
Set myCIM = rnyCIQ.GetInput(rnsdCadInputTypeDataPoint, -
rnsdCadInputTypeReset)
Select Case rnyCIM.InputType
Case msdCadInputTypeReset
Exit Sub
Case msdCadInputTypeDataPoint
pt3Start = rnyCIM.point
End Select
Set myCIM = rnyCIQ.GetInput(rnsdCadInputTypeDataPoint, -
msdCadInputTypeReset)
Select Case myCIM.InputType
Case msdCadInputTypeReset
Exit Sub
Case msdCadInputTypeDataPoint
pt3End = rnyCIM.point
End Select
CadInputQueue.SendDragPoints pt3Start, pt3End
SelElems = -
A c t i veModel R e f e r e n c e . G e t S e l e c t e d E l e m e n t s . B u i 1 d A r r a y F r o m C o n t e n t s
If MsgBox("Are y o u sure y o u want to delete & " ~
= vbYes Then
Cad1nputQueue.SendCornrnand "DELETE"
End If
End Sub
I Some Real-World Applications I 341
Function P o i n t s B y L i n e O A s Point3dO
Dim myCIQ As CadInputQueue
Dim myCIM As CadInputMessage
Dim pt3Start As Point3d
Dim pt3End As Point3d
Dim selPts(0 To 1) As Point3d
Set myCIQ = CadInputQueue
Set myCIM = myCIQ.GetInput(msdCadInputTypeDataPoint, -
msdCadInputTypeReset)
Select Case myCIM.InputType
Case msdCadInputTypeReset
Err.Raise -12345
Exit Function
Case m s d C a d I n p u t T y p e D a t a P o i n t
pt3Start = myCIM.point
End Select
CadInputQueue.SendCommand PLACE LINE
C a d 1 n p u t Q u e u e . S e n d D a t a P o i n t pt3Start
Set myCIM = myCIQ.GetInput(msdCadInputTypeDataPoint, -
msdCadInputTypeReset)
Select Case myCIM.InputType
Case msdCadInputTypeReset
342 I Chapter 17: Interactive Modification I
E r r . R a i s e -12346
E x i t Function
Case msdCadInputTypeDataPoint
pt3End = myCIM.point
End S e l e c t
selPts(0) = pt3Start
selPts(1) = pt3End
PointsByLine = selPts
End F u n c t i o 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 P o i n t s By L i 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 TestCadInputJ()
On E r r o r GoTo e r r h n d
D i m s e l P t s 0 As P o i n t 3 d
selPts = PointsByLine
CadInputQueue.SendReset
CommandState.StartDefaultCommand
Debug.Print selPts(O1.X & " , " & selPts(O1.Y & ", " & selPts(01.Z
Debug.Print s e l P t s ( l ) . X & " , " & selPts(l1.Y & " , " & selPts(l1.Z
E x i t Sub
errhnd:
CadInputQueue.SendReset
CommandState.StartDefaultCommand
S e l e c t Case E r r . N u m b e r
Case - 1 2 3 4 5
'Start Point not selected
MsgBox " S t a r t P o i n t n o t s e l e c t e d . " , vbcritical
Case - 1 2 3 4 6
'End P o i n t n o t s e l e c t e d
MsgBox " E n d P o i n t n o t s e l e c t e d . " , vbcritical
I Some Real-World Applications I 343
End S e l e c t
End Sub
Sub TestCadInputK( 1
On E r r o r GoTo e r r h n d
D i m s e l P t s 0 As P o i n t 3 d
D i m p t 3 T e x t P t As P o i n t 3 d
D i m m y T e x t As T e x t E l e m e n t
D i m r o t M a t r i x As M a t r i x 3 d
sel Pts = PointsByLine
Cad1nputQueue.SendReset
CommandState.StartOefaultCommand
S e t myText = CreateTextElernentl(Nothing, " S t a r t " , selPts(0). rotMatrix)
ActiveModel Reference.AddElement myText
Set myText = CreateTextElementl(Nothing, "End", s e l P t s ( l ) , r o t M a t r i x )
ActiveModel Reference.AddElement myText
pt3TextPt.X = selPts(O).X + (selPts(l).X - selPts(O).X) / 2
pt3TextPt.Y = selPts(O).Y + (selPts(l).Y - selPts(O).Y) / 2
pt3TextPt.Z = selPts(O).Z + ( s e l P t s ( l ) . Z - selPts(O).Z) / 2
S e t myText = CreateTextElernentl(Nothing. "Mid". pt3TextPt, r o t M a t r i x )
ActiveModel Reference.AddElement myText
E x i t Sub
errhnd:
Cad1nputQueue.SendReset
CommandState.StartOefaultCommand
S e l e c t Case E r r . N u m b e r
Case - 1 2 3 4 5
'Start Point not selected
MsgBox " S t a r t P o i n t n o t s e l e c t e d . " , vbcritical
Case - 1 2 3 4 6
'End P o i n t n o t s e l e c t e d
MsgBox " E n d P o i n t n o t s e l e c t e d . " , vbcritical
I Chapter 17: Interactive Modification I
End Select
End Sub
The framework is the same as the previous example. We use our new
Po in t s By L i n e 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
Microstation:
Start
Function P o i n t s B y R e c t a n g l e O As P o i n t 3 d O
Dim myCIQ As CadInputQueue
Dim myCIM As CadInputMessage
Dim pt3Start As Point3d
Dim pt3End As Point3d
Dim selPts(0 T o 1) As Point3d
Set myCIQ = CadInputQueue
Set myCIM = myCIQ.GetInput(msdCadInputTypeDataPoint,
msdCadInputTypeReset)
Select Case myCIM.InputType
Case msdCadInputTypeReset
Err.Raise -12345
Exit Function
Case msdCadInputTypeDataPoint
pt3Start = myCIM.point
End Select
Cad1nputQueue.SendCommand PLACE BLOCK
I Some Real-World Applications I 345
Cad1nputQueue.SendDataPoint pt3Start
Set myCIM = myCIQ.GetInput(msdCadInputTypeDataPoint, -
msdCadInputTypeReset.1
Select Case myCIM.InputType
Case msdCadInputTypeReset
Err.Raise -12346
Exit Function
Case msdCadInputTypeDataPoint
pt3End = myCIM.point
End Select
sel Pts(0) = pt3Start
selPts(1) = pt3End
PointsByRectangle = selPts
End Function
Andnowaprocedure that uses P o i n t s B y R e c t a n g l e :
Sub TestCadInputL( 1
On Error GoTo errhnd
Dim s e l P t s 0 A s Point3d
sel Pts = PointsByRectangle
Cad1nputQueue.SendReset
CommandState.StartDefaultCommand
Debug.Print selPts(O1.X & ", " & selPts(O).Y & ", " & selPts(O).Z
Debug.Print selPts(l1.X & ". " & selPts(l).Y & ". " & selPts(l).Z
Exit Sub
errhnd:
Cad1nputQueue.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
346 I Chapter 17: Interactive Modification I
Gie
SEYi
Sub TestCadInputM()
On E r r o r GoTo e r r h n d
D i m s e l P t s 0 As P o i n t 3 d
D i m L i n e P t s ( 0 To 1) As P o i n t 3 d
D i m L i n e E l e m As L i n e E l e m e n t
D i m myESC As New E l e m e n t s c a n c r i t e r i a
D i m myRange As Range3d
D i m myElemEnum As E l e m e n t E n u m e r a t o r
D i m myElem As E l e m e n t
D i m F F i l e As L o n g
D i m m y C e l 1 H e a d e r As C e l l E l e m e n t
sel Pts = PointsByRectangl e
Cad1nputQueue.SendReset
CommandState.StartDefaultCommand
myRange = Range3dFromPoint3dPoint3d(sel P t s ( 0 ) . sel Pts(1))
myESC. E x c l u d e A l 1 T y p e s
m y E S C . I n c l u d e T y p e msdElementTypeCellHeader
myESC.IncludeOnlyWithinRange myRange
S e t myElemEnum = ActiveModelReference.Scan(myESC)
I Some Real-World Applications I 347
FFile = FreeFile
Open "C:\MicroStation VBA\CellExport.txt" For Output As BFFile
Print #FFi le, ActiveDesignFi 1 e. Name
Whi 1 e myEl emEnum.MoveNext
Set myElem = myElemEnum.Current
Set myCel1 Header = myEl em
Print BFFile, myCellHeader.Name & vbTab & -
myCellHeader.0rigin.X & vbTab & -
myCellHeader.0rigin.Y & vbTab & ~
errhnd:
CadInputQueue.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.
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 w
ill
be output to the text file.
348 I Chapter 17: Interactive Modification I
USINGSENDCOMMAND
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 foreign 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. Lets 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.
Sub Macro10
Dim startpoint As Point3d
Dim point As Point3d, point2 As Point3d
Dim lngTemp As Long
Start a command
CadInputQueue.SendCommand CGPLACE L I N E CONSTRAINED
CommandState.StartDefaultCommand
End S u b
This recorded macro reveals a command of CGPLACE LINE
CONSTRAINED. The coordinates shown are those selected in
Microstation as the macro was being recorded. Lets copy and paste the
recorded macro and modify it as follows:
S u b Macrol-modi f i edA( )
Dim point As Point3d
CadInputQueue.SendCommand CGPLACE L I N E CONSTRAINED
We have now stripped down this macro to the bare essentials. The
coordinates for the line have been replaced with (0, 0,O) and (4 , 5 , 6 ).
Let's record another macro. This time we will record drawing a Block
(rectangle).
Sub Macro20
D i m s t a r t p o i n t As P o i n t 3 d
D i m p o i n t As P o i n t 3 d , p o i n t 2 As P o i n t 3 d
D i m l n g T e m p As L o n g
' S t a r t a command
CadInDutQueue.SendCommand "PLACE BLOCK I C O N "
CommandState.StartDefaultCommand
End S u b
S u b Macro2Lmodi f i edA( 1
D i m p o i n t As P o i n t 3 d
CadInputQueue.SendCommand "PLACE BLOCK I C O N "
p0int.X = 0
p0int.Y = 0
I Using Sendcommand I 351
p0int.Z = 0
CadInputQueue.SendDataPoint point, 1
p0int.X = p0int.X + 2.5
p0int.Y = p0int.Y - 0.75
CadInputQueue.SendDataPoint point, 1
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.
CadInputQueue.SendDataPoint point, 1
p0int.X = p0int.X + 2.5
p0int.Y = p0int.Y - 0.75
CadInputQueue.SendDataPoint point, 1
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 TestCadInputN( 1
Dim myCIQ As CadInputQueue
Dim myCIM As CadInputMessage
Dim I As Long
Set myCIQ = CadInputQueue
F o r I = 1 To 10
Set myCIM = myCIQ.GetInput(msdCad1nputTypeCommand)
352 I Chapter 17: Interactive Modification I
Debug.Print myCIM.CommandKeyin
Next I
End Sub
T e s t Ca d I n p u t N captures ten
commands. This is
CGPLACE LINE CONSTRAINED
different from recording PLACE BLOCK ICON
macros in that we do not ~ ~ ~ ~ ~ ICON ~ C ~ ~ R C L E
get all of the other input, Attach Tags
WORDPROCESSOR PLACE TEXT ICON
such as point selections, PLACE CELL ICON
etc. The only thing we MEASURE DISTANCE ICON
DIMCREATE ELEMENT
capture is the command PLACE FENCE ICON
name.
One additional method of determining command names should be
mentioned.
digitizer palette
dimcreate particletrace
dimension plot
dimstyle popset
displayset preview
dialog openfile
dialog drawingscale open
dialog drawingscale
delete
erase
bogus keyin
Sub TestMessageA( 1
CadInputQueue.SendCommand DIALOG OPENFILE
End Sub
I Modeless Dialog Boxes I 353
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.f rm
The first form,
frmMatchProperties.
frm,looks like this:
The form looks
simple enough. We
have a few command
buttons, a couple of
frames, a handful of
check boxes, a label,
and four text boxes.
Before we look at the
code behind the
controls, lets discuss the programs desired functionality.
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.
2 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. Lets 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:
El frmMatchProperties
El btnSelectSource
El chkLevel
El txtLevel
El chkColor
El txtColor
El chkLinestyle
El txtLinestyle
El chkLineweight
El txtlineweight
El btnchange
El lblcount
El btnClose
El fraSource
El fraDestination
I Modeless Dialog Boxes I 355
Control Properties
The Locked property of each TextBox should be True: We do not
want the user arbitrarily typing in values that do not work. The text
boxes will be populated by the source elements properties.
The Alignment property of each CheckBox should be
fmAlignmentLeW. This places the caption of the CheckBox on the
left of the CheckBox.
The ControlTipTextof the Select CommandButton is Click Here to
make the current selection the source element..
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 DeclarationsArea
We have two lines of code in the general declarations area of our code.
O p t i o n Expl ic i t
D i m 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.
2 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.
3 Get and place the linestyle property.
4 Get and place the lineweight property.
Now, let's look at the code behind the btnSelectSource-Click event:
P r i v a t e Sub b t n S e l e c t S o u r c e c C l i c k 0
D i m m y E l e m e n t s 0 As E l e m e n t
D i m myElemEnum As E l e m e n t E n u m e r a t o r
D i m myCol o r T a b l e As C o l o r T a b l e
S e t myElemEnum = ActiveModelReference.GetSelectedElements
myElements =
ActiveModelReference.GetSelectedElements.Bui1dArrayFromContents
If UBound(myE1ements) = 0 Then
Set elemsource = myElements(0)
If N o t m y E l e m e n t s ( O ) . L e v e l Is N o t h i n g Then
txtLevel .Text = m y E l e m e n t s ( 0 ) . L e v e l .Name
End If
Set myColorTable = ActiveDesignFile.ExtractColorTable
S e l e c t Case myEl e m e n t s ( 0 ) . C o l o r
Case - 1
txtColor.Text = "I'
txtColor.BackColor = RGB(255, 2 5 5 , 2 5 5 )
t x t L i nestyle.Text = -
myEl e m e n t s ( 0 ) . L i n e S t y l e . Name
txtLineweight .Text = myEl e m e n t s ( 0 ) . L i n e w e i g h t
Case E l s e
I Modeless Dialog Boxes I 357
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
End Sub
End Sub
End Sub
As the user moves the cursor around the form and the frames, we do not
want to display anything in the prompt because clicking on the form or
frame does not do anything. So, we use ShowPrompt with an empty
string so nothing displays.
P r i v a t e Sub b t n C l o s e c M o u s e M o v e ( B y V a 1 B u t t o n As I n t e g e r , -
B y V a l S h i f t As I n t e g e r , B y V a l X As S i n g l e ,
B y V a l Y As S i n g l e )
S h o w P r o m p t "C1 o s e " " V B A M a t c h P r o p e r t i e s " " "
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.
P r i v a t e Sub U s e r F o r m - I n i t i a l i z e 0
ShowCommand " V B A M a t c h P r o p e r t i e s : "
End Sub
UserForm Queryclose
The Queryclose event is triggered just before the form 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).
vbFormControlMenu = 0
vbFormCode = 1
El vbAppWindows = 2
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.
" "
Sh ow C omma n d
End Sub
Sub T e s t M a t c h P r o p e r t i e s ( 1
frmMatchProperties.Show vbModeless
End Sub
The procedure TestMatchProperties, 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 <FS>
key while holding down the <Alt> key (<Ah+ FS>).
362 I Chapter 17: Interactive Modification I
Here is the Match
Properties Form in use.
Notice the Command
and Prompt areas at the
bottom.
The Match Properties
Program is simple and
straightforward. We
allow the user to make
modifications to
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.
f rmAl ignText.frm
The next form we will import into
our VBA project is the
frmA/ignText.frm file. This form
allows the user to perform text
alignment and distribution
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
displayed as modeless. This
b program involves geometric
calculations and moving text
elements based on those calculations.
Desired Functionality
Selected Text can be aligned Horizontally to the selected or
entered X value.
Text can be aligned Left, Center, or Right.
Text can be distributed evenly vertically so equal spacing exists
between each text element.
Only Text elements can be used, not Text Nodes.
I Providing User Feedback and Information I 363
Frames, command buttons, labels, and text boxes are used in this
project. Once again, you can import the form 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
El fraBasePoint
El fraHoriAlign
El btnPickBasePoint
El txtx
El txtY
El btnAlignLeft
El btnAlignCenter
El btnAlignRight
El btnDistributeVert
Pick Button
The code in the Pick buttons click event allows the user to select a
point in Microstation. The selected points X and Y components then
display in the text boxes.
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.
P r i v a t e Sub t x t X L K e y P r e s s ( B y V a 1 K e y A s c i i A s M S F o r r n s . R e t u r n I n t e g e r )
S e l e c t Case KeyAsci i
Case A s c ( " 0 " ) To A s c ( " 9 " )
Case A s c ( " . " )
I f InStr(1, txtX.Text, "."I > 0 Then
KeyAscii = 0
End I f
Case E l s e
KeyAscii = 0
End S e l e c t
End Sub
I Providing User Feedback and Information I 365
P r i v a t e Sub t x t Y - K e y P r e s s ( B y V a 1 K e y A s c i i As M S F o r m s . R e t u r n 1 n t e g e r )
S e l e c t Case K e y A s c i i
Case A s c ( " 0 " ) To A s c ( " 9 " )
Case A s c ( " . " )
If InStr(1, txtY.Text, ".") > 0 Then
KeyAscii = 0
End I f
Case E l s e
KeyAscii = 0
End S e l e c t
End Sub
The Keypress event tells us the ASCII character code of the keyboard
character that was pressed. If we change the KeyAscii 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:
Is the Key Ascii between the numbers 0 to 9? If so, do nothing.
Always allow numbers 0 through 9 to be entered.
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.
Case Else (if any other key is pressed), set KeyAscii 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.
Enum A1 ignMode
msvbaAlignModeLeft = 1
msvbaAlignModeCenter = 2
msvbaAl ignModeRight = 3
End Enum
Now when we declare our procedure to align the selected text, it looks
like this:
D i m p t 3 B a s e P o i n t As P o i n t 3 d
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:
myTextElem.Boundary.Low.X, 0)
myText El em. Rewrite
Case msvbaAlignModeRight
rnyTextElem.Move P o i n t 3 d F r o m X Y ( p t 3 B a s e P o i n t . X ~ ~
myTextElem.Boundary.High.X, 0)
myText El em. Rewrite
Case msvbaAlignModeCenter
myTextElem.Move P o i n t 3 d F r o m X Y ( p t 3 B a s e P o i n t . X - -
yTextE1em.Boundary.Low.X - -
(myTextE1em.Boundary.High.X - ~
myTextE1em.Boundary.Low.X) / 2 , 0)
myText El em. 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
Sel ect Case statement to parse out the text elements from the others.
Next, we use another Se 1 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.
P r i v a t e Sub b t n A l i g n L e f t L C l i c k 0
A l i g n s e l e c t e d rnsvbaAlignModeLeft
End Sub
P r i v a t e Sub b t n A l i g n c e n t e r - C 1 i c k ( 1
A l i g n s e l e c t e d rnsvbaAlignModeCenter
End Sub
P r i v a t e Sub b t n A l i g n R i g h t - C l i c k 0
A l i g n s e l e c t e d rnsvbaAlignModeRight
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 evenly.
1 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:
Discover the minimum and maximum points of the selected
text elements.
Determine the vertical order in which the text elements appear.
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.
pt3StartPoint.Y Then
I Chapter 17: Interactive Modification I
pt3StartPoint.Y = m y T e x t E 1 e m . B o u n d a r y . H i g h . Y
End If
If myTextE1em.Boundary.High.Y < pt3EndPoint.Y Then
pt3EndPoint.Y = m y T e x t E 1 e m . B o u n d a r y . H i g h . Y
End If
End Select
Next I
Dim pt3Points(0 T o 1) As Point3d
pt3Points(O) = pt3StartPoint
pt3Points(l) = pt3EndPoint
GetMinMaxY = pt3Points
End 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.
pt3BoundPt(UBound(pt3BoundPt)) =
myTextElem.Boundary.High
ReDim P r e s e r v e lngElemID(UBound(lngElem1D) + 1) As
DLong
ReDim P r e s e r v e pt3BoundPt(UBound(pt3BoundPt) + 1 ) As -
Point3d
End I f
End S e l e c t
Next I
ReDim P r e s e r v e lngElemID(UBound(lngElemID) - 1 ) As DLong
b o o l Madechange = True
Whi 1 e b o o l Madechange = True
b o o l Madechange = Fa1 s e
F o r I = L B o u n d ( l n g E l e m 1 D ) To U B o u n d ( l n g E l e m 1 D ) - 1
I f pt3BoundPt(I + 1).Y > p t 3 B o u n d P t ( I ) . Y Then
tmpID = lngElemID(1)
tmpPt = pt3BoundPt(I)
lngElemID(1) = lngElemID(1 + 1)
pt3BoundPt(I) = pt3BoundPt(I + 1)
l n g E l e m I D ( 1 + 1) = tmpID
p t 3 B o u n d P t ( I + 1) = tmpPt
boolMadeChange = True
End I f
Next I
Wend
D i m E l e m s I n O As Element
ReDim E l e m s I n ( 0 To U B o u n d ( l n g E l e m 1 D ) )
F o r I = L B o u n d ( l n g E l e m 1 D ) To U B o u n d ( l n g E l e m 1 D )
Set ElemsIn(1) = ActiveDesignFile.GetElementByID(lngElemID~1~~
Next I
SortElementsVertically = ElemsIn
End F u n c t i o n
There is a lot of code to look at here. After we divide it into four little
chunks, it becomes easier to understand.
Variable Declaration
D i m I As Long
D i m boolMadeChange As Boolean
D i m I n g E l e m I D O A s DLong
D i m p t 3 B o u n d P t O As P o i n t 3 d
D i m mrTextElem As TextElement
372 I Chapter 17: Interactive Modification I
Dim myTextEl em2 As TextEl ement
Dim tmpID 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.
Poi nt3d
End If
End Select
Next I
ReDim Preserve lngElemID(UBound(lngElem1D) - 1) As DLong
ReDim Preserve pt3BoundPt(UBound(pt3BoundPt) - 1) As Point3d
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 elements ID property and put it in one
of the dynamic array variables and get the elements 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 dont 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
Bubble Sorting
bool Madechange = True
Whi 1 e bool Madechange = True
bool Madechange = Fa1 se
For I = LBound(lngElem1D) T o UBound(lngElem1D) - 1
If pt3BoundPt(I + 1).Y > pt3BoundPt(I).Y Then
tmpID = lngElemID(1)
tmpPt = pt3BoundPt(I)
lngElemID(1) = lngElemID(1 + 1)
pt3BoundPt(I) = pt3BoundPt(I + 1)
lngElemID(1 + 1) = tmpID
pt3BoundPt(I + 1) = tmpPt
boolMadeChange = True
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 boolMadeChangeto 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.
E l e m e n t s I n As V a r i a n t ) As L o n g
D i m I As Long
For I = L B o u n d ( E l e m e n t s 1 n ) To U B o u n d ( E l e m e n t s 1 n )
I f E l e m e n t s I n ( I ) . T y p e = ElemType Then
GetSelectedCount = GetSelectedCount + 1
End If
Next I
End F u n c t i o n
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 Distributiontask.
P r i v a t e Sub b t n D i s t r i b u t e V e r t L C l i c k 0
D i m M y P t s As V a r i a n t
D i m p t 3 S t a r t P o i n t As P o i n t 3 d
D i m p t 3 E n d P o i n t As P o i n t 3 d
D i m myElemEnum As E l e m e n t E n u m e r a t o r
D i m myElem As E l e m e n t
D i m m y E l e m s 0 As E l e m e n t
Dim I As Long
D i m m y T e x t E l em As T e x t E l e m e n t
D i m l n g S p a c e s As L o n g
D i m d b l S p a c e P e r E l e m e n t As D o u b l e
D i m s o r t E l e m s 0 As E l e m e n t
S e t myElemEnum = ActiveModelReference.GetSelectedElements
myElems = myEl emEnum. B u i 1 d A r r a y F r o m C o n t e n t s
MyPts = GetMinMaxY(msdElementTypeText, myElems)
1ngSpaces = GetSelectedCount(msdElementTypeText, m y E l e m s ) - 1
I f l n g S p a c e s > 1 Then
dblSpacePerElement = (MyPts(O1.Y - MyPts(l).Y) / 1ngSpaces
I Providing User Feedback and Information I 375
sortElerns = S o r t E l e r n e n t s V e r t i c a l l y ( r n s d E l e r n e n t T y p e T e x t , rnyElerns)
For I = L B o u n d ( s o r t E 1 e m s ) To U B o u n d ( s o r t E 1 e m s )
S e t myTextElem = sortElems(1)
myTextElem.Move Point3dFromXY(O, MyPts(O1.Y -
dblSpacePerElernent * I rnyTextE1ern.Boundary.High.Y)
~
m y T e x t E l em. R e w r i t e
myTextElem.Redraw
Next I
End I f
End Sub
A close look at the above code reveals the use of the three functions we
just finished discussing. G e t M i nMaxY, G e t S e l e c t e d c o u n t , and
S o r t E l e m e n t s V e r t i c a l l y are used. After we have sorted the elements
vertically, we move them so that they are spaced evenly.
P r i v a t e Sub UserForm-MouseMove(ByVa1 B u t t o n A s I n t e g e r , -
B y V a l S h i f t A s I n t e g e r , B y V a l X As S i n g l e , B y V a l Y A s S i n g l e )
S h owS t a t u s " "
ShowPrompt " "
ShowCommand M e . C a p t i o n
End Sub
P r i v a t e Sub btnDistributeVert-MouseMove(ByVa1 B u t t o n A s I n t e g e r ,
B y V a l S h i f t As I n t e g e r , B y V a l X As S i n g l e , -
B y V a l Y As S i n g l e )
" "
S h owS t a t u s
ShowPrompt " D i s t r i b u t e T e x t V e r t i c a l l y "
ShowCommand M e . C a p t i o n
End Sub
P r i v a t e Sub f r a H o r i A l i g n - M o u s e M o v e ( B y V a 1 B u t t o n As I n t e g e r , -
B y V a l S h i f t A s I n t e g e r , B y V a l X As S i n g l e , B y V a l Y As S i n g l e )
S h owS t a t u s " "
ShowPrompt " "
ShowCommand M e . C a p t i o n
End Sub
I Chapter 17: Interactive Modification I
P r i v a t e S u b fraBasePointLMouseMove(ByVa1 B u t t o n A s I n t e g e r , -
B y V a l S h i f t As I n t e g e r , B y V a l X As S i n g l e , B y V a l Y As S i n g l e )
S how St a t u s " "
ShowPrompt " "
ShowCommand M e . C a p t i o n
End S u b
P r i v a t e S u b btnPickBasePoint-MouseMove(ByVa1 B u t t o n As I n t e g e r ,
B y V a l S h i f t As I n t e g e r , B y V a l X As S i n g l e , B y V a l Y As S i n g l e )
" "
S how St a t u s
ShowPrompt " S e l e c t Base P o i n t : "
ShowCommand M e . C a p t i o n
End S u b
P r i v a t e S u b b t n A l i g n R i g h t - M o u s e M o v e ( B y V a 1 B u t t o n As I n t e g e r ,
B y V a l S h i f t As I n t e g e r , B y V a l X A s S i n g l e , B y V a l Y As S i n g l e )
" "
S how St a t u s
ShowPrompt " A l i g n S e l e c t e d T e x t R i g h t as Base P o i n t "
ShowCommand M e . C a p t i o n
End S u b
P r i v a t e S u b btnAlignLeftLMouseMove(ByVa1 B u t t o n A s I n t e g e r , -
B y V a l S h i f t As I n t e g e r , B y V a l X As S i n g l e , B y V a l Y As S i n g l e )
S how St a t u s " "
P r i v a t e S u b btnAlignCenter-MouseMove(ByVa1 B u t t o n As I n t e g e r ,
B y V a l S h i f t As I n t e g e r , B y V a l X As S i n g l e , B y V a l Y As S i n g l e )
" "
S how St a t u s
S h o w P r o m p t "A1 i g n S e l e c t e d T e x t C e n t e r a t B a s e P o i n t "
ShowCommand M e . C a p t i o n
End S u b
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 top-
left 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
f rmExportElements.f rm
The frmExportElemen ts.frm User
Form accomplishes a simple task:
it exports elements on specific
levels to a new design file.
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
1stLevels
txtFileName
btnExport
.
Control Properties
1stLevelsproperty MultiSelect is set to 2 -
fmMultiSelectExtended
1stLevelsproperty 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.
P r i v a t e Sub U s e r F o r m - I n i t i a l i z e 0
D i m m y L e v e l As L e v e l
D i m LevelNamesO As S t r i n g
D i m Madechange A s B o o l e a n
D i m tmpName A s S t r i n g
D i m I As L o n g
ReDim L e v e l N a m e s ( 0 )
F o r Each m y L e v e l I n A c t i v e D e s i g n F i l e . L e v e 1 s
L e v e l Names(UBound( L e v e l Names) ) = m y L e v e l .Name
I Chapter 17: Interactive Modification I
ReDim P r e s e r v e LevelNames(UBound(Leve1Names) + 1)
Next
ReDim P r e s e r v e LevelNames(UBound(Leve1Names) - 1)
Madechange = True
W h i l e Madechange = True
Madechange = False
F o r I = LBound(Leve1Names) To UBound(Leve1Names) - 1
I f S t r C o m p ( L e v e l N a m e s ( 1 ) . L e v e l N a m e s ( 1 + 1)) = 1 Then
tmpName = LevelNames(1)
LevelNames(1) = LevelNames(1 + 1)
LevelNames(1 + 1) = tmpName
Madechange = True
End I f
Next I
Wend
F o r I = LBound(Leve1Names) To UBound(Leve1Names)
1 s t L e v e l s . A d d I t e m L e v e l Names( I)
Next I
End Sub
When comparing numeric values, we can use greater than 0) 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 StrCornp
function to compare two strings.
P r i v a t e Sub b t n E x p o r t - C l i c k (
D i m m y F i l e N a m e As S t r i n g
D i m m y N e w F i l e As D e s i g n F i l e
D i m I As Long
D i m E l e m I D As DLong
D i m m y E l e m s 0 As E l e m e n t
D i m myElemEnum As E l e m e n t E n u m e r a t o r
D i m m y L e v e l As L e v e l
myFileName = txtFileName.Text
I f ActiveModelReference.1~30Then
C r e a t e D e s i g n F i 1 e " s e e d 3 d " . m y F i 1 eName, Fa1 s e
Else
C r e a t e D e s i g n F i 1 e " s e e d 2 d " , m y F i 1 eName, Fa1 s e
End I f
S e t myNewFile = OpenDesignFileForProgram(myFi1eName)
D i m m y S e l C r i t e r i a As New E l e m e n t s c a n c r i t e r i a
mySel Cri t e r i a . E x c l u d e A l 1 L e v e l s
I Providing User Feedback and Information I 379
For I = 1 To 1 s t L e v e l s . L i s t C o u n t
If lstLevels.Selected(1 - 1) T h e n
mySel Cri t e r i a . I n c l u d e L e v e l ~
ActiveModelReference.Levels(lstLeve1s.List (I - 1))
End I f
Next I
S e t myElemEnum = ActiveModel Reference.Scan(mySe1Criteria)
myElems = myElemEnum.BuildArrayFromContents
For I = LBound(myE1ems) To UBound(myE1ems)
myNewFile.Models(l).CopyElement myElems(1)
Next I
myNewFile.Save
MsgBox UBound(myE1ems) + 1 & " elements c r e a t e d i n f i l e " & vbCr & -
myFileName, v b I n f o r m a t i o n , Me.Caption
End Sub
P r i v a t e Sub UserForm-MouseMove(ByVa1 B u t t o n As I n t e g e r , -
B y V a l S h i f t As I n t e g e r , B y V a l X As S i n g l e ,
B y V a l Y As S i n g l e )
ShowPrompt " "
" "
S h owS t a t u s
" "
S h ow C omma n d
End Sub
P r i v a t e Sub 1 s t L e v e l s - M o u s e M o v e ( B y V a 1 B u t t o n As I n t e g e r , -
B y V a l S h i f t A s I n t e g e r , B y V a l X As S i n g l e , B y V a l Y A s S i n g l e )
380 I Chapter 17: Interactive Modification I
ShowPrompt " "
" "
S h ow C o mm a n d
End Sub
P r i v a t e Sub t x t F i l e N a m e c M o u s e M o v e ( B y V a 1 B u t t o n A s I n t e g e r , -
B y V a l S h i f t As I n t e g e r , B y V a l X A s S i n g l e , B y V a l Y As S i n g l e )
ShowPrompt "I'
" "
S h ow C o mm a n d
End Sub
P r i v a t e Sub btnExport-MouseMove(ByVa1 B u t t o n A s I n t e g e r ,
B y V a l S h i f t As I n t e g e r , B y V a l X A s S i n g l e , B y V a l Y As S i n g l e )
ShowPrompt "I'
" "
S h ow C o mm a n d
End Sub
f rmDFAV.frm
The frmDFAKfrm program is used
to display attachments of design files.
Control Names
txtFolder
btnBrowse
lstFiles
1stAttachments
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 Local Disk(C:)
Key Largo (0:)
instead. Here's the Folder projects on 'Puny' (V:)
PunylRem on 'Punyl' (Y:)
Selection dialog box we want: Store on 'Dev' (I:)
Shared Documents
How do we get it? We use the Administrator'sDocuments
Guest's Documents
Windows API. jerryw's Documents
jkw's Documents
Program Components
Retrieve Settings from Registry on Form Initialize
Allow User to Select Root Folder
Search in Folder for .dgn files
I Providing User Feedback and Information I 383
P r i v a t e Sub U s e r F o r m - I n i t i a l i z e 0
txtFolder.Text = GetSetting("VBA F i l e Attachment Viewer",
"Defaults", "Path")
I f t x t F o l der .Text <> " " Then
chkSubFolders.Value = GetSetting("VBA F i l e Attachment
Viewer", -"Defaults", "Include Subs")
Popul a t e F i l e L i s t
End I f
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.
P r i v a t e Type BrowseInfo
hWndOwner As Long
p i d l R o o t As Long
sDisplayName As S t r i n g
s T i t l e As S t r i n g
u l F l a g s As Long
l p f n As Long
l P a r a m As Long
i I m a g e As Long
End T y p e
P r i v a t e D e c l a r e F u n c t i o n SHBrowseForFolder L i b " s h e 1 1 3 2 . d l l " -
( b B r o w s e As B r o w s e I n f o ) As Long
P r i v a t e Declare Function SHGetPathFromIDList L i b " s h e 1 1 3 2 . d l l "
( B y V a l l I t e m A s L o n g , B y V a l s D i r As S t r i n g ) As L o n g
I Chapter 17: Interactive Modification I
P r i v a t e Const Bif-ReturnOnlyFSDirs = 1
P r i v a t e C o n s t Bif-DontGoBelowDomain = 2
P r i v a t e Const Bif-EditBox = 16
P r i v a t e Const Bif-NewDialogStyle = 64
P r i v a t e C o n s t Bif-UseNewui = 80
P r i v a t e Const Bif-BrowseForComputer = 4096
P r i v a t e Const Bif-BrowseForPrinter = 8192
P r i v a t e Const Bif-BrowseIncludeFiles = 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.
P r i v a t e Sub b t n B r o w s e c C 1 i c k ( 1
D i m MyBI A s B r o w s e I n f o
D i m F L i s t As L o n g
D i m D i r N a m e As S t r i n g
D i m S e l F o l d e r As L o n g
DirName = Space(255)
MyBI.sTitle = " S e l e c t Root F o l d e r : "
MyBI.sDisplayName = Space(255)
MyBI.ulFlags = Bif-ReturnOnlyFSDirs
FList = SHBrowseForFolder(MyB1)
Sel Folder = S H G e t P a t h F r o m I D L i s t ( F L i s t , DirName)
DirName = Left(DirName, I n S t r ( 1 , DirName, C h r ( 0 ) ) - 1)
I f DirName <> " " Then
txtFolder.Text = DirName
Else
t x t Fo 1 d e r .T e x t = " "
End I f
PopulateFileList
End Sub
Sub PopulateFileListO
1s t F i l e s . C l e a r
D i m m y F o l d e r As F o l d e r
D i m myFSO As New F i l e S y s t e m O b j e c t
I f txtFolder.Text <> "I' Then
S e t myFol d e r = myFSO.GetFo1 d e r ( t x t F o l d e r . T e x t )
FilesInFolder myfolder, "dgn", chkSubFolders, l s t F i l e s
I Providing User Feedback and Information I 385
End I f
1blFiles.Caption = "Design F i l e s i n Folder - " &
1 s t F i l e s . L i s t C o u n t & - " F i l e s Found."
End Sub
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.
End Sub
I Interacting with MDL Applications I 387
P r i v a t e Sub UserForm-QueryClose(Cance1 As I n t e g e r , -
C l o s e M o d e As I n t e g e r )
S a v e s e t t i n g "VBA F i l e A t t a c h m e n t V i e w e r " , "Defaults", -
" Pa t h " , t x t F o 1 d e r . T e x t
S a v e s e t t i n g "VBA F i l e A t t a c h m e n t V i e w e r " , " D e f a u l t s " ,
" I n c l u d e Subs", - chkSubFolders.Va1ue
End Sub
We are saving two settings to the Windows registry. These are the
settings read by the initialize event of the form.
D i m m o d a l H a n d l e r As New M a c r o 5 M o d a l H a n d l e r
AddModal D i a l o g E v e n t s H a n d 1 e r m o d a l H a n d l e r
' S t a r t a command
Cad1nputQueue.SendCommand "MDL LOAD PLAIMAGE"
' Send p o i n t s t o s i m u l a t e a d o w n - d r a g - u p a c t i o n
p0int.X = startP0int.X
p0int.Y = startP0int.Y
p0int.Z = startP0int.Z
point2.X = p 0 i n t . X + 2.938037
point2.Y = p0int.Y - 2.980928
point2.Z = p0int.Z
Cad1nputQueue.SendDragPoints p o i n t , p o i n t 2 , 1
RemoveModal D i a1 o g E v e n t s H a n d 1 e r m o d a l H a n d l e r
CommandState.StartDefaultCommand
End S u b
DialogBoxNarne A s S t r i n g , ByVal D i a l o g R e s u l t A s M s d D i a l o g B o x R e s u l t )
End Sub
DialogBoxName As S t r i n g , D i a l o g R e s u l t As M s d D i a l o g B o x R e s u l t )
CadInputQueue.SendCommand -
CadInputQueue.SendCommand -
CadInputQueue.SendCommand
"MDL COMMAND MGDSHOOK,fileList-setFileNameCmd " &
" b e n t 1 e y b .j p g "
' Remove t h e f o l l o w i n g l i n e t o l e t t h e u s e r c l o s e t h e d i a l o g b o x .
D i a l ogResul t = m s d D i a l ogBoxResu1 t O K
End Sub
Every time the macro Ma c r 05 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
clsl magelnsertion. We have added two public variables that act as
properties to this class module.
Implements IModalDialogEvents
P u b l i c F i l e P a t h As S t r i n g
P u b l i c F i l e N a m e As S t r i n g
D i a l o g B o x N a m e As S t r i n g , B y V a l D i a l o g R e s u l t As
MsdDi a1 o g B o x R e s u l t )
End Sub
390 I Chapter 17: Interactive Modification I
FilePath
CadInputQueue.SendCommand
"MDL COMMAND MGDSHOOK,fileListpsetFileNameCmd & "
Fi 1 eName
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 TestImageInsertionO
Dim pointl As Point3d, point2 As Point3d
Dim modalHandler As New ClsImageInsertion
modalHandler. F i l e P a t h = "C:\Program Files\Bentley\MicroStation\"
modalHandler. FileName = "bentleyb. jpg"
AddModalDialogEventsHandler modalHandler
CadInputQueue.SendCommand "MDL LOAD PLAIMAGE"
point1.X = 0: point1.Y = 0: point1.Z = 0
point2.X = 1: point2.Y = 1: point2.Z = 0
CadInputQueue.SendDragPoints pointl, point2, 1
RemoveModalDialogEventsHandler modalHandler
CommandState.StartDefaultCommand
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.
In this Chapter:
Interface basics
Class module review
Class module lifecycle
The dynamics event
391
392 I Chapter 18: Interface Essentials I
The Locatecriteria object
IPrimitiveCommandEvents interface
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 IPrimitiveCommandEvents
Using the Implements keyword in a class module means the class
module inherits the methods or events of the interface.
I m p l e m e n t s 1P r i m i t i v e c o
Implements IPrimitiveCommandEvents
P r i v a t e Sub IPrimitiveCommandEvents-Cleanup0
End S u b
P r i v a t e S u b I P r i r n i t i v e C o m m a n d E v e n t s D a t a P o i n t ( P o i n t ( P 0 i n t As P o i n t 3 d ,
B y V a l V i e w As V i e w )
End S u b
P r i v a t e S u b I P r i m i t i v e C o m m a n d E v e n t s D y n a m i c s ( P o i n t As P o i n t 3 d , ~
B y V a l V i e w A s V i e w , B y V a l DrawMode As M s d D r a w i n g M o d e )
End S u b
P r i v a t e Sub IPrimitiveCommandEventsKeyin(ByVa1 K e y i n As S t r i n g )
End S u b
P r i v a t e Sub IPrimitiveCommandEvents-Reset0
End S u b
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s S t a r t ( )
End S u b
CLASSMODULE
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 Essentials I
We create a new class module by using the VBA menu Insert > Class
Module. By default, VBA names the new class modules Classl:
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 clsFileWriter: In a later example, we will name a
Class LCE-Text to indicate we are working with the
ILocateCommandEvents interface (LCE) and that we are doing
something with text.
After a class module is inserted and named, we begin writing our code.
Methods and functions are written very much like they are in code
modules. We can create events for our class modules. We can create
properties by declaring variables as Public in the General Declarations
area of the code module. And we can also create properties through the
use of Get and Set (or Let)statements.
The end result of creating a class is a new object. Classes cannot operate
independently. They need other code to initiate them, set their
properties, and use their methods. I will demonstrate two ways to call up
a class module. The first is to create a new class module named
c1sNetNode: Here is the code:
P r i v a t e Type IPAddr
S e t 1 As B y t e
S e t 2 As B y t e
S e t 3 As B y t e
S e t 4 As B y t e
End T y p e
P u b l i c Name As S t r i n g
P r i v a t e I P A d d r e s s As I P A d d r
Sub P i n g 0
MsgBox P i n g i n g & 1PAddress.Setl & I. & -
IPAddress.Set4, , Name
End Sub
Sub S e t I P A d d r e s s ( 1 P A As B y t e , I P B As B y t e , I P C As B y t e ,
I P D As B y t e )
IPAddress.Set1 = IPA
I Class Module Lifecycle I 395
IPAddress.Set2 = IPB
IPAddress.Set3 = IPC
IPAddress.Set4 = IPD
End S u b
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.
S u b T e s t C l sNetNodeA( 1
D i m myNetNode As c l s N e t N o d e
S e t myNetNode = New c l s N e t N o d e
m y N e t N o d e . S e t I P A d d r e s s 192, 1 6 8 , 1, 1
myNetNode.Name = Router
myNetNode. P i n g
End S u b
S u b T e s t C l sNetNodeB( 1
D i m myNetNode As New c l s N e t N o d e
m y N e t N o d e . S e t I P A d d r e s s 192, 1 6 8 , 1, 1
myNetNode.Name = Router
myNetNode. P i n g
End S u b
CLASSMODULE
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:
S e t myNetNode = Nothing
ILocateCommandEvents
The ILocateCommandEvents Interface allows us to have the user select
or Locate an element. Here are the events exposed through the
ILocateCommandEvents interface:
Private Sub ILocateCommandEvents-Accept(ByVa1 Element As
Element, Point As Point3d, ByVal View As View)
Private Sub ILocateCommandEvents-Cleanup()
I Class Module Lifecycle I 397
Implements ILocateCommandEvents
P r i v a t e S e l E l e m e n t As E l e m e n t
P r i v a t e S u b ILocateCommandEvents-Accept(ByVa1 -
E l e m e n t As E l e m e n t , P o i n t As P o i n t 3 d , B y V a l V i e w As V i e w )
D i m e l e m T e x t As T e x t E l e m e n t
S e t e l emText = Element
elemText.Redraw msdDrawingModeErase
elemText.Text = UCase(elemText.Text)
e l e m T e x t . R e d r a w msdDrawingModeNorma1
e l emText. R e w r i t e
ActiveModel Reference.UnselectAl1 Elements
CommandState.StartDefaultCommand
End S u b
P r i v a t e Sub ILocateCommandEventsCleanupO
End S u b
P r i v a t e S u b I L o c a t e C o m m a n d E v e n t s - D y n a m i c s ( P o i n t As P o i n t 3 d , B y V a l
V i e w As V i e w , B y V a l DrawMode As M s d D r a w i n g M o d e )
End S u b
398 I Chapter 18: Interface Essentials I
P r i v a t e Sub ILocateCommandEvents-LocateFailedO
I f SelElement I s Nothing = F a l s e Then
ActiveModel Reference.Unse1 e c t A l 1 Elements
Set SelElement = Nothing
End I f
S h ow C o mm a n d " CA P T e x t "
ShowPrompt " S e l e c t T e x t t o b e C a p i t a l i z e d "
End Sub
P r i v a t e Sub I L o c a t e C o m m a n d E v e n t s L o c a t e F i l t e F i l t e r ( B y V a 1
~
E l e m e n t As E l e m e n t , P o i n t As P o i n t 3 d , A c c e p t e d As B o o l e a n )
Accepted = False
I f Element.IsTextElement = T r u e Then
S e t Sel Element = Element
Accepted = True
ActiveModel Reference.SelectElement Element, True
S h owC omma n d " CAP T e x t "
ShowPrompt " C l i c k a g a i n t o c o n f i r m . . ."
End I f
End Sub
ILocateCommandEvents-LocateResetO
P r i v a t e Sub
CommandState.StartDefaultCommand
End Sub
P r i v a t e Sub ILocateCommandEvents-Start0
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 the 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.StartDefau1tCommand:
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 LCE-Text
class.
Sub t s t L C E - T e x t ( )
CommandState.StartLocate New LCELText
S h ow C omm a n d C A P Text
" "
Implements ILocateCommandEvents
Private SelElement As Element
End Sub
I Class Module Lifecycle I 401
P r i v a t e Sub ILocateCommandEvents-Cleanup()
End Sub
P r i v a t e Sub ILocateCommandEvents-Dynamics(Point As P o i n t 3 d . -
B y V a l V i e w As V i e w , B y V a l DrawMode As M s d D r a w i n g M o d e )
End Sub
P r i v a t e Sub ILocateCommandEvents-LocateFailedO
I f SelElement I s Nothing = F a l s e Then
ActiveModelReference.UnselectAllElements
Set SelElement = Nothing
End I f
S h ow C o mm a n d " CAP T e x t "
ShowPrompt " S e l e c t T e x t t o b e C a p i t a l i z e d "
End Sub
P r i v a t e Sub I L o c a t e C o m m a n d E v e n t s L o c a t e F i l t e F i l t e r ( B y V a 1 E l e m e n t As
E l e m e n t , P o i n t As P o i n t 3 d , A c c e p t e d As B o o l e a n )
Accepted = False
D i m e l e m T e x t As T e x t E l e m e n t
I f Element.IsTextElement = T r u e Then
S e t e l emText = Element
elemText.Redraw msdDrawingModeErase
elemText.Text = UCase(elemText.Text)
e l e m T e x t . R e d r a w msdDrawingModeNorma1
e l emText. R e w r i t e
ActiveModelReference.UnselectAllElements
CommandState.StartDefaultCommand
End I f
End Sub
P r i v a t e Sub I L o c a t e C o m m a n d E v e n t s L o c a t e R e s e t O
CommandState.StartDefaultCommand
End Sub
P r i v a t e Sub I L o c a t e C o m m a n d E v e n t s S t a r t ( )
End Sub
Here i s t h e code t h a t i n i t i a l i z e s t h e I n t e r f a c e O b j e c t .
402 I Chapter 18: Interface Essentials I
Sub tstLCE-Text20
CommandState.StartLocate New LCELTextP
S h ow C omm a n d C A P Text" "
Lm o t i on
Implements ILocateCommandEvents
P r i v a t e Sub I L o c a t e C o m r n a n d E v e n t s A c c e p t ( B y V a 1 E l e m e n t As E l e m e n t ,
Point As Point3d, ByVal View A s View)
Dim txtElem As TextElement
Dim rotMatrix As Matrix3d
dblDistance = Point3dDistance(Point, pt3StPoint)
I Class Module Lifecycle I
Set txtElem = CreateTextElementl(selElem, R o u n d ( d b l D i s t a n c e , -
31, P o i n t , r o t M a t r i x )
ActiveModel Reference.AddElement t x t E l e m
t x t E l em. R e w r i t e
t x t E l em. Redraw
CommandState.StartLocate Me
End Sub
P r i v a t e Sub ILocateCommandEvents-Cleanup()
End Sub
P r i v a t e Sub I L o c a t e C o m m a n d E v e n t s D y n a m i c s ( P o i n t As P o i n t 3 d , ~
B y V a l V i e w As V i e w , B y V a l DrawMode As M s d D r a w i n g M o d e )
D i m t m p T x t E l e m As T e x t E l e m e n t
D i m r o t M a t r i x As M a t r i x 3 d
dblDistance = Point3dDistance(Point, pt3StPoint)
Set tmpTxtElem = CreateTextElementl(se1 Elem, Round(dblDistance, 3 ) , -
Point, rotMatrix)
t m p T x t E l e m . R e d r a w DrawMode
ShowPrompt " S e l e c t D i s t a n c e P o i n t : "
End Sub
P r i v a t e Sub I L o c a t e C o m m a n d E v e n t s L o c a t e F a i l e d O
CommandState.StartLocate Me
End Sub
P r i v a t e Sub ILocateCommandEvents-LocateFilter(ByVa1 -
E l e m e n t As E l e m e n t , P o i n t As P o i n t 3 d ,
A c c e p t e d As Boo1 e a n )
S e t s e l E l em = Element
pt3StPoint = Point
CommandState.StartDynamics
End Sub
P r i v a t e Sub I L o c a t e C o m m a n d E v e n t s L o c a t e R e s e t O
CommandState.StartDefaultCommand
End Sub
P r i v a t e Sub ILocateCommandEvents-Start0
S h ow C omma n d T e x t D is t a n c e
" "
Sub t s t L C E - D i stanceText( )
CommandState.StartLocate New L C E L D i s t a n c e T e x t
End Sub
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.
Implements ILocateCommandEvents
P r i v a t e S e l E l e m e n t As E l e m e n t
D i m myLC A s L o c a t e c r i t e r i a
End Sub
End Sub
End Sub
ActiveModelReference.UnselectAllElements
Set SelElement = Nothing
End I f
S h ow C o mm a n d " CAP T e x t "
ShowPrompt " S e l e c t T e x t t o b e C a p i t a l i z e d "
End Sub
P r i v a t e Sub I L o c a t e C o m m a n d E v e n t s - L o c a t e F i l t e r ( B y V a 1 E l e m e n t As -
E l e m e n t , P o i n t As P o i n t 3 d , -
A c c e p t e d As B o o l e a n )
D i m e l e m T e x t As T e x t E l e m e n t
S e t e l emText = Element
elemText.Redraw msdDrawingModeErase
elemText.Text = UCase(elemText.Text)
e l e m T e x t . R e d r a w msdDrawingModeNorma1
e l emText. R e w r i t e
ActiveModel Reference.UnselectAl1 Elements
CommandState.StartDefaultCommand
End Sub
P r i v a t e Sub I L o c a t e C o m m a n d E v e n t s L o c a t e R e s e t O
CommandState.StartDefaultCommand
End Sub
P r i v a t e Sub I L o c a t e C o m m a n d E v e n t s - S t a r t 0
S e t myLC = CommandState.CreateLocateCriteria(True)
my LC. E x c l u d e A l 1 T y p e s
myLC.IncludeType (msdElementTypeText)
CommandState.SetLocateCriteria myLC
End Sub
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:
El Private Sub IPrimitiveCommandEvents-Cleanup()
I Class Module Lifecycle I 407
PCE-Li n eTest
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 IPrimitiveCommandEvents
D i m p t 3 B a s e P o i n t As P o i n t 3 d
D i m b o o l S e t As B o o l e a n
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s _ C l e a n u p O
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-DataPoint(Point As P o i n t 3 d . -
ByVal View As View)
If boolSet = F a l s e Then
pt3BasePoint = Point
CommandState.StartDynamics
boolSet = True
Else
CommandState.StartDefau1tCommand
End I f
End Sub
408 I Chapter 18: Interface Essentials I
P r i v a t e Sub IPrimitiveCommandEvents-Dynamics(Point As P o i n t 3 d , -
B y V a l V i e w As V i e w , B y V a l DrawMode As M s d D r a w i n g M o d e )
D i m m y L i n e E l e m As L i n e E l e m e n t
S e t rnyLineElern = CreateLineElernentZ(Nothing. pt3BasePoint. P o i n t )
m y L i n e E l e m . Redraw DrawMode
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s K e y i n ( B y V a 1 K e y i n As S t r i n g )
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s R e s e t ( )
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-Start0
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 time-
consuming. 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 P1 a c e l i n e ( )
CommandState.StartPrimitive New P C E - L i n e T e s t
End Sub
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.
PCE-RecTest
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 PCE-RecTest.
Implements IPrimitiveCommandEvents
D i m p t 3 B a s e P o i n t As P o i n t 3 d
D i m b o o l S e t As B o o l e a n
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s C l e a n u p O
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s - D a t a P o i n t ( P o i n t As P o i n t 3 d . -
ByVal View As View)
If boolSet = F a l s e Then
pt3BasePoint = Point
CommandState.StartDynamics
boolSet = True
Else
CommandState.StartDefau1tCommand
End I f
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s D y n a m i c s ( P o i n t As P o i n t 3 d , ~
B y V a l V i e w A s V i e w , B y V a l DrawMode A s M s d D r a w i n g M o d e )
D i m p t 3 R e c P o i n t s ( O To 3 ) A s P o i n t 3 d
D i m myShapeElem A s S h a p e E l e m e n t
pt3RecPoints(O) = pt3BasePoint
pt3RecPoints(l).X = P0int.X
pt3RecPoints(l).Y = pt3BasePoint.Y
410 I Chapter 18: Interface Essentials I
pt3RecPoints(2) = Point
pt3RecPoints(3).X = pt3BasePoint.X
p t 3 Rec Po in t s ( 3 .Y = P o i n t .Y
S e t myShapeElem = CreateShapeElementl(Nothing, p t 3 R e c P o i n t s )
m y S h a p e E l e m . R e d r a w DrawMode
End Sub
P r i v a t e Sub I P r i r n i t i v e C o r n r n a n d E v e n t s - K e y i n ( B y V a 1 K e y i n As S t r i n g )
End Sub
P r i v a t e Sub I P r i r n i t i v e C o r n r n a n d E v e n t s - R e s e t 0
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s S t a r t O
End Sub
Notice how the X and Y elements of each shape vertex is derived from
the base point and the current cursor point.
Sub P1 a c e R e c ( )
C o m m a n d S t a t e . S t a r t P r i r n i t i v e New PCE-RecTest
End Sub
PCE-CircTest
The CircleTest class draws a circle with a center at the first selected point
to the cursor.
Implements IPrimitiveCommandEvents
D i m p t 3 B a s e P o i n t As P o i n t 3 d
D i m b o o l S e t As Boolean
I Class Module Lifecycle I 411
P r i v a t e Sub IPrimitiveCommandEvents-Cleanup0
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-DataPoint(Point As P o i n t 3 d . -
B y V a l V i e w As V i e w )
If b o o l S e t = F a l s e Then
pt3BasePoint = Point
CommandState.StartDynamics
boolSet = True
Else
CommandState.StartDefaultCommand
End If
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-Dynamics(Point As P o i n t 3 d . -
B y V a l V i e w As V i e w , B y V a l DrawMode A s MsdDrawingMode)
D i m myCi r c l e A s E l 1 i p s e E l e m e n t
D i m r o t M a t r i x As M a t r i x 3 d
D i m d b l Radius As Double
dblRadius = Point3dDistance(pt3BasePoint, Point)
S e t m y c i r c l e = CreateEllipseElementZ(Nothing, pt3BasePoint,
dbl Radius, dblRadius, r o t M a t r i x )
myCi r c l e . Redraw DrawMode
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-Keyin(ByVa1 K e y i n As S t r i n g )
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s R e s e t O
CommandState.StartDefaultCommand
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s S t a r t O
End Sub
This example makes use of the Reset event. If the user resets the
command, we exit the interface object by calling S t a r t D e f a u l tCommand.
Sub P1 a c e C i r c ( 1
412 I Chapter 18: Interface Essentials I
C o m m a n d S t a t e . S t a r t P r i m i t i v e New P C E - C i r c T e s t
End Sub
PCE-Pol yTest
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. Lets begin with the class module:
Implements IPrimitiveCommandEvents
D i m p t 3 B a s e P o i n t As P o i n t 3 d
D i m b o o l S e t As Boolean
P u b l i c V e r t i c e s As L o n g
P r i v a t e Sub IPrimitiveCommandEvents-Cleanup0
End Sub
P r i v a t e Sub IPrirnitiveCommandEventsDataPoint(Point(P0int A s P o i n t 3 d ,
B y V a l V i e w As V i e w )
If boolSet = F a l s e Then
pt3BasePoin t = Point
CommandState.StartDynamics
boolSet = True
Else
CommandState.StartDefaultCommand
End I f
End Sub
I Class Module Lifecycle I 413
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s - D y n a m i c s ( P o i n t As P o i n t 3 d . -
B y V a l V i e w As V i e w , B y V a l DrawMode As M s d D r a w i n g M o d e )
D i m p t 3 P o l y P o i n t s O As P o i n t 3 d
ReDim p t 3 P o l y P o i n t s ( O To V e r t i c e s - 1) A s P o i n t 3 d
D i m myShapeElem A s S h a p e E l e m e n t
D i m I As L o n g
D i m d b l B a s e A n g l e As D o u b l e
dblBaseAngle = Atn((P0int.Y - pt3BasePoint.Y) / ~
(P0int.X - pt3BasePoint.X))
For I = 0 To V e r t i c i e s - 1
p t 3 P o l y P o i n t s ( I ) = Point3dAddAngleDistance(pt3BasePoint, ~
Point3dDistance(pt3BasePoint, P o i n t ) , 0)
Next I
S e t myShapeElem = CreateShapeElementl(Nothing, p t 3 P o l y P o i n t s )
myShapeEl em. Redraw DrawMode
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-Keyin(ByVa1 K e y i n As S t r i n g )
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s R e s e t ( )
CommandState.StartDefaultCommand
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s S t a r t ( )
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 P1 a c e P o l y ( 1
D i m m y P o l y T e s t A s New P C E - P o l y T e s t
myPolyTest.Verticies = 8
CommandState.StartPrimitive m y P o l y T e s t
End Sub
PCE-PointStringTest
Each PrimitiveCommandEvent interface example we have used up to
this point has been based on the users 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
D i m p t 3 B a s e P o i n t As P o i n t 3 d
D i m p t 3 P o i n t s O As P o i n t 3 d
D i m b o o l S e t As Boolean
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s C l e a n u p O
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-DataPoint(Point A s P o i n t 3 d , -
B y V a l V i e w As V i e w )
If boolSet = F a l s e Then
I Class Module Lifecycle I 415
pt3BasePoint = Point
pt3Points(O) = Point
ReDim P r e s e r v e pt3Points(UBound(pt3Points) + 1)
CommandState.StartDynamics
boolSet = True
Else
pt3Points(UBound(pt3Points)) = P o i n t
ReDim P r e s e r v e pt3Points(UBound(pt3Points) + 1)
End If
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-Dynamics(Point As P o i n t 3 d . -
B y V a l V i e w A s V i e w , B y V a l DrawMode A s M s d D r a w i n g M o d e )
D i m m y p o i n t s t r i n g As P o i n t S t r i n g E l e m e n t
pt3Points(UBound(pt3Points)) = P o i n t
S e t m y p o i n t s t r i n g = CreatePointStringElementl( -
Nothing, pt3Points, False)
m y p o i n t s t r i n g . Redraw DrawMode
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s K e y i n ( B y V a 1 K e y i n As S t r i n g )
S e l e c t Case UCase( K e y i n )
Case P LC LO S E
D i m m y P o i n t S t r i n g As P o i n t S t r i n g E l e m e n t
pt3Points(UBound(pt3Points)) = pt3BasePoint
Set mypointstring = CreatePointStringElementl(Nothing, ~
pt3Points, False)
ActiveModelReference.AddElement mypointstring
CommandState.StartDefaultCommand
End S e l e c t
End Sub
IPrimitiveCommandEvents-Reset0
P r i v a t e Sub
CommandState.StartDefaultCommand
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s S t a r t ( )
ReDim p t 3 P o i n t s ( 0 )
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 Essentials I
for example. This may work or may be necessary in some circumstances,
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
needed. We change 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.
P r i v a t e Sub IPrimitiveCommandEventsKeyin(ByVa1 K e y i n As S t r i n g )
S e l e c t Case U C a s e ( K e y i n 1
C ase P LC LDS E
D i m m y p o i n t s t r i n g As P o i n t S t r i n g E l e m e n t
pt3Points(UBound(pt3Points)) = pt3BasePoint
Set mypointstring = CreatePointStringElementl(Nothing. ~
pt3Points. False)
A c t i veModel Reference.AddE1 ement myPoi n t S t r i n g
CommandState.StartDefaultCommand
End S e l e c t
End S u b
Here is the code in the Keyin event. If the user enters plclose,PlClose:
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 pt3Points 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
pIcIose
Multiple points are selected. As the points are selected, we are creating a
Pointstring 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 Pointstring 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.
S u b PlacePointString()
CommandState.StartPrimitive New P C E - P o i n t S t r i n g T e s t , T r u e
End S u b
PCE-Li neTest2
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 Chapter 18: Interface Essentials I
circles at each vertex of these lengths. Lets begin with the desired
interface and then we will discuss the code.
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:
Implements IPrimitiveCommandEvents
D i m p t 3 B a s e P o i n t As P o i n t 3 d
D i m b o o l S e t As B o o l e a n
Pub1 ic 1 n g D i v i s i o n s As L o n g
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s C l e a n u p O
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-DataPoint(Point As P o i n t 3 d , -
B y V a l V i e w As V i e w )
If boolSet = F a l s e Then
pt3BasePoin t = Point
CommandState.StartDynamics
boolSet = True
Else
D i m p t 3 E n d P o i n t As P o i n t 3 d
D i m d b l L i n e A n g l e As D o u b l e
D i m d b l S e g D i s t As D o u b l e
D i m D i v P o i n t s O As P o i n t 3 d
I Class Module Lifecycle I 419
D i m I As Long
ReDim D i v P o i n t s ( 0 T o 1 n g D i v i s i o n s - 2 ) As P o i n t 3 d
pt3EndPoint = Point
dblLineAngle = Atn((pt3EndPoint.Y - pt3BasePoint.Y) / ~
(pt3EndPoint.X - pt3BasePoint.X))
dblSegDist = Point3dDistanceXY(pt3BasePoint. pt3EndPoint) / ~
lngDivisions
F o r I = L B o u n d ( D i v P o i n t s ) To U B o u n d ( D i v P o i n t s )
DivPoints(1) =
Point3dAddAngleDistance(pt3BasePoint, ~
P r i v a t e Sub D r a w C i r c l e ( C e n P t As P o i n t 3 d , R a d i u s As D o u b l e )
D i m m y E l l i p s e As E l l i p s e E l e m e n t
D i m r o t M a t r i x As M a t r i x 3 d
Set rnyEllipse = CreateEllipseElementZ(Nothing, CenPt, R a d i u s , R a d i u s , -
rotMatrix)
ActiveModel Reference.AddElement myEl1 i p s e
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-Dynamics(Point As P o i n t 3 d . -
B y V a l V i e w As V i e w , B y V a l DrawMode As MsdDrawingMode)
D i m m y L i n e E l e m As L i n e E l e m e n t
S e t rnyLineElem = CreateLineElement2(Nothing, pt3BasePoint. P o i n t )
m y L i n e E l em. Redraw DrawMode
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s K e y i n ( B y V a 1 K e y i n As S t r i n g )
End Sub
IPrimitiveCommandEvents-Reset0
P r i v a t e Sub
CommandState.StartDefaultCommand
End Sub
420 I Chapter 18: Interface Essentials I
P r i v a t e Sub IPrimitiveCommandEvents-Start0
End Sub
Sub P 1 aceLinePB( )
D i m m y L i n e T e s t 2 As New PCELLineTest.2
myLineTest2.lngDivisions = 12
Commandstate. S t a r t P r i m i t i v e myLi n e T e s t 2
End Sub
The procedure P1 a c e l i ne2A divides the selected points into four equal
segments. P1 a c e l i 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
PCELTestLine2 class in the next example.
PCELTestLine2 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 PCELTestLine2, 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 42 1
S u b P1 a c e L i ne3A( 1
D i m m y D i v P o i n t s 0 As P o i n t 3 d
D i m m y L i n e T e s t 3 A s New P C E L L i n e T e s t 3
D i m I As Long
myLineTest3.1 n g D i v i s i o n s = 12
CommandState.StartPrimitive m y L i n e T e s t 3
Whi 1 e m y L i n e T e s t 3 . C l a s s C o m p l e t e = Fa1 s e
DoEvents
Wend
myDivPoints = myLineTest3.DivPts
For I = L B o u n d ( m y D i v P 0 i n t s ) To U B o u n d ( m y D i v P o i n t s )
D i m m y E l 1 i p s e As E l 1 i p s e E l e m e n t
D i m r o t M a t r i x As M a t r i x 3 d
Set m y E l l i p s e = C r e a t e E l l i p s e E l e r n e n t Z ( N o t h i n g , rnyDivPoints(1)
0.25, 0.25, rotMatrix)
ActiveModel Reference.AddElement myEl1 i p s e
Next I
End S u b
D i m m y D i v P o i n t s 0 As P o i n t 3 d
D i m m y L i n e T e s t 3 A s New P C E L L i n e T e s t 3
D i m I As Long
myLineTest3.1 n g D i v i s i o n s = 12
Commandstate. S t a r t P r i m i t i v e myLi n e T e s t 3
5 We get the points that had been created by the selection of the two
points.
myDivPoints = myLineTest3.DivPts
For I = L B o u n d ( m y D i v P o i n t s ) To U B o u n d ( m y D i v P 0 i n t s )
D i m m y E l 1 ip s e As E l 1 ip s e E l e m e n t
D i m r o t M a t r i x As M a t r i x 3 d
Set m y E l l i p s e = CreateEllipseElernentZ(Nothing, rnyDivPoints(1).
0.25, 0.25, r o t M a t r i x )
ActiveModel Reference.AddElement myEl1i p s e
Next I
S u b P1 aceLine3B( )
D i m m y D i v P o i n t s 0 As P o i n t 3 d
D i m m y L i n e T e s t 3 As New P C E L L i n e T e s t 3
D i m I As L o n g
myLineTest3.lngDivisions = 1 6
Commandstate. S t a r t P r i m i t i v e myLi n e T e s t 3
While myLineTest3.ClassComplete = False
DoEvents
Wend
myDivPoints = myLineTest3.DivPts
For I = L B o u n d ( m y D i v P o i n t s ) To U B o u n d ( m y D i v P 0 i n t s ) - 1
I Class Module Lifecycle I 423
D i m m y L i n e E l e m As L i n e E l e m e n t
Set myLineElern = CreateLineElernentZ(Nothing, rnyDivPoints(I),
myDivPoints(1 + 1))
ActiveModel Reference.AddElement myLineElem
Next I
End S u b
Follow through the code in P1 a c e l i ne3B. What is it doing with the points
returned by the PCE-LineTest3 Class?
P1 a c e l i ne3B is drawing lines for each segment identified by the
PCE-LineTest3 Class.
Lets look at one more example:
S u b P1 a c e L i ne3C( 1
D i m m y D i v P o i n t s 0 As P o i n t 3 d
D i m m y L i n e T e s t 3 As New P C E L L i n e T e s t 3
D i m I As L o n g
D i m L i n e B a s e P t As P o i n t 3 d
myLineTest3.1 n g D i v i s i o n s = 16
CommandState.StartPrimitive m y L i n e T e s t 3
Whi 1 e m y L i n e T e s t 3 . C l a s s C o m p l e t e = Fa1 s e
DoEvents
Wend
myDivPoints = myLineTest3.DivPts
LineBasePt.X = 3: LineBasePt.Y = 4: LineBasePt.Z = 5
For I = L B o u n d ( m y D i v P o i n t s ) To U B o u n d ( m y D i v P o i n t s )
D i m m y L i n e E l e m As L i n e E l e m e n t
S e t myLineElem = CreateLineElement2(Nothing, L i n e B a s e P t , -
myDivPoints(1))
ActiveModel Reference.AddElement myLineElem
Next I
End S u b
What does this code do? Of course, we are using the PCELLineTest3
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
Whi 1 e ... Wend structure to allow the user to select two points. After the
two points are selected, the value of ClassComplete 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. Lets take a
look at the code behind the class module now.
Implements IPrimitiveCommandEvents
D i m p t 3 B a s e P o i n t As P o i n t 3 d
D i m b o o l S e t As B o o l e a n
P u b l ic 1 n g D i v i s i o n s As L o n g
P u b l i c D i v P t s As V a r i a n t
P u b l i c C1 assComD1 e t e As Boo1 e a n
P r i v a t e Sub IPrimitiveCommandEvents-Cleanup0
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s D a t a P o i n t ( P o i n t ( P o i n t As P o i n t 3 d ,
B y V a l V i e w As V i e w )
If boolSet = F a l s e Then
pt3BasePoin t = Point
CommandState.StartDynamics
boolSet = True
Else
D i m p t 3 E n d P o i n t As P o i n t 3 d
D i m d b l L i n e A n g l e As D o u b l e
D i m d b l S e g D i s t As D o u b l e
D i m D i v P o i n t s O As P o i n t 3 d
D i m I As L o n g
ReDim D i v P o i n t s ( 0 To I n g D i v i s i o n s ) As P o i n t 3 d
DivPoints(0) = pt3BasePoint
DivPoints(UBound(DivPoints) = Point
I Class Module Lifecycle I 425
pt3EndPoint = Point
dblLineAngle = Atn((pt3EndPoint.Y - pt3BasePoint.Y) / -
(pt3EndPoint.X - pt3BasePoint.X))
dblSegDist = Point3dDistanceXY(pt3BasePoint, p t 3 E n d P o i n t ) / -
1ngDivisions
For I = LBound(DivPoints) + 1 To U B o u n d ( D i v P o i n t s ) - 1
DivPoints(1) = Point3dAddAngleDistance(pt3BasePoint, -
dblLineAngle, dblSegDist * (I), 0)
Next I
DivPts = DivPoints
C1 assCompl e t e = True
CornmandState.StartDefau1tCommand
End I f
End Sub
P r i v a t e S u b I P r i m i t i v e C o m m a n d E v e n t s D y n a m i c s ( P o i n t As P o i n t 3 d ,
~
B y V a l V i e w As V i e w , B y V a l DrawMode As M s d D r a w i n g M o d e )
D i m myLineElem As LineElement
S e t myLineElem = CreateLineElementZ(Nothing, pt3BasePoint, ~
Point)
m y L i n e E l em. Redraw DrawMode
End S u b
P r i v a t e Sub IPrimitiveCommandEvents-Keyin(ByVa1 K e y i n As S t r i n g )
End Sub
P r i v a t e Sub IPrimitiveCommandEventsReset()
CommandState.StartDefaultCommand
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-Start0
End S u b
Sub P l a c e L i n e 3 A ( 1
D i m m y D i v P o i n t s 0 As P o i n t 3 d
D i m m y L i n e T e s t 3 As New P C E L L i n e T e s t 3
D i m I As L o n g
myLineTest3.lngDivisions = 12
Commandstate. S t a r t P r i m i t i v e myLi n e T e s t 3
While myLineTest3.ClassComplete = False
DoEvents
Wend
myDivPoints = myLineTest3.DivPts
For I = L B o u n d ( m y D i v P o i n t s ) To U B o u n d ( m y D i v P o i n t s )
D i m m y E l 1 ip s e As E l 1 ip s e E l e m e n t
D i m r o t M a t r i x As M a t r i x 3 d
Set rnyEllipse = CreateEllipseElementZ(Nothing, myDivPoints(I), -
0.25, 0.25. rotMatrix)
ActiveModel Reference.AddE1 ement myEl1 i p s e
Next I
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-Dynamics(Point As P o i n t 3 d . -
B y V a l V i e w A s V i e w , B y V a l DrawMode A s M s d D r a w i n g M o d e )
D i m myLineElem As LineElement
Set myLineElem = CreateLineElement2(Nothing, pt3BasePoint. P o i n t )
m y L i n e E l em. Redraw DrawMode
End Sub
PCE-LineTest
Implements IPrimitiveCommandEvents
D i m p t 3 B a s e P o i n t As P o i n t 3 d
D i m b o o l S e t As B o o l e a n
P r i v a t e Sub IPrimitiveCommandEvents-Cleanup0
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-DataPoint(Point As P o i n t 3 d . -
ByVal View As View)
If boolSet = F a l s e Then
pt3BasePoint = Point
CommandState.StartDynamics
boolSet = True
428 I Chapter 18: Interface Essentials I
Else
CommandState.StartDefaultCommand
End I f
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-Dynamics(Point As P o i n t 3 d , -
B y V a l V i e w As V i e w , B y V a l DrawMode As M s d D r a w i n g M o d e )
D i m m y L i n e E l e m As L i n e E l e m e n t
Set myLineElem = CreateLineElement2(Nothing, pt3BasePoint. P o i n t )
m y L i n e E l e m . Redraw DrawMode
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s K e y i n ( B y V a 1 K e y i n As S t r i n g )
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-Reset0
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-Start0
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 lets look at the difference between PCE-LineTest and
PCE-LineTest4.
PCE-LineTest4
Implements IPrimitiveCommandEvents
D i m p t 3 B a s e P o i n t As P o i n t 3 d
D i m b o o l S e t As Boolean
D i m m y L i n e E l e m As L i n e E l e m e n t
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s C l e a n u p O
End Sub
P r i v a t e Sub IPrirnitiveCommandEventsDataPoint(Point(P0int A s P o i n t 3 d , ~
B y V a l V i e w As V i e w )
If boolSet = F a l s e Then
I Class Module Lifecycle I 429
pt3BasePoint = Point
Set myLineElem = CreateLineElement2(Nothing, Point, Point)
CommandState.StartDynamics
boolSet = True
Else
CommandState.StartDefau1tCommand
End If
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s D y n a m i c s ( P o i n t As P o i n t 3 d , ~
B y V a l V i e w A s V i e w , B y V a l DrawMode A s M s d D r a w i n g M o d e )
m y L i n e E l em. V e r t e x ( 1 ) = Point
m y L i n e E l em. Redraw DrawMode
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s K e y i n ( B y V a 1 K e y i n As S t r i n g )
End Sub
P r i v a t e Sub IPrimitiveCommandEvents-Reset0
End Sub
P r i v a t e Sub I P r i m i t i v e C o m m a n d E v e n t s S t a r t ( )
End Sub
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 applicationsbecome more interactive.
19 Using MicroStat ions
Built-In User Forms
In this Chapter:
Declaring Microstation user form functions
The FileOpen dialog
The Filecreate dialog
The FileCreateFromSeed dialog
The OpenAlert dialog
The OpenInfoBox dialog
DECLARING
MICROSTATION
USERFORMFUNCTIONS
Access Microstations 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.
43 1
432 I Chapter 19: Using MicroStation'sBuilt-In User Forms I
Decl a r e F u n c t i o n mdl D i a1 og-openAl e r t L i b -
" s t d m d l b l t i n . d l 1 " (ByVal s t r i n g P As S t r i n g ) As Long
Here is the declaration for the OpenAlert dialog box. Let's break up the
declaration into its individual parts:
f i l e N a m e As S t r i n g , ByVal r F i l e H As Long, B y V a l -
ByVal f i l t e r s t r i n g As S t r i n g , -
ByVal d e f a u l t D i r e c t o r y As S t r i n g , -
ByVal t i t l e s t r i n g As S t r i n g ) As Long
ByVal s u g g e s t e d F i 1eName As S t r i n g , -
ByVal f i l t e r s t r i n g As S t r i n g , -
ByVal d e f a u l t D i r e c t o r y As S t r i n g , -
ByVal t i t l e s t r i n g As S t r i n g ) As Long
Our first example prompts the user to select a file. We supply a file
extension, a default directory, and a dialog title.
S u b T e s t F i 1 eOpenA( 1
D i m s t r F N a m e As S t r i n g
D i m l n g f h a n d l e A s Long
D i m l n g r i d A s Long
D i m r e t V a l A s Long
strFName = Space(255)
retVal = mdl D i a l o g - f i l e O p e n ( s t r F N a m e , 1n g f h a n d l e , 1n g r i d , ~
I, I,
, " * .d g n
, " C : \ M i c r o S t a t io n VBA" ,
" ~
"Open F i 1 e " )
S e l e c t Case r e t V a l
Case 0 'Open
strFNarne = L e f t ( s t r F N a r n e , I n S t r ( 1 , strFName, C h r ( 0 ) ) - 1)
MsgBox " F i l e S e l e c t e d : " & vbCr & strFName
Case 1 ' C a n c e l
MsgBox "No F i 1 e S e l e c t e d . "
End S e l e c t
End S u b
434 I Chapter 19: Using MicroStations Built-In User Forms I
When the procedure T e s t F i 1 eOpenA is executed, we see the following
dialog box:
From this dialog we can see how many 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?
S e l e c t Case r e t V a l
Case 0 Open
Case 1 C a n c e l
End S e l e c t
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
strFName = Space(255)
The Space function fills the variable with the number of spaces specified.
If we provide a space-buffered variable to the OpenFi 1 e 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 = L e f t ( s t r F N a m e , I n S t r ( 1 , strFName, C h r ( 0 ) ) - 1)
MsgBox " F i l e S e l e c t e d : " & v b C r & s t r F N a m e
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.
It 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 Test Fi 1 eOpen A. Only one change has been
made:
Sub T e s t F i 1 eOpenB( 1
D i m strFName As S t r i n g
D i m l n g f h a n d l e As Long
D i m l n g r i d As Long
D i m r e t V a l As Long
436 I Chapter 19: Using MicroStation'sBuilt-In User Forms I
strFName = Space(255)
retVal = mdl D i a l o g - f i 1 e O p e n ( s t r F N a r n e , 1 n g f h a n d l e , 1 n g r i d , -
" t e st 4 . dgn " , " *.d g n " , " C : \ M i c r oS t a t io n V B A " , "Open F i 1 e " )
S e l e c t Case r e t V a l
Case 0 ' O p e n
strFName = L e f t ( s t r F N a m e , I n S t r ( 1 , strFNarne, C h r ( 0 ) ) - 1)
MsgBox " F i 1 e S e l e c t e d : " & vbCr & strFName
Case 1 ' C a n c e l
MsgBox "No F i 1 e S e l e c t e d . "
End S e l e c t
End Sub
Our previous example used a file extension, also called a file filter, of
"*.dgn? Microstation understands that this file extension is a
"MicroStation DGN File" and shows this in the "Files of type" combo
box. TestFileOpenC uses a file filter of "*.xis': This displays Microsoft
Excel files in the dialog box.
Sub T e s t F i 1 eOpenC( 1
D i m strFName As S t r i n g
D i m l n g f h a n d l e A s Long
D i m l n g r i d As Long
D i m r e t V a l As Long
strFName = Space(255)
retVal = mdlDialog-fileOpen(strFName, lngfhandle, lngrid, "", -
" *.x l s " , " C : \ M i c r o S t a t ion VBA" , "Open F i 1 e " )
S e l e c t Case r e t V a l
Case 0 'Open
strFName = L e f t ( s t r F N a r n e , I n S t r ( 1 . strFNarne, C h r ( 0 ) ) ~ 1)
MsgBox " F i l e S e l e c t e d : " & vbCr & strFName
Case 1 ' C a n c e l
MsgBox " N o F i 1 e S e l e c t e d . "
'User h i t t h e Cancel B u t t o n
End S e l e c t
End Sub
Let's look at another example that displays more than one type of file.
Sub T e s t F i 1 eOpenD( )
D i m s t r F N a m e As S t r i n g
D i m l n g f h a n d l e As L o n g
D i m l n g r i d As L o n g
D i m r e t V a l As L o n g
strFName = Space(255)
retVal = mdl D i a l og-fileOpen(strFName, 1 n g f h a n d l e , l n g r i d , " " ,-
" * .x 1 s ; * .md b ; * . d b f " , " C : \ M i c r o St a t ion V B A " , "Open F i1 e " )
S e l e c t Case r e t V a l
Case 0 ' O p e n
strFName = L e f t ( s t r F N a m e , I n S t r ( 1 , strFName, C h r ( 0 ) ) - 1)
MsgBox " F i 1 e S e l e c t e d : " & vbCr & strFName
Case 1 ' C a n c e l
MsgBox "No F i 1 e S e l e c t e d . "
' U s e r h i t t h e Cancel B u t t o n
End S e l e c t
End Sub
S u b TestFi 1 eOpenE( 1
D i m s t r F N a m e As S t r i n g
D i m l n g f h a n d l e As L o n g
D i m l n g r i d As L o n g
D i m r e t V a l As Long
strFName = Space(255)
retVal = mdl D i a l o g - f i l e O p e n ( s t r F N a m e , 1n g f h a n d l e , 1n g r i d , -
I, I,
, "*.dgn", "C:\MicroStation VBA", "Open F i l e " )
S e l e c t Case r e t V a l
Case 0 ' O p e n
strFNarne = L e f t ( s t r F N a r n e , I n S t r ( 1 , strFName, C h r ( 0 ) ) - 1)
D i m myDesFile As D e s i g n F i l e
Set myDesFile = OpenDesignFileForProgram(strFNarne, True)
MsgBox " D o s o m e t h i n g w i t h t h i s f i l e . "
myDesFi l e . C 1 o s e
Case 1 ' C a n c e l
MsgBox "No F i 1 e S e l e c t e d . "
End S e l e c t
End S u b
f i l e N a m e As S t r i n g , B y V a l r F i l e H As Long, -
ByVal r e s o u r c e I d As Long, -
ByVal s u g g e s t e d F i 1eName As S t r i n g , -
ByVal f i l t e r s t r i n g As S t r i n g , -
ByVal d e f a u l t D i r e c t o r y As S t r i n g , -
440 I Chapter 1 9 Using MicroStation's Built-In User Forms I
B y V a l t i t l e s t r i n g As S t r i n g ) -
As Long
Let's take a look on how to use the function.
Sub T e s t F i 1 e C r e a t e A ( )
D i m s t r F N a m e As S t r i n g
D i m l n g f h a n d l e As L o n g
D i m l n g r i d As L o n g
D i m r e t V a l As L o n g
strFName = Space(255)
retVal = mdlDialog-fileCreate(strFName, l n g f h a n d l e , l n g r i d ,
11 11
, " *.d g n " , " C : \ M i c r o S t a t io n V B A " , " C r e a t e F i 1 e A " 1
S e l e c t Case r e t V a l
Case 0 ' O p e n
strFName = L e f t ( s t r F N a m e , I n S t r ( 1 , strFName, C h r ( 0 ) ) ~ 1)
MsgBox " F i 1 e S e l e c t e d : " & vbCr & strFName
Case 1 ' C a n c e l
MsgBox "No F i 1 e S e l e c t e d . "
' U s e r h i t t h e Cancel B u t t o n
End S e l e c t
End Sub
The Filecreate dialog has the same look and feel as the FileOpen dialog
box.
I Declaring Microstation User Form Functions I 441
"stdmdlbltin.dl1" (ByVal f i l e N a m e As S t r i n g , -
ByVal s u g g e s t e d F i 1eName As S t r i n g , -
ByVal f i l t e r s t r i n g As S t r i n g , -
ByVal d e f a u l t D i r e c t o r y As S t r i n g , -
ByVal t i t l e s t r i n g As S t r i n g , -
ByVal s e e d F i l e As S t r i n g , -
ByVal s e e d D i r e c t o r y As S t r i n g , -
ByVal s e e d F i l t e r As S t r i n g ) As Long
Let's test the CreateFromSeed function now
S u b T e s t F i 1 eCreateFromSeedA()
D i m strFName As S t r i n g
D i m l n g f h a n d l e A s Long
D i m l n g r i d A s Long
D i m r e t V a l A s Long
D i m s t r S e e d F i l e As S t r i n g
D i m s t r S e e d D i r As S t r i n g
D i m s t r S e e d F i l t e r As S t r i n g
strFName = Space(255)
strSeedFile = "seed2d.dgn"
442 I Chapter 19: Using MicroStation'sBuilt-In User Forms I
strSeedDir = -
"Data\Documents\Bentley\Workspace\System\Seed\"
strSeedFilter = "*.dgn"
retVal = mdlDialogpfileCreateFromSeed(strFName, l n g f h a n d l e ,
" " ,
1 n g r id , " *. d g n " , " C : \ M i c r o S t a t io n VBA" ,
" C r e a t e F i 1e f r o m S e e d " , s t r S e e d F i 1e , s t r S e e d D i r , -
strSeedFi1t e r )
S e l e c t Case r e t V a l
Case 0 'Open
strFName = L e f t ( s t r F N a m e , I n S t r ( 1 , strFName, C h r ( 0 ) ) - 1)
MsgBox " F i 1 e S e l e c t e d : " & vbCr & strFName
Case 1 ' C a n c e l
MsgBox "No F i 1 e S e l e c t e d . "
'User h i t t h e Cancel B u t t o n
End S e l e c t
End Sub
S u b TestOpenAl ertA( )
D i m r e t V a l As Long
retVal = mdlDialog-openAlert("Standard M e s s a g e Box")
S e l e c t Case r e t V a l
Case 3 ' O K
MsgBox " U s e r c l i c k e d ' O K " '
Case 4 ' C a n c e l
MsgBox " U s e r c l i c k e d ' C a n c e l "'
End S e l e c t
End S u b
s t r i n g P As S t r i n g ) As Long
Sub TestOpenInfoBoxO
D i m r e t V a l As Long
444 I Chapter 19: Using MicroStation'sBuilt-In User Forms I
retVal = mdlDialog-openInfoBox("This i s a t e s t . " )
S e l e c t Case r e t V a l
Case 3 ' O K
MsgBox " U s e r c l i c k e d 'OK"'
End S e l e c t
End S u b
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
In this Chapter:
Encapsulating similar functionality
El Creating objects with properties, methods, and events
Using class modules with collections
445
446 I Chapter 20: Class Modules I
ENCAPSULATINGSIMILAR 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. Lets begin with a class named
clsUStationDialogthat will be used to display the dialog boxes used in
the chapter for FileOpen and Filecreate.
Before we begin looking at the code, lets identify what we want this class
to do.
Display File Open Dialog for Microstation DGN files.
Display File Open Dialog for Microsoft Excel files.
Display File Open Dialog for ASCII .txt files.
Display File Open Dialog for custom file extensions.
Property needed for File Name.
Property needed for Path Only.
Property needed for Path / File Name.
Property needed for Size of selected File.
Registry Entries to be used to store most recent path.
Registry Entries to be used to store most recent file.
You have accomplished each of the desired tasks already in this book.
The focus of this exercise will be to wrap it all into a single class.
We want to display.dgn files, .xls files, and .txt files. We also want to
display multiple custom file extensions in the dialog box. We could have
an OpenDGN method, an OpenXLS method, an OpenTXT
method, and an OpenCustom method. The main difference between
these methods would be the file extension(s) supplied. So, instead of
creating new methods for each file type we may want to browse, we will
work with one method, named OpenDi a1 og, that will handle any number
of file extensions.
Lets 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
'General Declarations
D i m p F i l e E x t s 0 As S t r i n g
P r i v a t e Sub C l a s s - I n i t i a l i z e ( )
ReDim p F i l e E x t s ( 0 )
End Sub
P u b l i c Sub C l e a r F i l e E x t s O
ReDim p F i l e E x t s ( 0 )
End Sub
P u b l i c Sub A d d F i l e E x t ( F i 1 e E x t A s S t r i n g )
D i m I As Long
D i m t m p F i l e E x t As S t r i n g
t m p F i1 e Ex t = LC a s e ( Rep 1 a c e ( F i1 e Ex t , " . " , " "
For I = 1 To U B o u n d ( p F i 1 e E x t s )
If tmpFileExt = p F i l e E x t s ( 1 ) Then
E x i t Sub
End I f
Next I
ReDim P r e s e r v e pFileExts(UBound(pFi1eExts) + 1)
pFileExts(UBound(pFi1eExts)) = t m p F i l e E x t
End Sub
S u b T e s t F i 1e E x t s ( 1
D i m MyUSD As New c l s U S t a t i o n D i a l o g
My U S D .Add F i 1 e E x t " . dgn "
MyUSD.AddFileExt "Xls"
MyUSD.ClearFi 1 e E x t s
End S u b
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 C1 ear F i 1 eExts method, remove all elements except
for the first element by redeclaring the variable pFileExts with an upper-
bound 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 Encapsulating Similar Functionality I 449
The ExtCount property tells how many file extensions have been added
to our class. If you declare a variable named ExtCount as public, you will
be able to read and write to the variable. This is not good because the
propertys 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.
P r o p e r t y G e t ExtCountO A s Long
ExtCount = UBound(pFi1eExts)
End P r o p e r t y
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.
P r o p e r t y Get G e t E x t s O As S t r i n g 0
I f UBound(pFi1eExts) = 0 Then
E x i t Property
End I f
D i m t m p G e t E x t s 0 As S t r i n g
ReDim tmpGetExts(UBound(pFi1eExts) - 1) As S t r i n g
D i m I As Long
For I = 1 To U B o u n d ( p F i 1 e E x t s )
tmpGetExts(1 - 1) = pFileExts(1)
Next I
GetExts = tmpGetExts
End P r o p e r t y
450 I Chapter 20: Class Modules I
First check to see if any file extensions have been added. If the Upper-
bound element of the array pFileExts is zero (0), nothing has been
added so immediately exit the property. Otherwise, create a new
temporary dynamic array to hold the file extensions that have been
added. Since the first element in the array pFileExts is empty, loop
through pFileExts 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 T e s t G e t E x t s O
D i m MyUSD As New c l s U S t a t i o n D i a l o g
D i m F i l e E x t s O As S t r i n g
My U S D .Add F i 1 e E x t " . dgn "
MyUSD.AddFileExt "DGN"
My U S D .Add F i 1 e E x t " . DWg "
MyUSD.AddFileExt "Xls"
FileExts = MyUSD.GetExts
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 t o 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
P r i v a t e p D e f F i l e P a t h As S t r i n g
P r o p e r t y Get D e f a u l t p a t h o As S t r i n g
Defaultpath = pDefFilePath
End P r o p e r t y
This is easy enough. Place the value stored in the variable pDefFilePath
into the property Defaultpath: Lets take a look at the Property Let
statement now for the Defaultpath property.
P r o p e r t y L e t D e f a u l t P a t h ( s t r P a t h 1 n As S t r i n g )
pDefFilePath = strPathIn
End P r o p e r t y
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. Lets try it out. This next code
should be placed in a code module.
Sub T e s t F i 1 e P a t h A ( 1
D i m MyUSD A s New c l s U S t a t i o n D i a l o g
MyUSD.Defau1 t P a t h = abc:\/?test
End Sub
If we run the code, abc:\/?testis 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:\/?testa legitimate path? At the time of the printing of this book,
it is not. So, what are we to do?
When a property is readkit e , 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.
P r o p e r t y L e t D e f a u l t P a t h ( s t r P a t h 1 n As S t r i n g )
I f Dir(strPathIn, vbDirectory) <> " " Then
pDefFilePath = strPathIn
End I f
End P r o p e r t y
Sub T e s t F i 1 e P a t h B ( )
D i m MyUSD As New c l s U S t a t i o n D i a l o g
MyUS D . De f a u 1 t Pa t h = " c : \ t e s t 5432 1"
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.
P r o p e r t y L e t D e f a u l t P a t h ( s t r P a t h 1 n As S t r i n g )
On E r r o r GoTo e r r h n d
I f Dir(strPathIn, vbDirectory) <> " " Then
pDefFilePath = strPathIn
End I f
E x i t Property
errhnd:
S e l e c t Case E r r . N u m b e r
Case 5 2 ' B a d f i l e name o r number
Err.Cl e a r
End S e l e c t
End P r o p e r t y
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.
ByVal f i l t e r s t r i n g A s S t r i n g , ByVal d e f a u l t D i r e c t o r y A s S t r i n g , -
ByVal titlestring A s String) A s Long
Now that the function is declared, we can use it.
Sub O p e n D i a l o g O
Dim tmpFilter A s String
pRetVal = 1
tmpFilter = " * . " & Join(GetExts, " ; * . " )
pFileNameSelected = Space(255)
pRetVal = mdlDialog~file0pen(pFileNameSelected, 0, 0, -
pDefFileName, tmpfilter, pDefFilePath, "Open File")
Select Case pRetVal
Case 1 'Cancel
Case 0 'Open
Dim tmpFile A s String
Dim xSplit A s Variant
tmpFile = Left(pFileNameSelected, InStr(1, ~
DFileNameSelected, Chr(0)) - 1)
454 I Chapter 20: Class Modules I
x S p l it = S p l it ( t m p F i 1 e , "\"I
pFileName = xSplit(UBound(xSp1it))
= " "
x S p l it ( UBound ( x S p l it
p F i 1e P a t h = J o i n ( x S p l it , " \ "
End S e l e c t
End S u b
P r o p e r t y G e t S e l e c t e d P a t h O As S t r i n g
SelectedPath = pFilePath
End P r o p e r t y
P r o p e r t y G e t S e l e c t e d F i 1e ( As S t r i n g
S e l e c t e d F i 1e = p F i 1 eName
End P r o p e r t y
P r o p e r t y G e t O p e n S u c c e s s O As B o o l e a n
S e l e c t Case p R e t V a l
Case 1 ' C a n c e l
OpenSuccess = False
Case 0 'Open
OpenSuccess = True
End S e l e c t
End P r o p e r 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
r e s o u r c e I d As L o n g , B y V a l s u g g e s t e d F i l e N a m e A s S t r i n g , -
ByVal f i l t e r s t r i n g As S t r i n g , ByVal d e f a u l t D i r e c t o r y As S t r i n g , -
B y V a l t i t l e s t r i n g As S t r i n g ) As L o n g
P r i v a t e p F i l e P a t h As S t r i n g
P r i v a t e p F i l e N a m e As S t r i n g
P r i v a t e p D e f F i l e P a t h As S t r i n g
P r i v a t e p D e f F i l e N a m e As S t r i n g
P r i v a t e P F i l e N a m e S e l e c t e d As S t r i n g
P r i v a t e p R e t V a l As L o n g
I Encapsulating Similar Functionality I 455
Private p F i l e E x t s 0 A s String
Sub O p e n D i a l o g O
Dim tmpFilter A s String
pRetVal = 1
tmpFilter = " * . " & Join(GetExts, " ; * . " )
pFileNameSelected = Space(255)
pRetVal = mdlOialog~file0pen(pFileNameSelected, 0, 0, -
pDefFileName, tmpfilter, pOefFilePath, "Open File")
Select Case pRetVal
Case 1 'Cancel
Case 0 'Open
Dim tmpFile A s String
Dim xSplit A s Variant
tmpFile = Left(pFileNameSelected, InStr(1, -
pFileNameSelected, Chr(0)) - 1)
xSplit = Split(tmpFile, " \ " )
pFileName = xSplit(UBound(xSp1it))
xSplit(UBound(xSp1it)) = " "
p Fi 1 e Pa t h = J o i n ( xSpl i t , \ )
" "
End Select
End Sub
456 I Chapter 20: Class Modules I
Property Get D e f a u l t F i l e O As String
DefaultFile = pDefFileName
End Property
pDefFilePath = strPathIn
End If
Exit Property
errhnd:
Select Case Err.Number
Case 52 'Bad file name or number
Err.Cl ear
End Select
End Property
Property Get G e t E x t s O As S t r i n g 0
If UBound(pFi1eExts) = 0 Then
Exit Property
End If
Dim t m p G e t E x t s 0 As String
ReDim tmpGetExts(UBound(pFi1eExts) - 1) A s String
Dim I A s Long
For I = 1 T o UBound(pFi1eExts)
tmpGetExts(1 - 1) = pFileExts(1)
Next I
GetExts = tmpGetExts
End Property
I Encapsulating Similar Functionality I 457
P r i v a t e Sub C l a s s - I n i t i a l i z e ( )
ReDim p F i l e E x t s ( 0 )
End Sub
P u b l i c Sub C l e a r F i l e E x t s O
ReDim p F i l e E x t s ( 0 )
End Sub
P u b l i c Sub A d d F i l e E x t ( F i 1 e E x t As S t r i n g )
D i m I As Long
D i m t m p F i l e E x t As S t r i n g
t m p F i1 e Ex t = LC a s e ( Rep 1 a c e ( F i1 e Ex t ,
" . " , " "
F o r I = 1 To U B o u n d ( p F i 1 e E x t s )
If t m p F i l e E x t = p F i l e E x t s ( 1 ) Then
E x i t Sub
End I f
Next I
ReDim P r e s e r v e pFileExts(UBound(pFi1eExts) + 1)
pFileExts(UBound(pFi1eExts)) = t m p F i l e E x t
End Sub
Sub T e s t S h o w D i a l o g A ( )
D i m MyUSD As New c l s U S t a t i o n D i a l o g
MyUSD.AddFileExt "dgn"
MyUSD.Defau1tPath = "c:\"
MyUSD. D e f a u l t F i 1 e = " t e s t .dgn"
MyUSD.OpenDialog
S e l e c t Case MyUSD.OpenSuccess
Case T r u e
MsgBox M y U S D . S e l e c t e d P a t h & M y U S D . S e l e c t e d F i l e
End S e l e c t
End Sub
Each file extension added to the class displays in the FileOpen dialog
box.
ByVal f i l t e r s t r i n g As S t r i n g , -
ByVal d e f a u l t D i r e c t o r y A s S t r i n g , -
ByVal t i t l e s t r i n g As S t r i n g ) As Long
Next, create a new method in the class module using variables and
properties that have already been declared.
Sub CreateDialog()
D i m t m p F i l t e r As S t r i n g
pRetVal = 1
tmpFilter = "*." & Join(GetExts, "; *.")
pFileNameSelected = Space(255)
pRetVal = mdlDialogpfileCreate(pFileNameSelected, 0 , 0,
pDefFileName, t m p f i l t e r , p D e f F i l e P a t h , "Create F i l e " )
S e l e c t Case p R e t V a l
Case 1 ' C a n c e l
Case 0 'Open
D i m t m p F i l e As S t r i n g
D i m x S p l i t As V a r i a n t
tmpFile = Left(pFileNameSelected, I n S t r ( 1 ,
pFileNameSelected, Chr(0)) - 1)
xSplit = Split(tmpFile, "\")
pFileName = xSplit(UBound(xSp1it))
xSplit(UBound(xSp1it)) = " "
p F i 1 e Pa t h = J o in ( x S p l it , " \")
End S e l e c t
End S u b
S u b TestShowDi a1 ogC( 1
D i m MyUSD As New c l s U S t a t i o n D i a l o g
MyUSD.AddFileExt "dgn"
MyUSD.DefaultPath = "c:\"
MyUSD . D e f a u l t F i 1 e = " t e s t .d g n "
MyUSD.CreateDialog
S e l e c t Case MyUSD.OpenSuccess
Case T r u e
MsgBox M y U S D . S e l e c t e d P a t h & M y U S D . S e l e c t e d F i 1 e
End S e l e c t
End S u b
460 I Chapter 20: Class Modules I
TestShowDialogC 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 TestShowDialogD and make
the same change.
Sub T e s t S h o w D i a l o g D ( )
D i m MyUSD As New c l s U S t a t i o n D i a l o g
MyUSD.AddFileExt "dgn"
MyUSD.AddFileExt "dwg"
My U S D .Ad d F i1 e Ex t " dx f "
MyUSD.Defau1 t P a t h = " c : \ M i c r o S t a t i o n VBA"
MyUS D . De f a u 1 t F i 1 e = " t e s t .d g n "
MyUSD.CreateDialog
S e l e c t Case MyUSD.OpenSuccess
Case T r u e
MsgBox M y U S D . S e l e c t e d P a t h & M y U S D . S e l e c t e d F i l e
End S e l e c t
End Sub
Sub T e s t S h o w D i a l o g E ( )
D i m MyUSD As New c l s U S t a t i o n D i a l o g
MyUSD.AddFileExt "dgn"
MyUSD.Defau1 t P a t h = "c:\"
MyUS D . De f a u 1 t F i 1 e = " t e s t .d g n "
My U S D . Open D i a 1 o g
S e l e c t Case MyUSD.0penSuccess
Case T r u e
MsgBox "Open " & MyUSD.SelectedPath & -
MyUSD.SelectedFile
Case F a l s e
I f M s g B o x ( " C r e a t e a new f i l e ? " , v b Y e s N o ) = vbYes T h e n
MyUSD.CreateDialog
I f MyUSD.0penSuccess = T r u e Then
MsgBox " C r e a t e " & MyUSD.Se1ectedPath & ~
MyUSD.SelectedFile
End I f
End I f
I Encapsulating Similar Functionality I 461
End S e l e c t
End S u b
create a new clsLineElem Class and include a few properties that are not
a part of the Microstation Object Model.
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.
S u b T e s t N e w L i neA( )
D i m myLE As New c l s L i n e E l e m
End S u b
When you run this macro, you will see the following MessageBox:
P r i v a t e p S t a r t P o i n t As P o i n t 3 d
P r i v a t e pEndPoint As P o i n t 3 d
P r o p e r t y L e t S t a r t P o i n t ( S t P t As P o i n t 3 d )
pStartPoint = StPt
End P r o p e r t y
P r o p e r t y G e t S t a r t P o i n t o As P o i n t 3 d
StartPoint = pStartPoint
End P r o p e r t y
P r o p e r t y L e t E n d P o i n t ( E n P t As P o i n t 3 d )
pEndPoint = EnPt
End P r o p e r t y
P r o p e r t y G e t E n d p o i n t 0 As P o i n t 3 d
Endpoint = pEndPoint
End P r o p e r t y
Sub T e s t N e w L i n e B ( 1
D i m myLE A s New c l s L i n e E l e m
myLE.StartPoint = Point3dFromXYZ(4, 4, 4 )
myLE.EndPoint = Point3dFromXYZ(10, 10, 10)
End Sub
P r o p e r t y G e t M i d p o i n t 0 As P o i n t 3 d
D i m t r n p P o i n t As P o i n t 3 d
trnpP0int.X = S t a r t P 0 i n t . X + (EndP0int.X - StartP0int.X) / 2
trnpP0int.Y = S t a r t P 0 i n t . Y + (EndP0int.Y - StartP0int.Y) / 2
trnpP0int.Z = S t a r t P 0 i n t . Z + (EndP0int.Z - StartP0int.Z) / 2
Midpoint = tmpPoint
End P r o p e r t y
P r o p e r t y G e t C h a n g e I n X O As D o u b l e
ChangeInX = pEndP0int.X - pStartP0int.X
End P r o p e r t y
P r o p e r t y G e t C h a n g e I n Y O As D o u b l e
ChangeInY = pEndP0int.Y - pStartP0int.Y
End P r o p e r t y
P r o p e r t y G e t C h a n g e I n Z O As D o u b l e
ChangeInZ = pEndP0int.Z - pStartP0int.Z
End P r o p e r t y
P r o p e r t y G e t L i n e A n g l e R a d s O As D o u b l e
LineAngleRads = Atn((pEndP0int.Y - pStartP0int.Y) /
466 I Chapter 20: Class Modules I
(pEndP0int.X - pStartP0int.X))
End P r o p e r t y
P r o p e r t y Get L i n e A n g l eDegs( As D o u b l e
LineAngleDegs = Degrees(LineAngleRads1
End P r o p e r t y
Enough with properties. Lets look at creating methods. The first one
will be DrawLine.
Sub D r a w L i n e ( )
D i m L i n e E l e m As L i n e E l e m e n t
Set LineElem = CreateLineElement2(Nothing, -
pStartPoint, pEndPoint)
ActiveModel Reference.AddElement LineElem
End Sub
S u b TestNewLineD(
D i m myLE As New c l s L i n e E l e m
myLE.StartPoint = P o i n t 3 d F r o m X Y Z ( O , 0 , 0)
myLE.EndPoint = P o i n t 3 d F r o m X Y Z ( 1 0 , 1 0 , 0)
my LE . Draw L i n e
myLE.EndPoint = P o i n t 3 d F r o m X Y Z ( - l O , 1 0 , 0)
my LE . Draw L i n e
End S u b
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 pEndPoint properties of the
class through the midpoint of the line.
S u b DrawLinePerp(
D i m P e r p S t As P o i n t 3 d
D i m P e r p E n As P o i n t 3 d
D i m P e r D M i d As P o i n t 3 d
I Creating Objects with Properties, Methods, and Events I 467
D i m L i n e A n g As D o u b l e
D i m L i n e L e n g t h As D o u b l e
L i neAng = L i n e A n g l eRads
PerpMid = Midpoint
LineLength = Point3dDistance(pStartPoint, pEndPoint)
PerpSt = Point3dAddAngleDistance(PerpMid, LineAng + P i / 2, -
L i n e L e n g t h / 2 , 0)
PerpEn = Point3dAddAngleDistance(PerpMid, LineAng - P i / 2, -
L i n e L e n g t h / 2 , 0)
D i m L i n e E l e m As L i n e E l e m e n t
Set LineElem = CreateLineElementZ(Nothing, ~
PerpSt, PerpEn)
ActiveModel Reference.AddElement LineElem
End Sub
Sub TestNewLineE()
D i m myLE As New c l s L i n e E l e m
myLE.StartPoint = P o i n t 3 d F r o m X Y Z ( O , 0 , 0)
myLE.EndPoint = P o i n t 3 d F r o m X Y Z ( 8 , 8 , 0)
myLE.DrawLine
myLE. D r a w L i n e P e r p
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 DrawCi r c l e ( )
D i m C i r c E l e m As E l l i p s e E l e m e n t
D i m R o t M a t r i x As M a t r i x 3 d
Set CircElem CreateEllipseElementZ(Nothing, M i d p o i n t , -
=
Point3dDistance(pStartPoint,pEndPoint) / 2, -
Point3dDistance(pStartPoint, p E n d P o i n t ) / 2, R o t M a t r i x )
ActiveModel Reference.AddElement C i rcElem
End Sub
Pub1 i c E v e n t L i n e A d d e d ( A d d e d L i n e As L i n e E l e m e n t )
Sub D r a w L i n e ( )
D i m L i n e E l e m As L i n e E l e m e n t
Set LineElem = CreateLineElement2(Nothing, -
pStartPoint, pEndPoint)
ActiveModel Reference.AddElement LineElem
R a i s e E v e n t L i n e A d d e d ( L i n e E l em)
End Sub
I Creating Objects with Properties, Methods, and Events I 469
When you declare a variable this way, the variables 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 forms code.
As clsLineElem
470 I Chapter 20: Class Modules I
After selecting myLE in the Object ComboBox, you can see the
clsLineElem Events in the Procedure ComboBox.
End S u b
Sub TestCol l e c t i o n A ( )
D i m m y C o l l As New C o l l e c t i o n
D i m m y L e v e l As L e v e l
For Each myLevel I n A c t i v e M o d e l R e f e r e n c e . L e v e l s
myCol1 .Add myLevel
Next
End S u b
I Using Class Modules with Collections I 471
Code 1 Long
Description String
ElementAccess msdLevelElementAccessAll MsdLevelElementAccess
ElementColor 0 Long
ElementLineSty le LineStylehineSty le
ElementLineWeight 0 Long
ID 1 Long
lsAd ive True Boolean
IsDispIay ed True Boolean
IsFromLevelLibrary False Boolean
IsFrozen False Boolean
IsInUse False Boolean
IsLocked False Boolean
Name "Level 1" String
Number 1 Long
OverrideCoIor 0 Long
OverrideLineStyle LineStylehineStyle
OverrideLineWeight 0 Long
ParentLevel Nothing Level
Plot True Boolean
Using0verrideCoIor True Boolean
UsingOverrideLineStyle True Boolean
UsingOverrideLineWeight True Boolean
bem 2 VariantlObjecthevel
bem 3 VariantlObjecthevel
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.
S u b TestCollectionBO
D i m m y C o l l As New C o l l e c t i o n
D i m m.yLeve1 As L e v e l
472 I Chapter 20: Class Modules I
F o r Each m y L e v e l I n A c t i v e M o d e l R e f e r e n c e . L e v e l s
m y C o l 1 .Add m y L e v e l
Next
'Now t h a t t h e c o l l e c t i o n i s p o p u l a t e d ,
' a c c e s s t h e o b j e c t s w i t h F o r Each . . Next
D i m m y L e v e l 2 As L e v e l
F o r Each m y L e v e l 2 I n m y C o l l
D e b u g . P r i n t myLevel2.Name
Next
End Sub
One way is to use the For Each ... Next statement. The example above
populates a collection with the levels in the active model. Then you
access each object in the collection using Fo r 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 Each ... Next for easy access to each object in the collection.
Here is another way to access objects in a collection:
Sub TestCol l e c t i o n C ( )
D i m m y C o l l As New C o l l e c t i o n
D i m m y L e v e l As L e v e l
F o r Each m y L e v e l I n A c t i v e M o d e l R e f e r e n c e . L e v e l s
m y C o l 1 .Add m y L e v e l
Next
'Now t h a t t h e c o l l e c t i o n i s p o p u l a t e d ,
'access t h e objects using t h e i t e m Index.
D i m m y L e v e l 2 As L e v e l
D i m I As Long
For I = 1 To m y C o l l . C o u n t
Set myLevel2 = 1
m y C o l 1 (I
D e b u g . P r i n t myLevel2.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 TestCol l e c t i o n D ( )
I Using Class Modules with Collections 473
D i m m y C o l l A s New C o l l e c t i o n
D i m myLevel As Level
F o r Each m y L e v e l I n A c t i v e M o d e l R e e r e n c e . L e v e l s
m y C o l 1 .Add m y L e v e l
Next
'Now t h a t t h e c o l l e c t i o n i s p o p u l a t e d ,
'access t h e objects using t h e i t e m Index.
D i m myLevel2 As Level
D i m I As Long
F o r I = m y C o l l . C o u n t To 1 S t e p -1
Set myLevel2 = myColl(1)
Debug. Pr in t my L e v e 1 2 . 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 T e s t C o l l e c t i o n F O
D i m m y C o l l A s New C o l l e c t i o n
D i m myLevel As Level
F o r Each m y L e v e l I n A c t i v e M o d e l R e f e r e n c e . L e v e l s
m y C o l 1 .Add m y L e v e l , m y L e v e l .Name
Next
'Now t h a t t h e c o l l e c t i o n i s p o p u l a t e d ,
' a d d r e s s a L e v e l b y i t ' s Key.
D i m myLevel2 As Level
Set myLevel2 = myColl("Defau1t")
MsgBox m y L e v e l 2 . N u m b e r
End S u b
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 objects 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.
Sub T e s t C o l l e c t i o n E O
D i m m y C o l l As New C o l l e c t i o n
D i m m y L e v e l As L e v e l
F o r Each m y L e v e l I n A c t i v e M o d e l R e f e r e n c e . L e v e l s
m y C o l 1 .Add m y L e v e l
Next
Now t h a t t h e c o l l e c t i o n i s p o p u l a t e d ,
remove t h e o b j e c t s u s i n g t h e i t e m Index.
D i m I As Long
F o r I = m y C o l l . C o u n t To 1 S t e p - 1
m y C o l 1 .Remove I
Next
End Sub
Sub TestCol l e c t i o n G ( )
D i m myLE As c l s L i n e E l e m
D i m XYA As D o u b l e
D i m XYMin As D o u b l e
D i m XYMax As D o u b l e
I Using Class Modules with Collections I 475
D i m m y C o l l As New C o l l e c t i o n
XYMin = 0
XYMax = 10
For XYA = XYMin T o XYMax
S e t myLE = New c l s L i n e E l e m
myLE.StartPoint = P o i n t 3 d F r o m X Y ( X Y A , XYMin)
myLE.EndPoint = P o i n t 3 d F r o m X Y ( X Y A , XYMax)
m y C o l 1 . A d d myLE
S e t myLE = Nothing
S e t myLE = New c l s L i n e E l e m
myLE.StartPoint = Point3dFromXY(XYMin, XYA)
myLE.EndPoint = Point3dFromXY(XYMax, XYA)
m y C o l 1 . A d d myLE
S e t myLE = Nothing
Next XYA
F o r Each myLE I n m y C o l l
myLE.DrawLine
Next
End S u b
rnyLE.StartPoint, Point3dFrornXY(xClose, y C l o s e ) )
A c t i v e M o d e l Reference.AddE1 ement myLineElem
S e t myLineElem = CreateLineElementZ(Nothing, -
rnyLE.EndPoint. Point3dFrornXY(xClose. y C l o s e ) )
A c t i v e M o d e l Reference.AddE1 ement myLineElem
End I f
Next
End Sub
we draw a line between the entered X and Y values and the Start and
End Points of the myLineElem object.
2.875
7.32
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:
Using VBA for maintaining standards
Using VBA to facilitate cross-company standards
Using VBA to track time in drawings
Auto-loading and auto-running macros
Protecting projects
Distributing VBA projects
Working in high-security mode
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.
S u b FindUnsupportedLevels()
D i m G o o d L e v e l s ( 0 To 4 ) As S t r i n g
D i m G o o d L e v e l s J As S t r i n g
D i m m y L e v e l As L e v e l
GoodLevels(0) = "ROADWAY"
GoodLevels(1) = "SIDEWALK"
GoodLevels(2) = "PAINT"
GoodLevels(3) = "ELECTRIC"
GoodLevels(4) = "GAS"
GoodLevelsJ = UCase("-" & J o i n ( G o o d L e v e l s , "-") & "-")
For Each myLevel I n A c t i v e D e s i g n F i l e . L e v e 1 s
I f I n S t r ( 1 , GoodLevelsJ, "-" & UCase(myLevel.Name) & "-"I = 0 Then
Debug.Print "Unsupported Level Found: " & myLevel .Name
End I f
Next
End S u b
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.
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 FindMissingLevelsO
D i m G o o d L e v e l s ( 0 To 4 ) As S t r i n g
D i m L e v e l F o u n d ( 0 To 4 ) As B o o l e a n
D i m m y L e v e l As L e v e l
D i m I As L o n g
G o o d L e v e l s (0) = "ROADWAY"
GoodLevels(1) = "SIDEWALK"
GoodLevels(2) = "PAINT"
GoodLevels(3) = "ELECTRIC"
GoodLevels(4) = "GAS"
GoodLevelsJ = UCase("-" & J o i n ( G o o d L e v e l s , "-"I & "-"I
F o r Each m y L e v e l I n A c t i v e D e s i g n F i l e . L e v e l s
For I = L B o u n d ( G o o d L e v e 1 s ) To U B o u n d ( G o o d L e v e 1 s )
I f StrComp(GoodLevels(I), myLevel .Name, vbTextCompare) = 0 Then
LevelFound(1) = True
End I f
Next I
Next
For I = L B o u n d ( G o o d L e v e 1 s ) To U B o u n d ( G o o d L e v e 1 s )
I f L e v e l F o u n d ( I)= Fa1 s e T h e n
D e b u g . P r i n t " M I S S I N G LEVEL: " & GoodLevels(1)
End I f
Next I
End Sub
Sub FindUnsupportedLevel s B ( )
D i m G o o d L e v e l s ( 0 To 4 ) As S t r i n g
D i m G o o d L e v e l s J As S t r i n g
D i m m y L e v e l As L e v e l
D i m F F i l e As L o n g
I Chapter 21:VBA for CAD Managers I
GoodLevels(0) = "ROADWAY"
GoodLevels(1) = "SIDEWALK"
GoodLevels(2) = "PAINT"
GoodLevels(3) = "ELECTRIC"
GoodLevels(4) = "GAS"
GoodLevelsJ = UCase("-" & J o i n ( G o o d L e v e l s , "-") & "-")
FFile = FreeFile
Open " C : \ M i c r o S t a t i o n VBA\LevelsUnSupported.txt" For Output As # F F i l e
For Each myLevel I n A c t i v e D e s i g n F i l e . L e v e l s
I f I n S t r ( 1 , GoodLevelsJ, "-" & UCase(myLevel.Name) & "-") = 0 Then
P r i n t #FFi 1 e, A c t i veDesi gnFi 1 e. Path & A c t i v e D e s i g n F i 1 e. Name & -
Sub F i n d M i s s i n g L e v e l s B O
D i m G o o d L e v e l s ( 0 To 4 ) As S t r i n g
D i m L e v e l F o u n d ( 0 To 4 ) As B o o l e a n
D i m m y L e v e l As L e v e l
D i m I As L o n g
D i m F F i l e As L o n g
GoodLevels(0) = "ROADWAY"
GoodLevels(1) = "SIDEWALK"
GoodLevels(2) = "PAINT"
GoodLevels(3) = "ELECTRIC"
GoodLevels(4) = "GAS"
GoodLevelsJ = UCase("-" & Join(GoodLevels, 'I-") & 'I-")
FFile = FreeFile
Open " C : \ M i c r o S t a t i o n V B A \ L e v e l s M i s s i n g . t x t " For Output As # F F i l e
For Each myLevel I n A c t i v e D e s i g n F i l e . L e v e 1 s
F o r I = L B o u n d ( G o o d L e v e 1 s ) To U B o u n d ( G o o d L e v e 1 s )
I f StrComp(GoodLevels(I), myLevel.Name, vbTextCompare) = 0 Then
L e v e l F o u n d ( I)= T r u e
End I f
Next I
Next
F o r I = L B o u n d ( G o o d L e v e 1 s ) To U B o u n d ( G o o d L e v e 1 s )
I f L e v e l F o u n d ( I)= Fa1 s e T h e n
P r i n t # F F i l e , A c t i v e D e s i g n F i l e . Path & ActiveDesignFile.Name & -
v b T a b & "MISSING LEVEL: " & G o o d L e v e l s ( I)
I Using VBA for Maintaining Standards I 483
End I f
Next I
C1 o s e # F F i 1 e
End S u b
You are writing to an ASCII file. Place a tab between the filepathhame
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 FindUnsupportedLevelsC(Fi1eToQuery As D e s i g n F i l e )
D i m G o o d L e v e l s ( 0 To 4 ) As S t r i n g
D i m G o o d L e v e l s J As S t r i n g
D i m m y L e v e l As L e v e l
D i m F F i l e As L o n g
G o o d L e v e l s (0) = "ROADWAY"
GoodLevels(1) = "SIDEWALK"
GoodLevels(2) = "PAINT"
GoodLevels(3) = "ELECTRIC"
GoodLevels(4) = "GAS"
GoodLevelsJ = UCase("-" & Join(GoodLeve1s. "-"I & "-"I
FFile = FreeFile
Open " C : \ M i c r o S t a t i o n V B A \ L e v e l s U n S u p p o r t e d . t x t " F o r Append ~
As # F F i l e
F o r Each m y L e v e l I n F i 1 eToCluery. L e v e l s
I f I n S t r ( 1 . GoodLevelsJ. "-" & UCase(myLevel.Name) & "-") = 0 Then
P r i n t #FFile, F i l e T o Q u e r y . P a t h & FileToCluery.Name & -
S u b FindMissingLevelsC(Fi1eToQuery As D e s i g n F i l e )
484 I Chapter 21:VBA for CAD Managers I
D i m G o o d L e v e l s ( 0 To 4 ) As S t r i n g
D i m L e v e l F o u n d ( 0 To 4 ) As B o o l e a n
D i m m y L e v e l As L e v e l
D i m I As L o n g
D i m F F i l e As L o n g
GoodLevels(0) = "ROADWAY"
GoodLevels(1) = "SIDEWALK"
GoodLevels(2) = "PAINT"
GoodLevels(3) = "ELECTRIC"
GoodLevels(4) = "GAS"
GoodLevelsJ = UCase("-" & Join(GoodLevels, 'I-") & 'I-")
FFile = FreeFile
O p e n "C:\MicroStation VBA\LevelsMissing.txt" F o r A p p e n d As #FFile
F o r E a c h m y L e v e l I n F i 1e T o Q u e r y . L e v e l s
F o r I = L B o u n d ( G o o d L e v e 1 s ) To U B o u n d ( G o o d L e v e 1 s )
If StrComp(GoodLevels(I), myLevel.Name, v b T e x t C o m p a r e ) = 0 Then
L e v e l F o u n d ( I)= T r u e
End I f
Next I
Next
F o r I = L B o u n d ( G o o d L e v e 1 s ) To U B o u n d ( G o o d L e v e 1 s )
I f L e v e l F o u n d ( I)= Fa1 s e T h e n
P r i n t I l F F i l e , F i l e T o Q u e r y . P a t h & Fi1eToQuery.Name & ~
Sub DoFilesInFolderO
D i m MyFSO As New F i l e S y s t e m O b j e c t
I Cross-Company Standards I 485
A1 1 U s e r s \ A p p l ic a t i o n D a t a \ D o c u m e n t s \ B e n t l e y \ W o r k S p a c e \ & -
Projects \ Examp 1 e s \ Ar c h i te c t u r a 1 \ Dgn
CROSS-COMPANYSTANDARDS
Two companies need to work with each others 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 L e v e l SpecA translates one standard to another for level
names. When the procedure finds Level 20: it changes the name to
STREET: The other level name mappings are easy to see.
I Chapter 21:VBA for CAD Managers I
S u b L e v e l SpecA( 1
D i m m y L e v e l As L e v e l
D i m I As L o n g
For I = 1 To ActiveDesignFile.Leve1s.Count
S e t myLevel = ActiveDesignFile.Levels(1)
S e l e c t Case m y L e v e l . Name
Case " L e v e l 20"
m y L e v e l .Name = "STREET"
ActiveDesignFile.Levels.Rewrite
Case " L e v e l 2 1 "
m y L e v e l .Name = "SIDEWALK"
ActiveDesignFile.Leve1s.Rewrite
Case " L e v e l 2 3 "
m y L e v e l .Name = "GUTTER"
ActiveDesignFile.Levels.Rewrite
Case " L e v e l 3 8 "
m y L e v e l .Name = "STRIPING"
ActiveDesignFile.Leve1s.Rewrite
Case " L e v e l 3 9 "
m y L e v e l .Name = "SEWER"
ActiveDesignFile.Levels.Rewrite
Case " L e v e l 4 0 "
m y L e v e l .Name = "PHONE"
ActiveDesignFile.Leve1s.Rewrite
Case " L e v e l 4 1 "
m y L e v e l .Name = "ELECTRIC"
ActiveDesignFile.Levels.Rewrite
Case " L e v e l 4 2 "
m y L e v e l .Name = "NATGAS"
ActiveDesignFile.Leve1s.Rewrite
Case " L e v e l 4 7 "
m y L e v e l .Name = "FIBER"
ActiveDesignFile.Leve1s.Rewrite
End S e l e c t
Next I
End S u b
After this procedure is run, the design file meets the company's
standards for level names. We can work on the file with our custom-
developed 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.
S u b L e v e l SpecB( )
Dim myLevel A s Level
Dim I A s Long
For I = 1 T o ActiveDesignFile.Leve1s.Count
Set myLevel = ActiveDesignFile.Levels(1)
Select Case myLevel .Name
C a se ST RE ET
" "
Level 20 STREET
Level 21 SIDEWALK
Level 23 GUTER
Level 38 STRIPING
Level 39 SEWER
Level 40 PHONE
Level 41 ELECTRIC
Level 42 NATGAS
Level 47 FIBER
Let's look at the code that makes use of our LevelMap file.
S u b L e v e l SpecFromFi 1 e ( )
D i m m y F i l e As S t r i n g
D i m O l d L e v e l O As S t r i n g
D i m NewLevel 0 As S t r i n g
D i m F F i l e As L o n g
D i m t x t I n As S t r i n g
D i m x S p l i t 0 As S t r i n g
ReDim O l d L e v e l (0)
ReDim NewLevel (0)
D i m m y L e v e l As L e v e l
Dim I A s Long
D i m J As Long
myFile = "C:\MicroStation VBA\LevelMap.txt"
FFile = FreeFile
I Cross-Company Standards I 489
Open m y F i l e F o r I n p u t As # F F i l e
W h i l e EOF(FFi1e) = False
Line Input #FFile, txtIn
I f I n S t r ( 1 , t x t I n , vbTab) > 0 Then
xSplit = S p l i t ( t x t I n , vbTab)
OldLevel(UBound(01dLevel ) ) = xSplit(0)
NewLevel(UBound(NewLeve1)) = xSplit(1)
ReDim P r e s e r v e O l d L e v e l ( U B o u n d ( O l d L e v e 1 + 1)
ReDim P r e s e r v e NewLevel (UBound(NewLeve1 + 1)
End I f
Wend
ReDim P r e s e r v e O l d L e v e l ( U B o u n d ( O l d L e v e 1 ) - 1)
ReDim P r e s e r v e NewLevel ( U B o u n d ( N e w L e v e 1 ) - 1)
C1 o s e # F F i 1 e
F o r I = 1 To Active0esignFile.Levels.Count
S e t myLevel = ActiveDesignFile.Levels(1)
For J = L B o u n d ( O l d L e v e 1 ) To U B o u n d ( O l d L e v e 1 1
I f StrComp(OldLevel(J), myLevel.Name) = 0 Then
m y L e v e l .Name = NewLevel(J)
A c t i v e D e s i g n F i l e . L e v e l s . Rewri t e
E x i t For
End I f
Next J
Next I
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 doesnt 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 project
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 drafteddesigner 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 doesnt 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.
I Tracking 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?".What
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 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:
Imp1 e m e n t s I V i e w U p d a t e E v e n t s
D i m W i t h E v e n t s MSApp As A p p l i c a t i o n
P r i v a t e Sub C l a s s - I n i t i a l i z e ( )
D i m F F i l e As Long
FFile = FreeFile
Open " C : \ M i c r o S t a t i o n VBA\TimeTrack.txt" F o r Append As I I F F i l e
P r i n t BFFile, " I N I T " & v b T a b & Now
C1 o s e B F F i 1 e
S e t MSApp = Application
End Sub
P r i v a t e Sub C l a s s - T e r m i n a t e 0
D i m F F i l e As Long
FFile = FreeFile
Open " C : \ M i c r o S t a t i o n VBA\TimeTrack.txt" F o r Append As B F F i l e
P r i n t I I F F i l e , "TERM" & v b T a b & Now
C1 o s e # F F i 1 e
I Chapter 21:VBA for CAD Managers I
End Sub
P r i v a t e Sub IViewUpdateEvents-AfterRedraw(TheViews0 As V i e w ,
T h e M o d e l s O As M o d e l R e f e r e n c e , -
B y V a l DrawMode As M s d D r a w i n g M o d e )
I f DrawMode = msdDrawingModeNorma1 T h e n
D i m F F i l e As L o n g
FFile = FreeFile
Open " C : \ M i c r o S t a t i o n V B A \ T i m e T r a c k . t x t " For A p p e n d As # F F i l e
P r i n t I I F F i l e , " R E D R " & v b T a b & Now & v b T a b &
Application.UserName & vbTab &
ActiveDesignFile.Name
C1 o s e # F F i 1 e
End I f
End Sub
P r i v a t e Sub IViewUpdateEvents-BeforeRedraw(TheViews0 As V i e w ,
T h e M o d e l s O As M o d e l R e f e r e n c e ,
B y V a l DrawMode As M s d D r a w i n g M o d e )
End Sub
XSpl it ( U B o u n d ( X S p 1 i t )
C1 o s e # F F i 1 e
End Sub
MSApp-OnDesignFileOpened(ByVa1
P r i v a t e Sub D e s i g n F i l e N a m e As S t r i n g )
D i m F F i l e As L o n g
D i m X S p l i t O As S t r i n g
XSplit = Split(DesignFileName, " \ " )
FFile = FreeFile
Open " C : \ M i c r o S t a t i o n V B A \ T i m e T r a c k . t x t " F o r A p p e n d As # F F i l e
Print #File, " O P E N " & v b T a b & Now & v b T a b & -
C1 o s e B F F i 1 e
End S u b
As the code suggests, we write our time logging data into an ASCII text
f i l e 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 i s written in a class module, use code in a code module t o
call up the class so the class can capture the events.
S u b TestTimeTrack( 1
S e t myME = New c l s T i m e T r a c k
AddModel A c t i v a t e E v e n t s H a n d l e r myME
End S u b
494 I Chapter 21:VBA for CAD Managers I
Here is
the
output of
the Class:
.
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-LOADAND 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 dont want the user to
be responsible for turning it on. To be effective, load and execute
without any user intervention.
Sub O n P r o j e c t L o a d ( )
TestTimeTrack
End Sub
I Auto-Load and Auto-Run I 495
MS-VBA-OPEN-I N-MEMORY
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
Lets discuss the acceptable values for this variable, define it, and set its
value.
OK
PROTECTING
PROJECTS
Normally, we discourage writing passwords on pieces of paper. Why?
Because we dont 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 dont want our hard-earned code to be
available to just anyone.
I Protecting Projects I 499
OK
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 wont discuss them here.
500 I Chapter 21:VBA for CAD Managers I
Use the Protection tab
to set the Projects
password.
Projects set as Locked
for viewing can be
loaded and procedures
can be executed from
the VBA Project
Manager but they
cannot be opened in
the VBA environment
for modifications
unless the correct
password is supplied.
3 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.
4 After saving the project, unload it and then reload it using the VBA
Project Manager.
When you get back into VBA after M
loading your project, you see the
project in the project list with the
project tree compressed.
5 Click on the Plus symbol to
expand the project results in a
request for the password. Now
what was that password?Was it
6
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 501
DISTRIBUTING
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 dialog box:
IAS Helper COM Component 1.0 Type Library
Each selected reference refers to a .dll or .exe file with functionality for
use in our programming. The top three items appear in every
Microstation VBA project. We added the Microsoft Scripting Runtime
library earlier in the chapter. Before distributing projects, look at the
references added to the project because their absence on someone elses
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.
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 HIGHSECURITYMODE
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:
In this Chapter:
The OnDesignFileOpened Event
The OnDesignFileClosed Event
The ISaveAsEvents-BeforeRemap Event
The ISaveAsEvents-AfterRemap Event
The ISaveAsEvents-AfterSaveAs Event
505
506 I Chapter 22: Microstation File-Based Events I
ONDESIGNFILEOPENED
The OpenDesi gnFi 1 eOpened event is part of the Microstation Application
Object. Each time a DesignFile is opened, the OnDesi gnFi 1 eOpened event
is triggered. Lets begin by adding some very simple code to this event.
To begin with, this event is only available when a variable is declared as
an Applicationtype of object using the WithEventskeyword.
Lets create a new Class Module and name it clsS aveAs.
D i m W i t h E v e n t s myMS As A p p l i c a t i o n
D i m WithEvents m y M S A s Application
P r i v a t e Sub myMSpDnDesignFileDpened(ByVal
D e s i g n F i l e N a m e As S t r i n g )
D i m f f i l e As L o n g
ffile = FreeFile
Open C : \ M i c r o S t a t io n V B A \ F i 1 eDpen . t x t F o r A p p e n d As {if f i1 e
P r i n t { i f f i l e , Now & v b T a b & D e s i g n F i l e N a m e
Close S i f f i l e
End Sub
Here is the code in the OnDesi gnFi 1 eOpened event. It is very simple. We
are writing the datehime and the file name to an ASCII Text file at
C:\MicroStation VBA\FileOpen. txt.
Writing the code is simple. But we cannot execute the code in an event
in the same manner as we do when we 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
ONDESIGN
FILECLOSED
This event is triggered when a file is closed. We could write code to write
to an ASCII file just as we did with the OnDesignFi 1 eOpened event. But
there are far more powerful and useful things we can do.
Lets do some brainstorming on what we could do with the
OnDesi gnFi 1 eC1 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 dont have space in this book to write 100 different applications
making use of the On Des i gn Fi 1 eC 1 osed event. We have space for exactly
one example.
When a file is closed, it may be useful to capturethe file at the time it is
closed. Of course, we could copy the file to a folder. This would
accomplish the task but could take up more hard drive space than we
want to dedicate to this purpose. If we copy the file but it is zipped
(compressed into a .zip file), we would save some disk space. If we place
the file in an existing zipped folder or file, this may be even more
helpful.
So, how do we 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 dont need to?
Windows XP introduced the use of Compressed (zipped) Folders. A
Compressed (zipped) Folder is essentially a zip file (.zip extension) that
Windows treats as a folder. Files can be copied to and pasted from a
Compressed (zipped) Folder using standard Windows functionality.
Lets leverage this new Windows XP functionality so we can zip files
using VBA.
Before we write any code we need to add a Reference to the Microsoft
Shell Controls and Automation object in VBA (Tools > References).
When we do so, we have 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.
508 I Chapter 22: Microstation File-Based Events I
As we will see later in this chapter, we will find 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, when we see a file with a .dgn file
extension, we instinctively refer to it as a Microstation file. But there is
nothing keeping us from changing the file extension of a .txt file to .dgn.
So, even though the file extension is a good indication as to what type of
file we are looking at, we do not have any guarantees. The second
identifying feature found in most files is a file header. A file header
consists of a specific number of bytes at the beginning of a file that helps
programs to verify the file type.
A zip file (or a compressed zipped folder) header consists of 24 bytes.
How do we know this? Creating a new zipped folder in Windows and
opening the file in a Hex Editor shows us 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(0)
values. This writes a zip file header to a file and once this is done,
Windows XP recognizes the file not only because of the file extension
but also because of the file header. Other zip file readerdwriters also
recognize the file as a legitimate zip file. Of course, a zip file is not very
useful if it is empty.
Once the zip file is created (if it did not already exist), we can copy files
to the 'file/folder' by using the Shell library.
The procedure Copy F i 1 e T o Z i p F i 1 e is placed in the Class Module, not the
Code Module.
S u b CopyFileToZipFile(ZipFi1e As S t r i n g , F i l e T o C o p y As S t r i n g , -
C o p y F i l e A s As S t r i n g )
D i m f f i l e As Long
D i m m y s h e l l As New S h e l l
D i m z i p F o l d e r As S h e 1 1 3 2 . F o l d e r 3
ffile = FreeFile
If Dir(ZipFi1e) = " " Then
Open Z i p F i l e F o r O u t p u t As ] I f f i l e
P r i n t { I f f i l e , Chr(80) & Chr(75) & Chr(5) & Chr(6) &
Chr(0) & Chr(0) & Chr(0) & Chr(0) & ~
ISAVEASEVENTS
INTERFACE
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 we have been working with:
Implements ISaveAsEvents
As with previous discussions on Interfaces, we need to make sure that all
events (methods) of the Interface are declared before we begin putting
code into any of them.
End Sub
I ISaveAsEvents Interface I 511
P r iv a t e S u b I S a v e As Eve n t s-A f t e r Re ma p ( -
B y V a l T h e D e s i g n F i l e As D e s i g n F i l e , -
ByVal SavedFormat As M s d D e s i g n F i l e F o r m a t ,
ByVal D e s t i n a t i o n F i lename As S t r i n g )
End Sub
P r i v a t e Sub I S a v e A s E v e n t s - A f t e r S a v e A s O
End Sub
D i m F i l e s a v e d As S t r i n g
D i m F i l e F o r m a t As L o n g
D i m F i l e p r e v i o u s As S t r i n g
Now that the variables are declared, they can be used in the A f t e r Remap
event as follows:
P r iv a t e S u b I S a v e As Eve n t s-A f t e r Re ma p ( -
B y V a l T h e D e s i g n F i l e As D e s i g n F i l e , -
ByVal SavedFormat As M s d D e s i g n F i l e F o r m a t ,
ByVal D e s t i n a t i o n F i lename As S t r i n g )
F i 1e S a v e d = D e s t i n a t i o n F i lename
F i 1eFormat = SavedFormat
Fileprevious = TheDesignFile.Ful1Name
End Sub
512 I Chapter 22: Microstation File-Based Events I
OK. We have 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 A f t e r S a v e A s event?
Its time to brainstorm again. The possibilities are endless. Each function
we discuss could be performed on the TheDesignFileparameter or the
DestinationFilenameparameter. 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 examples would be
useful and could be easily implemented. Lets do something else,
though.
When the user performs a SaveAs, the file that had been opened is given
a new file name and optionally, saved as a different file type. The
scenario we will work with right now is, when a file experiences a
SaveAs and is saved as an AutoCAD .dwg file, we w ill take the original
design file (it could already be an AutoCAD .dwg file) and we will
prepare it to be written to a CD.
Once again, in this example, we are using functionality introduced in
Windows XP. Windows XP 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 see a message informing
us we have files waiting to be written to CD:
So, how do we get files into this temporary storage location? Where is it?
Lets use the Shell Object we just finished working with in the last
example in this example as well.
A new procedure needs to be created in the Class Module we used in the
previous example. This procedure will be named CopyFi 1 e t o C D and it
I ISaveAsEvents Interface I 513
will take one parameter, the file that is to be copied to the CD. Heres the
code. A discussion of the code follows.
S u b CopyFileToCD(Fi1eToCopy A s String)
Dim myshell A s New Shell
Dim cdFolder A s She1132.Folder3
Set cdFolder = myShell.Namespace(59)
If Not cdFolder Is Nothing Then
cdFol der .CopyHere Fi leToCopy, 0
End If
End S u b
The first thing we need to do is find out where the temporary storage
location is for the user that is logged in. Now, how are we going to do
that? We can discover this by supplying a number of 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. Thats all there is to it.
One of the great things about this procedure is that we dont need to
purchase CD writing add-ins to make this work. If the user has
Windows XP, we can make use of this procedure.
Now, we need to remember that the
Copy F i 1 eToCD procedure does not actually
burn the CD. It only copies the file to the
staging area to burn the CD. When we are
ready to burn the CD, we place a CD-R or
CD-RW into the CD burner, select the CD
New
drive in Windows Explorer, and then go to
the Explorer menu File > Write these 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 for being written to a CD. How
do we use it? We will use it in the ISaveAsEvents-AfterSaveAs event.
Private S u b ISaveAsEvents-AfterSaveAsO
Select Case Fi leFormat
Case msdDesignFileFormatCurrent
Case msdDesignFileFormatDWG
CopyFi 1 eToCD Fi 1 ePrevi o u s
514 I Chapter 22: Microstation File-Based Events I
Case m s d D e s i g n F i 1 e F o r m a t D X F
Case m s d D e s i g n F i 1 e F o r m a t U n k n o w n
Case m s d D e s i g n F i 1 e F o r m a t V 7
Case m s d D e s i g n F i 1 e F o r m a t V 8
End S e l e c t
End Sub
The A f t e r S a v e A s event is the last event to execute when a SaveAs is
executed by the user. But the event itself does not tell us what the source
file name, destination file name, or format is. We collect this
information in the A f t e r R e m a p event so we can use it in the A f t e r S a v e A s
event.
The example above shows how to use the Copy F i 1 eToCD procedure we
created but it only executes it when the SaveAs command was 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 S e l e c t Case structure altogether and use Copy F i 1 eToCD
every time a SaveAs occurs.
We have addressed the functionality in our new Class Module
clsSaveAs in a couple of sections. Lets take a look at the entire Class
Module from beginning to end. Remember, the variable declarations
appear in the General Declarations area of the Class Module. Particular
attention should be paid to the use of the variables FileSaved,
FileFormat, and FilePrevious.
D i m W i t h E v e n t s myMS As A p p l i c a t i o n
ImDlements ISaveAsEvents
D i m F i l e S a v e d As S t r i n g
D i m F i l e F o r m a t As L o n g
D i m F i l e P r e v i o u s As S t r i n g
Private Sub C l a s s - I n i t i a l i z e ( 1
S e t myMS = A p p l ic a t i o n
End Sub
B y V a l S a v e d F o r m a t As M s d D e s i g n F i l e F o r m a t , -
B y V a l D e s t i n a t i o n F i l e n a m e As S t r i n g )
F i 1eSaved = D e s t i n a t i o n F i lename
F i 1e F o r m a t = SavedFormat
Fileprevious = TheDesignFile.FullName
End Sub
P r i v a t e Sub ISaveAsEvents-AfterSaveAsO
S e l e c t Case F i l e F o r m a t
Case msdDesignFileFormatCurrent
Case m s d D e s i g n F i l e F o r m a t D W G
Copy F i 1 eToCD F i 1 e P r e v i ous
Case m s d D e s i g n F i l e F o r m a t D X F
Case m s d D e s i g n F i l e F o r m a t U n k n o w n
Case m s d D e s i g n F i l e F o r m a t V 7
Case m s d D e s i g n F i l e F o r m a t V 8
End S e l e c t
End Sub
P r i v a t e Sub ISaveAsEvents-BeforeRemap(
B y V a l T h e D e s i g n F i l e As D e s i g n F i l e ,
B y V a l S a v e d F o r m a t As M s d D e s i g n F i l e F o r m a t ,
B y V a l D e s t i n a t i o n F i l e n a m e As S t r i n g )
End Sub
P r i v a t e Sub myMS-OnDesignFileClosed(ByVa1
D e s i g n F i l e N a m e As String)
C o p y F i 1e T o Z i p F i l e " C : \ M i c r o S t a t i o n VBA\Fi 1 eC1 o s e d . z i p " ,
DesignFileName, DesignFileName & "." & CLng(Timer * 1000)
End Sub
P r i v a t e Sub myMS-OnDesignFileOpened(ByVa1 -
D e s i g n F i l e N a m e As String)
D i m f f i l e As Long
ffile = FreeFile
Open " C : \ M i c r o S t a t i o n VBA\FileOpen.txt" F o r Append As B f f i l e
Print ]Iffile, Now & v b T a b & D e s i g n F i l e N a m e
Close ] I f f i l e
End Sub
516 I Chapter 22: Microstation File-Based Events I
S u b C o p y F i l e T o C D ( F i 1 e T o C o p y As S t r i n g )
D i m m y s h e l l As New S h e l l
D i m c d F o l d e r As S h e 1 1 3 2 . F o l d e r 3
Set cdFolder = myShell.Namespace(59)
I f N o t c d F o l d e r Is N o t h i n g Then
c d F o l d e r . C o p y H e r e F i 1 eToCopy , 0
End I f
End S u b
S u b C o p y F i l e T o Z i p F i 1 e ( Z i p F i 1 e As S t r i n g , F i 1 eToCopy As S t r i n g ,
C o p y F i l e A s As S t r i n g )
D i m f f i l e As L o n g
D i m m y s h e l l As New S h e l l
D i m z i p F o l d e r As S h e 1 1 3 2 . F o l d e r 3
ffile = FreeFile
If Dir(ZipFi1e) = "I' Then
Open Z i p F i l e F o r O u t p u t As ] I f f i l e
P r i n t { i f f i l e , Chr(80) & Chr(75) & Chr(5) & Chr(6) & -
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. Let's discuss how to
make use of the Class Module code.
The code in a Class Module must be called up by code in a Code Module
or UserForm. We will use a Code Module for this example.
I Review I 517
Sub TestSaveAs()
D i m mySaveAs As New c l s S a v e A s
A d d S a v e A s E v e n t s H a n d l e r mySaveAs
End S u b
The next four chapters (this one included) continue to deal with
responding to events in Microstation. This chapter reviews the
IAttachmentEvents Interface. Five events are exposed.
In this Chapter:
The IAttachmentEvents Interface
AfterAttach
AfterDetach
AttachmentModified
BeforeAttach
BeforeDetach
519
520 I Chapter 23: Responding to Microstation Attachment Events I
THEIATTACHMENTEVENTS
INTERFACE
Let's create a new Class Module named clsAttachmentEvents. Each
event implemented by the IAttachmentEvents Interface must be
declared. Adding simple Debug.Print statements inside each event helps
us to understand the order in which the events are triggered.
P r i v a t e Sub IAttachmentEvents-AfterAttach(ByVa1 -
T h e A t t a c h m e n t As A t t a c h m e n t )
Debug.Print "AfterAttach"
End Sub
P r i v a t e Sub IAttachmentEvents-AfterDetach(ByVa1
T h e A t t a c h m e n t As A t t a c h m e n t )
Debug.Print "AfterDetach"
End Sub
P r i v a t e Sub IAttachmentEvents-AttachmentModified(ByVa1 -
T h e A t t a c h m e n t As A t t a c h m e n t )
Debug . P r in t " A t t a c h m e n t Mod if ie d "
End Sub
P r i v a t e Sub IAttachmentEvents-BeforeAttach(Fi1eName As S t r i n g , -
A1 1 o w A t t a c h m e n t As Boo1 e a n )
Debug.Print "BeforeAttach"
End Sub
P r i v a t e Sub IAttachmentEvents-BeforeDetach(ByVa1
T h e A t t a c h m e n t As A t t a c h m e n t )
Debug.Print "BeforeDetach"
End Sub
Two of the events refer to attaching a reference file and two refer to
detaching a reference file.
AFTERATTACH
P r i v a t e Sub IAttachmentEvents-AfterAttach(ByVa1
T h e A t t a c h m e n t As A t t a c h m e n t )
End Sub
I AfterAttach I 521
P r i v a t e Sub IAttachmentEvents-AfterAttach(ByVa1
T h e A t t a c h m e n t As A t t a c h m e n t )
D i m T x t P t As P o i n t 3 d
TxtPt.X = 0
TxtPt.Y = -100
D i m R o t M a t r i x As M a t r i x 3 d
D i m m y T e x t As T e x t E l e m e n t
S e t myText CreateTextElementl(Nothing, N o w & " . . . " & -
=
TheAttachment.DesignFile.Path, T x t P t , R o t M a t r i x )
ActiveModelReference.AddElement m y T e x t
End S u b
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 right now, the text will be added to the same place whether
I AfterAttach I 523
P r i v a t e Sub IAttachmentEvents-AfterAttach(ByVa1
T h e A t t a c h m e n t As A t t a c h m e n t )
D i m F F i l e As L o n g
FFile = FreeFile
Open " C : \ M i c r o S t a t i o n VBA\AttachrnentLog.txt" F o r Append A s # F F i l e
P r i n t # F F i l e , Now & v b T a b & A c t i v e D e s i g n F i 1 e . P a t h & vbTab & -
TheAttachment.DesignFi1e.Path
C1 o s e B F F i 1 e
End Sub
If we follow the code shown directly above, we will see that we are
writing the ActiveDesignFile's Path and the Attachment's Path to an
ASCII text file named Attuchrnentlog.txt. This log file can be used to
track file dependencies.
One more example demonstrates additional use of the Attachment's
Properties:
P r i v a t e Sub IAttachmentEvents-AfterAttach(ByVa1 -
T h e A t t a c h m e n t As A t t a c h m e n t )
D i m P t A As P o i n t 3 d
D i m P t B As P o i n t 3 d
D i m myRange As Range3d
D i m MyRec As L i n e E l e m e n t
D i m L i n e P t s ( 0 To 4 ) As P o i n t 3 d
myRange = TheAttachment.Range(True)
PtA = myRange.High
PtB = myRange.Low
LinePts(O1.X = PtA.X: LinePts(O).Y = PtA.Y
LinePts(l1.X = PtB.X: LinePts(l).Y = PtA.Y
LinePts(21.X = PtB.X: LinePts(Z).Y = PtB.Y
LinePts(31.X = PtA.X: LinePts(3).Y = PtB.Y
LinePts(4l.X = PtA.X: LinePts(4).Y = PtA.Y
S e t MyRec = Application.CreateLineElementl(Nothing, LinePts)
A c t i v e M o d e l R e f e r e n c e . A d d E l e m e n t MyRec
End Sub
Sub TestAttachmentsA( 1
D i m myAE As New c l s A t t a c h m e n t E v e n t s
AddAttachmentEventsHandler myAE
End Sub
AFTERDETACH
Two events relate to the Detaching of Attachments. One is Before
Detach and the other is After Detach. As the names imply, Before
Detach occurs prior to After Detach.
The After Detach event is the last opportunity we have to do anything
with the Attachment.
Let's try some code that could be used to notify someone that an
Attachment has been detached.
P r i v a t e Sub IAttachmentEvents-AfterDetach(ByVa1 -
T h e A t t a c h m e n t As A t t a c h m e n t )
Shell "c:\Program F i l e s \ I n t e r n e t Explorer\iexplore.exe " &
" " " . . .
h t t p : / / www t r a c kmy d g n d r a w in g s c om / 1 o g a s p? f i1 en a me=" & ~
End Sub
A~ACHMENTMODIFIED
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
the fact that changes have been made to an attachment but will not help
us with the nature of the modification. We would need to look at
additional events to get more detailed information on that.
End Sub
BEFOREA~ACH
EVENT
Private Sub IAttachmentEvents-BeforeAttach(Fi1eName As String,
AllowAttachment As Boolean)
The BeforeAttach Event is triggered before an Attachment takes place.
We are supplied with the FileName and are given the opportunity to
cancel the attachment by setting the AllowAttachment parameter to
False.
BEFOREDETACH
EVENT
P r i v a t e Sub IAttachmentEvents-BeforeDetach(ByVa1
T h e A t t a c h m e n t As A t t a c h m e n t )
The last event we need to discuss is the BeforeDetach Event. Once 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 of the few things we can do is log the fact that the detachment took
place.
P r i v a t e Sub IAttachmentEvents-BeforeDetach(ByVa1
T h e A t t a c h m e n t As A t t a c h m e n t )
D i m F F i l e As Long
FFile = FreeFile
Open " C : \ M i c r o S t a t i o n VBA\DetachrnentLog.txt" F o r Append As # F F i l e
P r i n t #FFile, Now & v b T a b & A p p l i c a t i o n . U s e r N a m e &
v b T a b & TheAttachment.DesignFile.Ful1Name
C1 o s e # F F i 1 e
End Sub
In this example, we are writing to a log file. We capture the Date and
Time, the User that is detaching the file, and the full name of the file
being detached.
REVIEW
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. The
Interfaces are named IModelActivateEvents and
IModelChangeEvents: To simplify matters, we will implement both
Interfaces in the same Class Module. The Class Module will be named
CISModelEvents.
In this Chapter:
The AfterActivate Event
The BeforeActivate Event
The Modelchange Event
Implementing each of the Interfaces in a single 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 IModelChangeEvents
P r i v a t e Sub I M o d e l A c t i v a t e E v e n t s A f t e r A c t i v a t e ( B y V a 1 TheModel
As M o d e l R e f e r e n c e )
Debug.Print AfterActivate: & vbTab & vbTab & ~
527
I Chapter 24:Model Events I
End Sub
P r i v a t e Sub IModelChangeEvents-ModelChange(ByVa1 T h e M o d e l As
M o d e l R e f e r e n c e , B y V a l Change As M s d M o d e l C h a n g e T y p e )
D e b u g . P r i n t "Change: " & vbTab & vbTab & vbTab & -
TheModel.DesignFi1e.Name & v b T a b & TheModel.Name & " - " & -
ModelChange(Change1
End Sub
F u n c t i o n M o d e l C h a n g e ( C h a n g e 1 n As M s d M o d e l C h a n g e T y p e ) As S t r i n g
S e l e c t Case C h a n g e I n
'Active
Case MsdModelChangeType.mdlModelChangeBeforeActive
M o d e l Change = "BeforeActive"
Case MsdModelChangeType.mdlModelChangeActive
M o d e l Change = "Active"
'Create
Case MsdModelChangeType.mdlModelChangeBeforeCreate
Mode 1 Change = " Be f o r eC r ea t e "
Case MsdModelChangeType.mdlModelChangeCreate
M o d e l Change = "Create"
'Delete
Case MsdModelChangeType.mdlModelChangeBeforeDelete
M o d e l Change = "BeforeDelete"
Case MsdModelChangeType.mdlModelChangeDelete
M o d e l Change = "Delete"
'Name
Case MsdModelChangeType.mdlModelChangeBeforeName
M o d e l Change = "BeforeName"
Case MsdModelChangeType.mdlModelChangeName
M o d e l Change = "Name"
'Properties
I Model Events I 529
Case MsdModelChangeType.mdlModelChangeBeforeProperties
Modelchange = "BeforeProperties"
Case MsdModelChangeType.mdlModelChangeProperties
Mode 1 C h a n g e = " P r o p e r t ie s "
'Settings
Case MsdModelChangeType.mdlMode1ChangeBeforeSettings
Modelchange = "BeforeSettings"
Case MsdModelChangeType.mdlModelChangeSettings
Modelchange = "Settings"
' UnCreate
Case MsdModelChangeType.mdlModelChangeBeforeUnCreate
Modelchange = "BeforeUnCreate"
Case MsdModelChangeType.mdlModelChangeUnCreate
Model Change = "UnCreate"
'UnDelete
Case MsdModelChangeType.mdlModelChangeBeforeUnDelete
Modelchange = "BeforeUnDel e t e "
Case MsdModel ChangeType.md1 Model ChangeUnDel e t e
Model Change = "UnDel e t e "
' PropagateAnnotationScale
Case -
MsdModelChangeType.mdlModelChangePropagateAnnotationScale
M o d e l c h a n g e = "PropagateAnnotationScale"
End S e l e c t
End F u n c t i o n
P r i v a t e myME As c l s M o d e l E v e n t s
Sub AddEventsO
RemoveEvents
S e t myME = New c l s M o d e l E v e n t s
AddModel A c t i v a t e E v e n t s H a n d l e r myME
530 I Chapter 24:Model Events I
AddModelChangeEventsHandler myME
End Sub
Sub R e m o v e E v e n t s (
I f myME I s N o t h i n g = F a l s e Then
RemoveModelActivateEventsHandler myME
RemoveModelChangeEventsHandler myME
S e t myME = Nothing
End I f
End Sub
IModelActivateEventsBeforeActivate
IModelChangeEvents-Modelchange ( W i t h B e f o r e A c t i v e E v e n t )
IModelActivateEvents-AfterActivate
IModelChangeEvents-Modelchange ( W i t h A c t i v e E v e n t )
Each event supplies us with 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 Modelchange
Event with the BeforeActivate constant and then a Modelchange
Event with an Active Constant.
25 Level Events
531
532 I Chapter 25: Level Events I
The change type names are fairly self-explanatory.We had better look at
the actual event before we continue.
P r i v a t e Sub ILevelChangeEvents-LevelChanged( -
B y V a l C h a n g e T y p e As M s d L e v e l C h a n g e T y p e ,
B y V a l T h e L e v e l As L e v e l ,
B y V a l TheModel As M o d e l R e f e r e n c e )
End Sub
Implements I L e v e l ChangeEvents
b e f o r e c h a n g e a c t i v e shows t h e o l d l e v e l name
P r i v a t e Sub ILevelChangeEvents-LevelChanged(
B y V a l C h a n g e T y p e As M s d L e v e l C h a n g e T y p e , -
B y V a l T h e L e v e l As L e v e l , -
B y V a l TheModel As M o d e l R e f e r e n c e )
End Sub
F u n c t i o n G e t C h a n g e T y p e ( C h a n g e 1 n As M s d L e v e l C h a n g e T y p e ) As S t r i n g
S e l e c t Case C h a n g e I n
Case MsdLevelChangeType.msdLevelChangeAfterChangeActive
GetChangeType = "AfterChangeActive"
Case MsdLevelChangeType.msdLevelChangeAfterCreate
GetChangeType = "Aftercreate"
Case MsdLevelChangeType.msdLevelChangeAfterDelete
G e t C h a n g e T y pe = " A f t e r De 1 e t e "
Case MsdLevelChangeType.msdLevelChangeBeforeChangeActive
GetChangeType = "BeforeChangeActive"
Case MsdLevelChangeType.msdLevelChangeBeforeDelete
GetChangeType = "BeforeDelete"
Case MsdLevelChangeType.msdLeve1ChangeChangeAttribut.e
GetChangeType = "ChangeAttribute"
Case MsdLevelChangeType.msdLevelChangeChangeCode
GetChangeType = "Changecode"
Case MsdLevelChangeType.msdLevelChangeChangeDisplay
GetChangeType = "ChangeDisplay"
Case MsdLevelChangeType.msdLevelChangeChangeName
GetChangeType = "ChangeName"
Case MsdLevelChangeType.msdLevelChangeChangeParent
GetChangeType = "Changeparent"
Case MsdLevelChangeType.msdLevelChangeTableRed0
G e t C h a n g e T y pe = " C h a n g e T a b 1 e Red o "
Case MsdLevelChangeType.msdLevelChangeTableUnd0
G e t C ha n g e T y p e = " C ha n geTa b 1 e U n d o "
End S e l e c t
End F u n c t i o n
P r i v a t e myLC As c l s L e v e l E v e n t s
Sub AddEvents( 1
S e t myLC = New c l s L e v e l E v e n t s
A d d L e v e l C h a n g e E v e n t s H a n d l e r myLC
End Sub
Sub RemoveEventsO
534 I Chapter 25: Level Events I
I f myLC I s N o t h i n g = F a l s e Then
R e m o v e L e v e l C h a n g e E v e n t s H a n d l e r myLC
S e t myLC = Nothing
End I f
End S u b
P r i v a t e Sub ILevelChangeEvents-LevelChanged(
B y V a l C h a n g e T y p e As M s d L e v e l C h a n g e T y p e , -
B y V a l T h e L e v e l As L e v e l , -
B y V a l TheModel As M o d e l R e f e r e n c e )
S e l e c t Case C h a n g e T y p e
Case M s d L e v e l C h a n g e T y p e . m s d L e v e 1 C h a n g e A f t e r C h a n g e A c t i v e
Case MsdLevelChangeType.msdLevelChangeAfterCreate
Case MsdLevelChangeType.msdLevelChangeAfterDelete
Case M s d L e v e l C h a n g e T y p e . m s d L e v e 1 C h a n g e B e f o r e C h a n g e A c t i v e
Case MsdLevelChangeType.msdLevelChangeBeforeDelete
Case MsdLevelChangeType.msdLevelChangeChangeAttribute
Case MsdLevelChangeType.msdLevelChangeChangeCode
Case MsdLevelChangeType.msdLevelChangeChangeDisp1ay
Case MsdLevelChangeType.msdLevelChangeChangeName
Case MsdLevelChangeType.msdLevelChangeChangeParent
Case MsdLevelChangeType.msdLevelChangeTableRed0
Case MsdLevelChangeType.msdLevelChangeTableUnd0
End S e l e c t
End Sub
Now we are ready to populate our Select Case statement to deal with the
Level Changes. It should be said that we do not have the ability to
prohibit changes from taking place in this Interface. We can only react
to the events.
Lets take a look at some of the more useful events in this Interface:
I The Active Event I 535
THEACTIVEEVENT
Case MsdLevelChangeType.msdLevelChangeAfterChangeActive
Dim FFile As Long
FFile = FreeFile
Open "c:\levelactivated.txt" For Append As BFFile
Print #FFile, Now & vbTab & -
TheLevel.Name & vbTab & -
TheModel.Name & vbTab & ~
TheModel .DesignFile.FullName
C1 ose #FFi 1 e
A simple log is kept of when the Active Level is changed. We log the
datehime, name of the Level, name of the Model, and the Design File's
full name.
THEAFTERCREATE
EVENT
Case MsdLevelChangeType.msdLevelChangeAfterCreate
Dim FFile2 A s Long
FFile2 = FreeFile
Open "c:\levelcreated.txt" For Append As #FFileP
Print #FFile2, Now & vbTab & ~
THEAFTERDELETE
EVENT
Case MsdLevelChangeType.msdLevelChangeAfterDelete
MsgBox "Level & TheLevel .Name &
" has been deleted."
"
By the time this Event is triggered, very little can be done with the Level.
We can still get its name, however.
536 I Chapter 25: Level Events I
THEBEFORECHANGEACTIVE
EVENT
Case MsdLevelChangeType.msdLevelChangeBeforeChangeActive
Debug. P r i n t " L e v e l " " " & TheLevel.Name & ~
The name of this event may suggest that we are being told which Level is
about to become active. Not so. We are only told which Level is about to
become deactivated. We do not know which Level is about to become
activated until the AfterChangeActive Event.
THEBEFOREDELETE
EVENT
Case MsdLevelChangeType.msdLevelChangeBeforeDelete
Debug.Print "Before Delete: " & T h e L e v e l .Name & v b C r & -
vbTab & TheLevel . D e s c r i p t i o n
THECHANGEA~RIBUTE
EVENT
Case MsdLevelChangeType.msdLeve1ChangeChangeAttribute
D e b u g . P r i n t "Change A t t r i b u t e : " & T h e L e v e l .Name & vbCr & -
vbTab & T h e L e v e 1 . D e s c r i p t i o n & vbCr & -
Every model makes use of levels. 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
In this Chapter:
The BeginUndoRedo Event
The Elementchanged Event
BEGINUNDOREDOEVENT
P r i v a t e Sub IChangeTrackEvents-BeginUndoRedo(
B y V a l A f t e r U n d o R e d o As E l e m e n t ,
B y V a l B e f o r e U n d o R e d o As E l e m e n t , -
B y V a l A c t i o n As M s d C h a n g e T r a c k A c t i o n , -
B y V a l I s U n d o As B o o l e a n )
End S u b
537
538 I Chapter 26: Change Track Events I
its properties before the Undo or Redo action takes place. The Action
parameter tells us which type of action has taken place. IsUndohelps us
know whether the action was an Undo or a Redo. Lets take a look at
some code in this event:
End Sub
In this example we are writing the Level Names of the elements that are
modified, Action, and IsUndo parameters to the Immediate Window.
For example, if we changed the Level of an element, and then issued an
Undo and then a Redo, we would see the following lines in the
Immediate Window:
We can see the After and Before Level names, the Trpe (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.
msdChangeTrackActionAdd 2 =
msdChangeTrackActionAppData = 8
msdChangeTrackActionDelete = 1
msdChangeTrackActionDrop = 6
msdChangeTrackActionMark = 7
msdChangeTrackActionModelAdd = 9
msdChangeTrackActionMode1 D e l e t e = 10
I Element Changed Event I 539
msdChangeTrackActionModify = 3
msdChangeTrackActionModifyFence = 5
msdChangeTrackActionNewFilePositionAndModify = 4
We can see here that we were performing Undo/Redo actions on a
modification to an element.
We should keep in mind that the constants contained in this
enumeration are used in other areas of the Change Track Events
Interface as well as the BeginUndoRedo Event.
ELEMENTCHANGEDEVENT
P r i v a t e Sub IChangeTrackEvents-ElementChanged(
B y V a l A f t e r c h a n g e As E l e m e n t , -
B y V a l B e f o r e c h a n g e As E l e m e n t , -
B y V a l A c t i o n As M s d C h a n g e T r a c k A c t i o n , -
C a n t B e U n d o n e As B o o l e a n )
End Sub
P r i v a t e Sub IChangeTrackEvents-ElementChanged( -
B y V a l A f t e r c h a n g e As E l e m e n t , -
B y V a l B e f o r e c h a n g e As E l e m e n t , -
B y V a l A c t i o n As M s d C h a n g e T r a c k A c t i o n ,
C a n t B e U n d o n e As B o o l e a n )
End Sub
540 I Chapter 26: Change Track Events I
The code is simple and straight forward. 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.
P r i v a t e Sub IChangeTrackEvents-ElementChanged( -
B y V a l A f t e r c h a n g e As E l e m e n t , -
B y V a l B e f o r e c h a n g e As E l e m e n t , -
B y V a l A c t i o n As M s d C h a n g e T r a c k A c t i o n ,
C a n t B e U n d o n e As B o o l e a n )
S e l e c t Case Application.CommandState.CommandName
Case D e l e t e E l e m e n t
Debug.Print D e l e t e Element
D e b u g . P r i n t vbTab & BeforeChange.Level.Name & vbTab & ~
End Sub
I Element Changed Event I 541
E l e m e n t Selection
New L e v e l (0) New L e v e l (0) 4 False
D r a g Selection
New L e v e l (0) New L e v e l (0) 3 False
E l e m e n t Selection
Default Default 3 False
D e l e t e Element
New L e v e l (0) 1 False
P r i v a t e Sub IChangeTrackEvents-ElementChanged(
B y V a l A f t e r c h a n g e As E l e m e n t , -
B y V a l B e f o r e c h a n g e As E l e m e n t , -
B y V a l A c t i o n As M s d C h a n g e T r a c k A c t i o n , -
C a n t B e U n d o n e As B o o l e a n )
S e l e c t Case A c t i o n
Case r n s d C h a n g e T r a c k A c t i o n A d d
Case r n s d C h a n g e T r a c k A c t i o n A p p D a t a
Case r n s d C h a n g e T r a c k A c t i o n D e l e t e
Case m s d C h a n g e T r a c k A c t i o n D r o p
Case m s d C h a n g e T r a c k A c t i o n M a r k
Case m s d C h a n g e T r a c k A c t i o n M o d e l A d d
Case r n s d C h a n g e T r a c k A c t i o n M o d e l D e l e t e
Case r n s d C h a n g e T r a c k A c t i o n M o d i f y
Case r n s d C h a n g e T r a c k A c t i o n M o d i f y F e n c e
Case msdChangeTrackActionNewFilePositionAndModify
542 I Chapter 26: Change Track Events I
End S e l e c t
End Sub
We can now build on this framework. Lets take a look at a few examples.
P r i v a t e Sub IChangeTrackEvents-ElementChanged( -
B y V a l A f t e r c h a n g e As E l e m e n t , ~
B y V a l B e f o r e c h a n g e As E l e m e n t , ~
B y V a l A c t i o n As M s d C h a n g e T r a c k A c t i o n , -
C a n t B e U n d o n e As B o o l e a n )
S e l e c t Case A c t i o n
Case m s d C h a n g e T r a c k A c t i o n A d d
Case msdChangeTrackActionAppData
Case m s d C h a n g e T r a c k A c t i o n D e l e t e
Case m s d C h a n g e T r a c k A c t i o n D r o p
Case m s d C h a n g e T r a c k A c t i o n M a r k
Case m s d C h a n g e T r a c k A c t i o n M o d e 1 Add
Case m s d C h a n g e T r a c k A c t i o n M o d e l D e l e t e
Case m s d C h a n g e T r a c k A c t i o n M o d i f y
S e l e c t Case A f t e r C h a n g e . T y p e
Case MsdElementType.msdElementTypeTextNode, -
MsdElementType.msdElementTypeText
I f A f t e r c h a n g e . L e v e l . Name <> TEXT T h e n
Afterchange. Level = -
.
A c t iveDes ig n F i 1 e L e v e l s ( T E X T 1
A f t e r c h a n g e . Rewri t e
End I f
End S e l e c t
Case m s d C h a n g e T r a c k A c t i o n M o d i f y F e n c e
Case msdChangeTrackActionNewFilePositionAndModify
End S e l e c t
End Sub
Example 2
P r i v a t e Sub I C h a n g e T r a c k E v e n t s - E l e m e n t C h a n g e d (
B y V a l A f t e r c h a n g e As E l e m e n t , -
B y V a l B e f o r e c h a n g e As E l e m e n t , -
B y V a l A c t i o n As M s d C h a n g e T r a c k A c t i o n , -
C a n t B e U n d o n e As B o o l e a n )
S e l e c t Case A c t i o n
Case r n s d C h a n g e T r a c k A c t i o n A d d
D e b u g . P r i n t "Add: " & A f t e r C h a n g e . T y p e & vbTab & -
End Sub
Add: 1 5 0 136
Add: 6 0 137
Add: 4 0 138
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 w ill modify the Event to make use of this new function.
P r i v a t e Sub I C h a n g e T r a c k E v e n t s - E l e m e n t C h a n g e d (
B y V a l A f t e r c h a n g e As E l e m e n t ,
B y V a l B e f o r e c h a n g e As E l e m e n t ,
B y V a l A c t i o n As M s d C h a n g e T r a c k A c t i o n , -
C a n t B e U n d o n e As B o o l e a n )
S e l e c t Case A c t i o n
Case m s d C h a n g e T r a c k A c t i o n A d d
I Chapter 26: Change Track Events I
D e b u g . P r i n t "Add: " & GetType(AfterChange.Type1 & vbTab &
AfterChange.ID.High & vbTab & -
AfterChange.ID.Low
Case msdChangeTrackActionAppData
Case m s d C h a n g e T r a c k A c t i o n D e l e t e
Case m s d C h a n g e T r a c k A c t i o n D r o p
Case m s d C h a n g e T r a c k A c t i o n M a r k
Case m s d C h a n g e T r a c k A c t i o n M o d e 1 Add
Case msdChangeTrackActi onModel Delete
Case m s d C h a n g e T r a c k A c t i o n M o d i f y
Case m s d C h a n g e T r a c k A c t i o n M o d i f y F e n c e
Case msdChangeTrackActionNewFilePositionAndModify
End Select
End Sub
Case r n s d E l e m e n t T y p e C o n i c
GetType = "Conic"
Case r n s d E l e m e n t T y p e C u r v e
G e t T y pe = " C urve "
Case r n s d E l e m e n t T y p e D e s i g n F i l e H e a d e r
GetType = "DesignFileHeader"
Case r n s d E l e m e n t T y p e D g n S t o r e C o m p o n e n t
GetType = "DgnStoreComponent"
Case r n s d E l e m e n t T y p e D g n S t o r e H e a d e r
GetType = "DgnStoreHeader"
Case r n s d E l e m e n t T y p e D i g S e t D a t a
GetType = "DigSetData"
Case r n s d E l e m e n t T y p e D i m e n s i o n
G e tT y p e = " D imen s ion "
Case r n s d E l e m e n t T y p e E l l i p s e
GetType = " E l 1i p s e "
Case rnsdElementTypeGroupData
GetType = "GroupData"
Case r n s d E l e m e n t T y p e L e v e l M a s k
GetType = "LevelMask"
Case rnsdEl e m e n t T y p e L e v e 1 Symbol o g y
GetType = " L e v e l Symbology"
Case r n s d E l e m e n t T y p e L i n e
GetType = "Line"
Case r n s d E l e m e n t T y p e L i n e S t r i n g
Ge t T y p e = " L in e St r in g "
Case r n s d E l e m e n t T y p e M a t r i x D o u b l e D a t a
Ge t T y p e = " M a t r ix Do u b 1 e Da t a "
Case r n s d E l e m e n t T y p e M a t r i x H e a d e r
Ge t T y p e = " M a t r ix He a d e r "
Case r n s d E l e m e n t T y p e M a t r i x I n t e g e r D a t a
Ge t T y p e = " M a t r ix I n t e ge r Da t a "
Case rnsdElementTypeMeshHeader
GetType = "MeshHeader"
Case r n s d E l e m e n t T y p e M i c r o S t a t i o n
GetType = "MicroStation"
Case rnsdElementTypeMu1 t i L i n e
GetType = "Mu1 t i L i n e "
Case rnsdElementTypeNamedGroupComponent
GetType = "NamedGroupComponent"
Case rnsdElementTypeNamedGroupHeader
I Chapter 26: Change Track Events I
GetTy p e = Named G ro u p H e a d e r
" "
Case msdElementTypePointString
GetTy p e = Poi n t St r i n g
" "
Case msdElementTypeRasterComponent
GetTy p e = Ra st e rC omp o n e n t
" "
Case m s d E l e m e n t T y p e R a s t e r H e a d e r
GetTy p e = Ra st e rH e a d e r
" "
Case m s d E l e m e n t T y p e R a s t e r R e f e r e n c e
GetTy p e = Ra st e r Ref e re n c e
" "
Case msdElementTypeRasterReferenceComponent
GetType = " R a s t e r R e f e r e n c e C o m p o n e n t "
Case msdElementTypeReferenceAttachment
GetType = "ReferenceAttachment"
Case m s d E l e m e n t T y p e R e f e r e n c e O v e r r i d e
GetType = Referenceoverride"
"
Case msdElementTypeShape
GetType = "Shape"
Case msdElementTypeSharedCel1
GetType = "SharedCell "
End S e l e c t
End F u n c t i o n
Example 3
When an element is deleted, the action recorded is
'msdChangeTrackActionDelete'.
P r i v a t e Sub I C h a n g e T r a c k E v e n t s - E l e m e n t C h a n g e d ( -
B y V a l A f t e r c h a n g e As E l e m e n t , -
B y V a l B e f o r e c h a n g e As E l e m e n t , -
B y V a l A c t i o n As M s d C h a n g e T r a c k A c t i o n ,
C a n t B e U n d o n e As B o o l e a n )
S e l e c t Case A c t i o n
Case r n s d C h a n g e T r a c k A c t i o n A d d
Case r n s d C h a n g e T r a c k A c t i o n A p p D a t a
Case m s d C h a n g e T r a c k A c t i o n D e l e t e
D e b u g . P r i n t GetType(BeforeChange.Type1 & " Deleted"
Case m s d C h a n g e T r a c k A c t i o n D r o p
Case r n s d C h a n g e T r a c k A c t i o n M a r k
Case r n s d C h a n g e T r a c k A c t i o n M o d e l A d d
Case r n s d C h a n g e T r a c k A c t i o n M o d e l D e l e t e
Case m s d C h a n g e T r a c k A c t i o n M o d i f y
Case m s d C h a n g e T r a c k A c t i o n M o d i f y F e n c e
Case msdChangeTrackActionNewFilePositionAndModify
End S e l e c t
End Sub
548 I Chapter 26: Change Track Events I
Text Deleted
Shape D e l e t e d
E l l i p s e Deleted
Line Deleted
Line Deleted
We have had several examples that deal with the 'Action' parameter
instead of counting on the 'CommandName: Our next example makes
use of both.
P r i v a t e Sub IChangeTrackEvents-ElementChanged(
B y V a l A f t e r c h a n g e As E l e m e n t , -
B y V a l B e f o r e c h a n g e As E l e m e n t , -
B y V a l A c t i o n As M s d C h a n g e T r a c k A c t i o n , -
C a n t B e U n d o n e As B o o l e a n )
D i m m y L e v e l As L e v e l
D i m L e v e l c o u n t As L o n g
S e l e c t Case A c t i o n
Case m s d C h a n g e T r a c k A c t i o n A d d
Case msdChangeTrackActionAppData
Case m s d C h a n g e T r a c k A c t i o n D e l e t e
Case m s d C h a n g e T r a c k A c t i o n D r o p
Case m s d C h a n g e T r a c k A c t i o n M a r k
Case m s d C h a n g e T r a c k A c t i o n M o d e 1 Add
Case m s d C h a n g e T r a c k A c t i o n M o d e l D e l e t e
Case m s d C h a n g e T r a c k A c t i o n M o d i f y
Case m s d C h a n g e T r a c k A c t i o n M o d i f y F e n c e
Case msdChangeTrackActionNewFilePositionAndModify
GetType(AfterChange.Type)
S e l e c t Case
Case " T a b 1 e "
S e l e c t Case CommandState.CommandName
Case "New L e v e l "
Levelcount = ActiveDesignFile.Leve1s.Count
I Activating the ChangeTrackEvents Interface I 549
Set myLevel = ~
ActiveDesignFile.Levels(Leve1Count - 1)
D e b u g . P r i n t m y L e v e l .Name & " Added."
End S e l e c t
End S e l e c t
End S e l e c t
End Sub
ACTIVATINGTHE CHANGETRACKEVENTS
INTERFACE
As with other Interfaces, we implement each of the Interface's events in a
Class Module and then add the Event Handler. The code that adds the
Event Handler is run from within a Code Module as follows:
P r i v a t e myCTE As C l s C h a n g e T r a c k E v e n t s
Sub AddEvents( 1
RemoveEvents
S e t myCTE = New C l s C h a n g e T r a c k E v e n t s
A d d C h a n g e T r a c k E v e n t s H a n d l e r myCTE
End Sub
Sub RemoveEventsO
I f myCTE I s N o t h i n g = F a l s e Then
550 I Chapter 26: Change Track Events I
RemoveChangeTrackEventsHandler myCTE
Set m y C T E = Nothing
End I f
End S u b
27 Non-Graphical Info =
Databases
In this Chapter:
How Microstation links elements to Databases
How to create a Microsoft Access Database from scratch to
link to a Design File
Making use of UDL (Universal Data Link) files
Linking elements in Microstation to a Database using VBA
Creating Database Records using SQL
Creating a User Interface to view Database Information
551
552 I Chapter 27: Non-Graphical Info - Databases I
Now that we know which Table we need to look in, we begin looking in
that Table for a matching mslink value.
In the above example, a DatabaseLink object was found attached to an
element in Microstation. The EntityNumber of the DatabaseLink object
is 18. In the mscatalog table, the EntityNumber 18 points us to the
Table named parcel.In the parcel Table, a value of 531 was found in
the mslink field and this 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 more than one
record is linked to an element, multiple DatabaseLink objects will be
attached to the element in Microstation.
Now, we have discussed the theory behind linking elements in
Microstation to records in databases. Lets look at an actual example.
1 Open the file ...\xurnp/es\Civi/\Dgn\cogo.dgn. This file is installed
with Microstation.
2 Next, zoom into the upper-left hand corner of the file.
If we look at this file and the illustration shown above, we will see
that we were looking at Parcel Number 13 on Hayden Drive.
554 I Chapter 27: Non-Graphical Info - Databases I
3 Now, lets open the database file
...\Examples\Database\Oledb\Examples\Access\gis.mdb in
Microsoft Access. This is the database that is attached to this file. A
review of the mscatalogtable reveals the database tables in this
Access Database that Microstation can work with. Opening the
parcel table reveals the records we just looked at.
So far we have looked into how Microstation links elements in design
files to records in databases. More information can be found in
Microstations help file by searching for Database.A large number of
help topics will appear. Much of what displays relates to using
Microstations database tools. But we are going to be concentrating 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 are going to use
an OLEDB connection with a Microsoft Access database. OLEDB
drivers are installed as part of the Microsoft Windows operating system
and we will use these drivers to not only communicate with the database
but create a new database.
Lets begin by creating a new Microsoft Access database. Since this is a
book about programming, we will demonstrate how this can be done
even if Microsoft Access is not installed on our computers. We will
begin with the code and then offer an explanation.
Sub C r e a t e D B ( 1
D i m m y C a t As New A D O X . C a t a l o g
D i m d b L o c a t i o n As S t r i n g
D i m C o n n S t r i n g As S t r i n g
dbLocation = C:\MicroStation VBA\DatabaseLinkTest.mdb
ConnString = Provider=Microsoft.Jet.OLEDB.4.O;Data Source= &
dbLocation
myCat.Create ConnString
End Sub
This code makes use of the Microsoft ADO Ext. 2.8 for DDL and
Security Reference. Before executing the procedure, we need to add
this Reference in VBA. This code creates a new database. The database is
empty; there are no tables in it. Creating Tables is our next step. There
I Creating a Database from Scratch I 555
Sub C r e a t e D B P ( 1
D i m m y C a t As New A D O X . C a t a l o g
D i m d b L o c a t i o n As S t r i n g
D i m C o n n S t r i n g As S t r i n g
d b L o c a t i on = " C : \ M i c r o S t a t i on V B A \ D a t a b a s e L i n k T e s t .mdb"
ConnString = "Provider=Microsoft.Jet.OLEDB.4.O;Data Source=" &
dbLocation
myCat.ActiveConnection = ConnString
D i m m y T a b l e As New T a b l e
D i m m y c o l u m n As ADOX.Column
myTable.Name = "Lots"
' m s l i n k Column
my Ta b 1 e . C o 1 umn s .A p p e n d " ms 1 in k " , a d I n t e g e r
'Owner Column
S e t mycolumn = New ADOX.Column
myCol umn. Name = "Owner"
myColumn.Type = adVarWChar
myCol umn . A t t r i b u t e s = a d C o l Nu1 1 a b l e
myCo1umn.DefinedSize = 50
myTable.Columns.Append mycolumn
' S o l d Column
S e t mycolumn = New ADOX.Column
myCol umn. Name = "Sold"
myColumn.Type = adBoolean
myTable.Columns.Append mycolumn
' D a t e S o l d Column
S e t mycolumn = New ADOX.Column
myCol umn. Name = "DateSol d"
myColumn.Type = adDate
myCol umn . A t t r i b u t e s = a d C o l Nu1 1 a b l e
myTable.Columns.Append mycolumn
' A c r e s Column
556 I Chapter 27: Non-Graphical Info - Databases I
Set myCol umn = New ADOX.Co1 umn
myCol umn. Name = "Acres"
myCol umn .Type = adDoubl e
myColumn.Attributes = adColNullable
myTable.Co1 umns.Append myCol umn
myCat.Tables.Append myTable
myCat.ActiveConnection = Nothing
End Sub
When we create the "mslink" column, we do so by appending 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, we may or may not want a field (column) to be required. If we
do not want a column to be required, we can specify the Field's
Attributes property as 'adColNullable'. This means the column can have
a value of 'null'.
The second way we can create a table is to use the 'XctiveX Data Objects
Library" Reference. A new Reference needs to be added before we can
run the procedure CreateDB3. When we open the References dialog box
to add "ActiveX Data Objects" as a Reference, we may see several
Libraries to choose from. We 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 C r e a t e D B 3 ( )
Dim myDB As New ADODB.Connection
Dim dbLocation As String
Dim ConnString As String
dbLocation = "C:\MicroStation V B A \ D a t a b a s e L i n k T e s t . m d b "
ConnString = " P r o v i d e r = M i c r o s o f t . J e t . O L E D B . 4 . O ; D a t a Source=" &
dbLocation
I Creating a Database from Scratch I 557
myDB.0pen C o n n S t r i n g
myDB.Execute " C r e a t e T a b l e Expenses ( m s l i n k C o u n t e r , " & -
When we select New, a new UDL file is created and we are asked to
enter the UDL file parameters.
ConnectivityService Provider
MediaCatalogDB OLE DB Provider
MediaCatalogMergedDBOLE DB Provider
MediaCataloaWebDB OLE DB Provider
Microsoft ISLM 1.1 OLE DB Provider
MicrosoftJet 3.51 OLE DB Provider
Sub C r e a t e D B 3 ( 1
D i m myDB As New A D O D B . C o n n e c t i o n
D i m d b L o c a t i o n As S t r i n g
D i m C o n n S t r i n g As S t r i n g
d b L o c a t i on = C : \ M i c r o S t a t i on V B A \ D a t a b a s e L i n k T e s t .mdb
ConnString = Provider=Microsoft.Jet.OLEDB.4.O;Data Source= & -
dbLocation
myDB.0pen C o n n S t r i n g
myDB.Execute C r e a t e T a b l e Expenses ( m s l i n k C o u n t e r , & ~
L o t L i n k L o n g , D e s c r i p t i o n C h a r ( 1 0 0 ) , Amount D o u b l e )
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. This allows us to connect to a database at a specific location:
It demonstrates that it is possible to work with databases without the use
of a UDL file. But what happens if the databases location or file name
changes? We need to change the code if we have hard-coded the
database connection information in our programs. If we are the only
one using the program, this may not seem like much of a problem. But if
multiple people are using our program, it is much easier to change a
UDL file than it is to change code, especially if we have Locked our
code in VBA. And even the most basic computer user can be instructed
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, we should just keep in mind that using a UDL file provides the
perfect combination of power and flexibility.
LINKINGMICROSTATION
ELEMENTSTO DATABASE
RECORDS
The process of linking a Microstation Element to a Database Record is
very simple.
Here is the declaration for CreateDatabaseLink:
Function CreateDatabaseLink(Ms1ink As L o n g , ~
E n t i t y As L o n g , ~
L i n k T y p e As M s d D a t a b a s e L i n k a g e , -
I s I n f o r m a t i o n As B o o l e a n , ~
DisplayableAttributeType As L o n g ) As D a t a b a s e L i n k
And here is an example using CreateDatabaseLink:
Sub D a t a b a s e L i n k A ( )
D i m myElem As E l e m e n t
D i m m y L i n k As D a t a b a s e L i n k
S e t myElem = CommandState.GetLocatedElement(True)
S e t rnyLink = C r e a t e D a t a b a s e L i n k ( 1 , 1, r n s d D a t a b a s e L i n k a g e O l e D b , -
T r u e , 0)
myEl e m . A d d D a t a b a s e L i n k m y L i n k
myElem. R e w r i t e
End Sub
I Creating Database Records using SQL I 565
Sub G e t D a t a b a s e L i n k s A ( 1
D i m myElem A s E l e m e n t
D i m m y l i n k s 0 As DatabaseLink
D i m m y L i n k As D a t a b a s e L i n k
D i m I As L o n g
S e t myElem = CommandState.GetLocatedElement(True)
myLi n k s = myEl em. G e t D a t a b a s e L i n k s
For I = L B o u n d ( m y L i n k s ) To U B o u n d ( m y L i n k s )
Set myLink = myLinks(1)
MsgBox m y L i n k . D a t a b a s e T y p e & v b C r & -
my L i n k . Ms 1 in k
Next I
End Sub
CREATING DATABASE
RECORDSUSING SQL
Thus far we have 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
566 I Chapter 27: Non-Graphical Info - Databases I
database is data! We need to know how to add records to the database
we created. We do this by using the same SQL Query Builder we used to
add a Table to the database. Lets add a record to the Lots Table.
A p p l i c a t i o n Oata\Oocuments\Bentley\WorkSpace\ & -
Projects\Exampl es\Database\Ol edb\Udl \ & ~
DatabaseLinkTest.ud1
D i m M S L i n k I D As L o n g
I Creating a User Interface to view Database Information I 567
If IsDate(txtDateSold.Text) Then
my RS ( Da teSo 1 d ) = CDa te ( txt Da teSol d .Text 1
" "
Else
myRS("DateSo1d") = Null
End If
If IsNumeric(txtAcres.Text) Then
myRS("Acres") = CDbl (txtAcres.Text.1
Else
myRS("Acres") = Null
End If
If IsNumeric(txtSa1eAmount.Text) Then
my RS ( "Sa 1 eAmoun t " ) = CDbl (txtSal eAmoun t .Text 1
Else
myRS("Sa1eAmount") = Null
End If
my RS . Update
End If
End Sub
I Chapter 27: Non-Graphical Info - Databases I
P r i v a t e Sub U s e r F o r m - I n i t i a l i z e 0
D i m myElem As E l e m e n t
D i m m y l i n k s 0 As D a t a b a s e L i n k
D i m m y L i n k As D a t a b a s e L i n k
D i m I As L o n g
D i m myDB As New A D O D B . C o n n e c t i o n
D i m myRS As New A D O D B . R e c o r d s e t
myDB.Open " F i l e name=" & U D L F i l e
S e t myElem = CommandState.GetLocatedElement(True)
my L i n k s = my E l em. G e t Da t a b a s e L i n k s
F o r I = L B o u n d ( m y L i n k s ) To U B o u n d ( m y L i n k s )
Set myLink = myLinks(1)
I f myLink.EntityNumber = 1 Then
myRS.Dpen " S e l e c t * f r o m L o t s Where m s l i n k = " &
myLink.Mslink, -
myDB, adOpenForwardOnly, adLockReadOnly
I f myRS.EDF = F a l s e Then
MSLinkID = myLink.Mslink
Else
t x t Dwne r . T e x t = my RS ( "Owner " )
End I f
c h k S o 1 d .V a 1 u e = my RS ( " So 1 d " 1
I f I s N u l l (myRS("DateSo1d")) Then
txtDateSold.Text = "I'
Else
t x t Da t e S o l d . T e x t = my RS ( " Da t e S o 1 d " )
End I f
Else
t x t Ac r e s . T e x t = my RS ( " A c r e s " )
End I f
I f I s N u l l (myRS("Sa1eAmount")) Then
I Review I 569
txtSaleAmount.Text =
Else
t x t Sa 1 e Amoun t .T e x t = my RS ( Sa 1 e Amoun t 1
End I f
myRS.Close
myDB.Close
E x i t Sub
End I f
my RS . C1 o s e
End I f
Next I
myDB. C1 o s e
End Sub
Tags are useful for storing and displaying information that is associated
with elements in a Design File. They are often used to display the same
type of information from file to file but 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.
The macros presented in this chapter are to be used with the project
named Building which is installed with Microstation.
In this Chapter:
Getting Information from Tags based on a Selection
Getting All Tags in a File
Working with Tagsets
Getting All Tags of All Files in a Folder
Changing a Tags Value
Changing multiple Tags in Multiple Files
Exporting Tag Information to a File
571
572 I Chapter 28:Tags I
Sub G e t S e l e c t e d T a g A ( 1
D i m myTag As T a g E l e m e n t
D i m myElemEnum As E l e m e n t E n u m e r a t o r
S e t myElemEnum = ~
A p p l i c a t i o n . A c t i v e M o d e 1 Reference.GetSelectedElements
W h i l e myElemEnum.MoveNext
S e l e c t Case m y E l e m E n u m . C u r r e n t . T y p e
Case M s d E l e m e n t T y p e . m s d E l e m e n t T y p e T a g
S e t myTag = myElemEnum.Current
If myTag Is N o t h i n g = F a l s e Then
MsgBox m y T a g . V a l u e
End If
End S e l e c t
Wend
End Sub
Sub G e t S e l e c t e d T a g B O
D i m myTag As T a g E l e m e n t
D i m myElemEnum As E l e m e n t E n u m e r a t o r
S e t myElemEnum = -
A p p l i c a t i o n . A c t i v e M o d e 1 Reference.GetSelectedElements
W h i l e myElemEnum.MoveNext
S e l e c t Case m y E 1 e m E n u m . C u r r e n t . T y p e
Case M s d E l e m e n t T y p e . m s d E l e m e n t T y p e T a g
S e t myTag = myElemEnum.Current
If myTag Is N o t h i n g = F a l s e Then
MsgBox myTag.TagDefinitionName & vbTab & myTag.Value
End If
End S e l e c t
I Getting Information from Tags based on a Selection I 573
Wend
End Sub
The Tags Name is very important. It can be used to help us know if the
Tags Value is telling us who created the drawing, who checked the
drawing, or who printed the drawing. G e t S e l e c t e d T a g B gives us more
information than the previous procedure but can be improved upon.
Lets get the Tags TagSetName.
Sub G e t S e l e c t e d T a g C O
Dim myTag A s TagElement
Dim myElemEnum A s ElementEnumerator
Set myElemEnum = ~
Application.ActiveModelReference.GetSelectedE1ements
Whi 1 e myEl emEnum.MoveNext
Select Case myE1emEnum.Current.Type
Case MsdElementType.msdE1ementTypeTag
Set myTag = myElemEnum.Current
If myTag Is Nothing = False Then
MsgBox myTag.TagSetName & vbTab & -
myTag.TagDefinitionName & vbTab & myTag.Value
End If
End Select
Wend
End Sub
The TagSet tells us to which groupa 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. Lets expand our code some more to include all Tags that
belong to the same TagSet.
In our next example, we are going to display Tag information again. And
we are going to make use of the selected Tag. But we are going to display
all Tags that are siblings to the selected tag. We do this by getting the all
Tags belonging to the selected tags BaseElement:
Sub G e t S e l e c t e d T a g D O
Dim myTag A s TagElement
Dim s i b T a g s 0 A s TagElement
Dim myElemEnum A s ElementEnumerator
Dim I As Long
574 I Chapter 28:Tags I
S e t myElemEnum = -
GETTINGALLTAGSIN A FILE
We have just seen how we can 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( 1
D i m myTag As T a g E l e m e n t
D i m myElemEnum As E l e m e n t E n u m e r a t o r
D i m myFi 1 t e r As New E l e m e n t S c a n C r i t e r i a
myFi 1 t e r . E x c l u d e A l 1 T y p e s
myFi 1 t e r . I n c l u d e T y p e msdEl e m e n t T y p e T a g
S e t myElemEnum = ~
Application.ActiveModelReference.Scan(myFi1ter)
W h i l e myElemEnum.MoveNext
S e t myTag = myElemEnum.Current
MsgBox myTag.TagSetName & v b T a b & m y T a g . T a g D e f i n i t i o n N a m e -
Sub G e t T a g s S e t s A O
D i m myTagSet A s T a g S e t
F o r Each myTagSet I n Application.ActiveDesignFile.TagSets
MsgBox myTagSet.Name
Next
End Sub
Now that we know how to identify each TagSet in a file we can look at
each TagDefinition in each TagSet in a file.
Sub G e t T a g s S e t s B ( )
D i m myTagSet A s T a g S e t
D i m myTagDef A s T a g D e f i n i t i o n
F o r Each myTagSet I n Application.ActiveDesignFile.TagSets
F o r Each myTagDef I n m y T a g S e t . T a g D e f i n i t i o n s
MsgBox myTagSet.Name & v b T a b & myTagDef.Name
Next
Next
End Sub
And now we will perform the same basic function, only we will display
more Tag information:
Sub G e t T a g s S e t s C ( )
D i m myTagSet A s T a g S e t
D i m myTagDef A s T a g D e f i n i t i o n
576 I Chapter 28:Tags I
F o r Each myTagSet I n A p p l ic a t i o n . A c t i v e D e s i g n F i 1 e . T a g s e t s
F o r Each myTagDef I n m y T a g S e t . T a g D e f i n i t i o n s
MsgBox "SetName: " & myTagSet.Name & v b C r & ~
Sub G e t F o l d e r T a g s ( )
D i m myDGN As D e s i g n F i l e
D i m myFSO As New Scripting.FileSystemObject
D i m m y F o l d e r As S c r i p t i n g . F o l d e r
D i m m y F i l e As S c r i p t i n g . F i l e
D i m myTagSet As T a g S e t
D i m myTagDef As T a g D e f i n i t i o n
D i m T a r g e t T a g s e t As S t r i n g
D i m myTag As T a g E l e m e n t
D i m myElemEnum As E l e m e n t E n u m e r a t o r
D i m myFi 1 t e r As New E l e m e n t S c a n C r i t e r i a
TargetTagset = " T i t l eB1 o c k "
Set myFolder = rnyFSO.GetFolder("C:\Docurnents and S e t t i n g s \ " & ~
"Examples\Building\Dgn")
I Getting All Tags of All Files in a Folder I 577
F o r Each m y F i l e I n m y F o l d e r . F i l e s
S e l e c t Case m y F i l e . T y p e
Case B e n t l e y M i c r o s t a t i o n D e s i g n F i l e
S e t myDGN = Application.OpenDesignFileForProgram( ~
myFile.Path, True)
F o r Each m y T a g S e t I n myDGN.TagSets
S e l e c t Case UCase(myTagSet.Name)
Case U C a s e ( T a r g e t T a g s e t 1
myFi 1t e r . E x c l u d e A l 1 T y p e s
m y F i 1t e r . I n c l u d e T y p e m s d E l e m e n t T y p e T a g
S e t myElemEnum = ~
myDGN.Mode1 s ( 1 ). S c a n ( m y F i 1 t e r )
Whi 1 e myEl emEnum.MoveNext
S e t myTag = myElemEnum.Current
D e b u g . P r i n t myFile.Name & vbTab & -
myTag.TagDefinitionName; v b T a b & -
This example opens each design file in the specified folder for
program. This means the file is opened in memory and is not displayed
in Microstation. Files can be opened and manipulated very quickly
when they do not need to be rendered to the screen.
One piece of information we are extracting in this example that we
hadnt extracted before is the High and Low elements of the ID property.
This ID property is very important because it provides a unique
identifier for an Element in Microstation and it persists from session to
session. So, storing the ID property of an Element in a database, for
example, would allow 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'SVALUE
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 then set a
Tag's value. Here's the procedure:
Sub C h a n g e T a g ( 1 D H i g h As L o n g , IDLow As L o n g , N e w V a l u e As S t r i n g )
D i m T a g I D As DLong
D i m myTag As T a g E l e m e n t
TagID.High = IDHigh
TagID.Low = IDLow
S e t myTag = Application.ActiveDesignFile.GetElementByID(Tag1D)
myTag.Value = NewValue
myTag.Rewrite
End Sub
When we have an ID, we can get its element by using the G e t E l emen t By1D
procedure. After we set the Tag's value based on the ID, we Rewrite the
Tag element. This procedure cannot be executed by itself. It needs
something else to 'call' it. Let's take a look at a procedure that does just
that:
Sub T e s t C h a n g e T a g A O
D i m myTag As T a g E l e m e n t
D i m myEnum As E l e m e n t E n u m e r a t o r
S e t myEnum = ActiveModel Reference.GetSe1 ectedElements
W h i l e myEnum.MoveNext
S e l e c t Case m y E n u m . C u r r e n t . T y p e
Case MsdElementType.msdElementTypeTag
ChangeTag m y E n u m . C u r r e n t . I D . H i g h , -
In the "real world, we would be storing the High and Low elements of
the ID property in a database or in some other storage mechanism. In
our example here, we get the ID property from selected elements. Then
we use our new procedure C ha ngeTa g to change the Tag's value.
I Changing multiple Tags in Multiple Files I 579
TagName) = 0 Then
myTag.Value = NewValue
myTag.Rewrite
End If
End If
Wend
Next
myDGN.Save
myDGN. C1 ose
End Sub
In this procedure, we open the specified file ForProgram,scan the file
for Tags with a specific TagSet and TagName and set its value. Heres a
procedure that makes use of ChangeTag2.
580 I Chapter 28:Tags I
Sub TestChangeTagB( 1
Dim myFSO As New Scripting.Fi1eSystemDbject
Dim myFolder As Scripting.Fo1der
Dim myFile As Scripting.File
Set myFolder = myFSO.GetFolder("C:\Docurnents and Settings\" & -
"A1 1 Users\Appl ication Data\" & -
"Examples\Building\Dgn")
For Each myFile In myFolder.Files
Select Case myFile.Type
Case "Bentley MicroStation Design File"
C hangeTag2 my Fi 1 e . Path , "Tit 1 eB1 oc k" , -
"Checked By", "J Winters"
End Select
Next
End Sub
Running the above procedure changes each design file in the specified
folder and saves it. All Tags named "Checked By" in the "TitleBlock"
TagSet are given a value of "J Winters". Powerful? Yes. Dangerous?
Potentially. Let's be careful so the programming we do is a benefit to our
employers instead of the derailing of our careers.
EXPORTINGTAGlNFORMATlON TO A FILE
All of the MessageBoxes and Debugprint 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.
Sub ExportFolderTagsO
I Exporting Tag Information to a File I 581
D i m myDGN As D e s i g n F i l e
D i m myFSO As New Scripting.FileSystem0bject
D i m m y F o l d e r As S c r i p t i n g . F o 1 d e r
D i m m y F i l e As S c r i p t i n g . F i l e
D i m myTagSet As T a g S e t
D i m myTagDef As T a g D e f i n i t i o n
D i m T a r g e t T a g s e t As S t r i n g
D i m myTag As T a g E l e m e n t
D i m myElemEnum As E l e m e n t E n u m e r a t o r
D i m m y F i l t e r As New E l e m e n t s c a n c r i t e r i a
D i m F F i l e As Long
FFile = FreeFile
Open " C : \ M i c r o S t a t i o n VBA\Tags. t x t " F o r O u t p u t As B F F i 1 e
TargetTagset = "TitleBlock"
S e t rnyFolder = myFSO.GetFolder("C:\Docurnents and S e t t i n g s \ " & -
"Bentley\WorkSpace\Projects\" & ~
"Examples\Building\Dgn")
F o r Each m y F i l e I n m y F o l d e r . F i l e s
S e l e c t Case m y F i l e . T y p e
Case " B e n t l e y M i c r o s t a t i o n D e s i g n F i l e "
S e t myDGN = Application.OpenDesignFileForProgram( ~
myFile.Path, True)
F o r Each myTagSet I n myDGN.TagSets
S e l e c t Case UCase(myTagSet.Name)
Case U C a s e ( T a r g e t T a g s e t 1
m y F i 1t e r . E x c l u d e A l 1 T y p e s
m y F i l t e r . I n c l u d e T y p e msdElementTypeTag
S e t myElemEnum =
myDGN.Models(l).Scan(myFilter)
Whi 1 e myEl emEnum.MoveNext
S e t myTag = myElemEnum.Current
P r i n t #FFile, m y F i l e . N a m e & v b T a b & -
myTag.TagDefinitionName; v b T a b & -
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 we will create an .htm file.
Sub ExportFolderTagsToHTMLO
D i m myDGN As D e s i g n F i l e
D i m myFSO As New Scripting.FileSystem0bject
D i m m y F o l d e r As S c r i p t i n g . F o l d e r
D i m m y F i l e As S c r i p t i n g . F i l e
D i m m y T a g S e t As T a g S e t
D i m m y T a g D e f As T a g D e f i n i t i o n
D i m T a r g e t T a g s e t As S t r i n g
D i m myTag As T a g E l e m e n t
D i m myElemEnum As E l e m e n t E n u m e r a t o r
D i m m y F i 1 t e r As New E l e m e n t S c a n C r i t e r i a
D i m F F i l e As L o n g
FFile = FreeFile
Open " C : \ M i c r o S t a t i o n VBA\Tags. h t m " F o r O u t p u t As # F F i l e
P r i n t SIFFile, " < t a b l e width=660 border=l>" & vbCr
P r i n t SIFFile, vbTab & " < t r > < t d > < / t d > < / t r > " & vbCr
TargetTagset = " T i t l eB1 o c k "
Set myFolder = myFSO.GetFolder("C:\Documents and S e t t i n g s \ " & -
"Examples\Building\Dgn")
For Each m y F i l e I n m y F o l d e r . F i l e s
S e l e c t Case m y F i l e . T y p e
Case " B e n t l e y M i c r o s t a t i o n D e s i g n F i l e "
P r i n t I I F F i l e , " < t r > < t d colspan=5>" & ~
m y f i l e . Path & " < / t d > < / t r > " & vbCr
P r i n t # F F i l e , vbTab & " < t r > < t d > T a g S e t Name</td>" & -
S e t myDGN = Application.OpenDesignFileForProgram( -
I Exporting Tag Information to a File I 583
myFile.Path, True)
F o r E a c h m y T a g S e t I n myDGN.TagSets
S e l e c t Case UCase(myTagSet.Name)
Case U C a s e ( T a r g e t T a g s e t . 1
m y F i 1t e r . E x c l u d e A l l T y p e s
myFi 1t e r . I n c l u d e T y p e m s d E l e m e n t T y p e T a g
S e t myElemEnum = -
myDGN.Models(l).Scan(myFilter)
W h i l e myElemEnum.MoveNext
S e t myTag = myElemEnum.Current
P r i n t #FFile, vbTab & " < t r > < t d > " & -
" < t d > " & myTag.Value & " < / t d > " & -
" < t d > " & myTag.ID.High & " < / t d > " & -
" < t d > " & myTag. ID.Low & " < / t d > < / t r > " & vbCr
Wend
End S e l e c t
Next
myDGN.Close
End S e l e c t
Next
P r i n t BFFile, "</table>"
C1 o s e B F F i 1 e
End Sub
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 not only tags in the active design file but in every
file in a specific folder and so forth. Reading values and changing them
is easy to do as we 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 in a web browser.
We will provide an example of extracting Tag information into
Microsoft Excel in a later chapter. Tag information could also be
extracted to a database or used as the body of an e-mail. We are only
limited by our imagination.
.
29 XML
In this Chapter:
El WhatisXML?
XML File Structure
Reading XML Files With VBA
WHATIS XML?
XML is an abbreviation for Extensible Markup Language. XML
documents often have a file extension of xml.Even though XML files
conform to a common specification, the data it contains can vary greatly
from file to file and from structureto structure.
Companies and organizations create their own XML Document
Definitions to store their own type of data with their own data
585
I Chapter 29: XML I
structures. For example, there are XML documents to store financial
transactions, another to store GIS information, and yet another 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 FILESTRUCTURE
The XML File Structure is what allows it to be so flexible and powerful.
Here is a small snippet of an XML file:
READINGXML FILES
There are several ways we can read an XML file. We 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. Adding a
Reference to a Microsoft XML DLL file is the first step.
S u b ReadXMLFi 1 e ( 1
D i m myXML As New MSXML2.DOMDocument
D i m m y X E l e m As M S X M L 2 . I X M L D O M E l e m e n t
D i m m y X R e c o r d As M S X M L 2 . I X M L D O M E l e m e n t
D i m m y X F i e l d As M S X M L 2 . I X M L D O M E l e m e n t
588 I Chapter 29: XML I
myXML.async = False
myXML.validateOnParse = False
myXML. Load " c : \ M i c r o S t a t i o n V B A \ p a r c e l . x m l "
S e t myXElem = myXML.documentElement
F o r Each myXRecord I n m y X E l e m . c h i l d N o d e s
Debug . P r i n t "****NEW RECORD****"
F o r Each m y X F i e l d I n m y X R e c o r d . c h i 1 d N o d e s
Debug. P r i n t m y X F i e l d . baseName & " .. " & myXField.Text
Next
Next
End Sub
Our first example loads the XML file and then prints each record with
its children (the fields) in the Immediate window.
The procedure ReadXMLFi 1 e starts at the beginning of the file and looks
at each item until it reaches the end.
If we only want to display the record with a mslink of 531, we could use
the following code:
myXML.validate0nParse = False
myXML. Load "c:\MicroStation VBA\parcel .xml "
S e t myXElem = myXML.documentElement
S e t myXNode = myXML.selectSingleNode("//parcel [ m s l i n k = 5 3 1 1 " )
For Each m y X F i e l d I n rnyXNode.childNodes
D e b u g . P r i n t rnyXField.baseNarne & " .. " & rnyXField.Text
Next
End Sub
Now we are getting somewhere. Now we are letting the Microsoft XML
Library do the work for us. It is fast, efficient, and very easy to modify It
doesn't 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 53 1.
If we take a look at the screen capture shown previously, we will see that
there are a good number of fields we have to work with. One of them is
"parc-value". We could modify the code above, looking for "parc-value"
instead of "mslink" and find a parcel valued at 250,000. But the code
would only work if a parcel was 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.
myXNode.selectSingleNode(".//parc-area") . T e x t
Next
End Sub
Now we have expanded the filter to only return nodes with parc-are$
values between 1 and 2 Acres. We are once again exporting the mslink:
owner,and parc-area node values to the Immediate Window.
Sub ReadXMLFi 1 e l ( 1
D i m myXML As New MSXMLP.DDMDocument
D i m myWSheets As MSXML2.IXMLDDMNodeList
D i m myWSheet As MSXMLP.IXMLDOMNode
D i m myRows As MSXML2.IXMLDOMNodeList
D i m myRow As MSXMLP.IXMLDOMNode
D i m m y c e l l s As MSXML2.IXMLDDMNodeList
D i m m y C e l l As MSXMLP.IXMLDDMNode
myXML.async = False
myXML.validateOnParse = False
myXML. Load " c : \ M i c r o S t a t i o n V B A \ t o w e r d a t . x m l "
S e t myWSheets = myXML.getElementsByTagName("Worksheet")
F o r Each myWSheet I n myWSheets
S e t myRows = myWSheet.selectNodes(".//Row")
F o r Each myRow I n myRows
S e t myCel1s = myRow.se1 e c t N o d e s ( " . / / C e l l " )
F o r Each m y C e l l I n m y c e l l s
Debug.Print myCell.Text
Next
Next
Next
End Sub
S u b ReadXMLFi 1 e 2 ( )
I Chapter 29: XML I
D i m myXML As New MSXMLP.DOMDocument
D i m myWSheets As MSXML2.IXMLDOMNodeList
D i m myWSheet As MSXMLP.IXMLDOMNode
D i m myRows As MSXML2.IXMLDOMNodeList
D i m myRow As MSXMLP.IXMLDOMNode
D i m m y c e l l s As MSXML2.IXMLDOMNodeList
D i m m y C e l l As MSXMLP.IXMLDOMNode
D i m myRowAtt As M S X M L 2 . I X M L D O M A t t r i b u t e
D i m myCol A t t As MSXMLP. I X M L D O M A t t r i b u t e
D i m m y A t t s As MSXML2.IXMLDOMNamedNodeMap
D i m CurRow As L o n g
D i m C u r C o l As L o n g
myXML.async = False
myXML.validateOnParse = False
myXML.Load " c : \ M i c r o S t a t i o n V B A \ t o w e r d a t . x m l "
S e t myWSheets = myXML.getElementsByTagName("Worksheet")
F o r Each myWSheet I n myWSheets
CurRow = 1
CurCol = 1
S e t myRows = myWSheet.selectNodes(".//Row")
F o r Each myRow I n myRows
Set myAtts = myRow.Attributes
S e t myRowAtt = myAtts.getNamedItem("ss:Index")
I f N o t myRowAtt I s N o t h i n g Then
CurRow = myRowAtt.Text
Else
CurRow = CurRow + 1
End I f
CurCol = 1
S e t myCel1s = myRow.se1 e c t N o d e s ( " . / / C e l l " )
F o r Each m y C e l l I n m y c e l l s
Set myAtts = myCell.Attributes
S e t myCol A t t = myAtts.getNamedItem("ss: I n d e x " )
I f N o t m y C o l A t t I s N o t h i n g Then
CurCol = myColAtt.Text
Else
CurCol = CurCol + 1
End I f
I Review I 597
In the above Procedure, ReadXMLFi 1 e2, we are printing the Row and
Column of the Cell as well as the Text. We do this by looking for Index
Attributes of Rows and Cells. When found, we use them as the current
values for the variables CurRow and CurCol? When they are not
found we increase the CurRow and CurCol values by 1 (based on
whether we are looking at a Row Object (when we increase the
CurRow variable) or a Cell Object (when we increase the CurCol
variable).
.
Here is the output of ReadXMLFi 1 e2. Notice that some ofthe Cells we find
have an Empty String () for their Text property.
REVIEW
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, a little time may be needed
to learn how to traversea new XML file type.
30 Batch Processing
The ability to rapidly process 10, 100, or even 1000 files is as simple as it
is powerful.
In this Chapter:
Processing Files listed in an ASCII File
Processing All Files in a Folder
Processing All Files in a Folder and SubFolders
Creating a User Interface for File Selection
Logging Batch File Processing
599
600 I Chapter 30: Batch Processing I
The first example we will examine opens this file ProcessThese.txt and
reads each line in it. Each line in the file displays in a MessageBox.
Sub ProcessASCIIo
D i m F i l e T o O p e n As S t r i n g
D i m B a t c h F i l e As S t r i n g
D i m F F i l e As L o n g
FFile = FreeFile
BatchFile = " C : \ M i c r o S t a t i o n VBA\BatchProcessing\ProcessThese.txt"
Open B a t c h F i l e F o r I n p u t As B F F i l e
W h i l e EOF(FFi1e) = False
L i n e I n p u t # F F i 1 e , F i 1 eToDpen
MsgBox F i 1 eToDpen
Wend
End Sub
Let's make a change to the ASCII file format. After the path and name of
the file we will have lines specifying which Levels we want to process.
Each "Level" line in the file should begin with a Tab and then should
have the Level name. After adding Level names to the file, we will
perform a SaveAs to the file with a new filename of
ProcessTheseLevels.txt.
C : \ M i c r O s t a t i o n uBA\BatchProcessing\File A.dgn
Level A
Level B
Level c
Level 0
Level E
Level F
<:\Mi c r o s t a t i o n uBA\BatchProcessing\File C.dgn
Level A
Level c
Level E
<:\Mi c r o s t a t i o n uBA\BatchProcessing\File E.dgn
Level B
Level 0
Level F
Now, instead of opening each file and displaying the file name in a
MessageBox we will open each file and display each Level name in a
MessageBox.
Sub ProcessASCIICo
D i m FileToOpen As S t r i n g
D i m B a t c h F i l e As S t r i n g
D i m s t r L I N As S t r i n g
D i m F F i l e As Long
FFile = FreeFile
BatchFile = -
"C:\MicroStation VBA\BatchProcessing\ProcessTheseLevels.txt"
Open B a t c h F i l e F o r I n p u t A s # F F i l e
W h i l e EOF(FFi1e) = False
Line Input #FFile, strLIN
S e l e c t Case L e f t ( s t r L I N , 1)
Case v b T a b
MsgBox "Do s o m e t h i n g t o L e v e l " & ~
Re p 1 a c e ( s t r L IN , v b T a b , " " 1
Case E l s e
FileToOpen = strLIN
Application.OpenDesignFile F i l e T o O p e n , T r u e
End S e l e c t
Wend
End Sub
602 I Chapter 30: Batch Processing I
The Procedure ProcessASCIIC works very well as long as the files
specified in the file exist. What happens if we attempt to open a file that
does not exist?
.
Prior to attempting to open any file, we should make sure the file exists.
)" "
Rep 1 ace ( st r L I N , v bT a b ,
End If
Case Else
FileToOpen = strLIN
If Dir(FileTo0pen) = Then
"I'
FileIsOpen = False
Else
FileIsOpen = True
Application.OpenDesignFile FileToOpen, True
End If
End Select
Wend
End Sub
I Processing All Files in a Folder I 603
Sub ProcessInFolderB()
D i m myFSO As New Scripting.FileSystem0bject
D i m m y F o l d e r As S c r i p t i n g . F o l d e r
D i m m y F i l e As S c r i p t i n g . F i l e
D i m R o o t F o l d e r As S t r i n g
RootFolder = C:\MicroStation VBA\BatchProcessing
S e t my F o l d e r = my FSD. G e t F o l d e r ( R o o t F o l d e r )
F o r Each m y F i l e I n m y F o l d e r . F i l e s
S e l e c t Case UCase( R i g h t ( m y F i 1 e . Name, 3 ) )
C a se DG N
Debug. P r i n t m y f i l e . P a t h
End S e l e c t
Next
End Sub
Instead of looking at the Type, we are now looking 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 RootFolder to the Debug window, we should open the file in
Microstation and Process it. The procedure P r o c e s s I n F o l derB works
well as long as the files we want to process are in the folder
C:\MicroStution VBA\ButchProcessing.Of course, we wouldnt want to
change our code every time we need to process files in a different folder,
now would we?
Sub P i c k A F o l d e r O
Dim myshell A s New Shell32.Shell
Dim myRootFolder A s She1132.Folder3
Set myRootFolder = myshell .BrowseForFolder(O, " P i c k " , 0)
MsgBox myRootFolder.Self .Path
End Sub
606 I Chapter 30: Batch Processing I
Can it be any easier? Add a simple reference
and a few lines of code and users can select
any folder on their computer or anywhere on
their network. Lets put it to work now in
conjunction with our Batch Processing code.
Sub ProcessInFolderD( 1
Dim myFSO As New Scripting.FileSystem0bject
Dim myFolder As Scripting.Folder
Dim myFile As Scripting.File
Dim myshell As New Shell3P.Shell
Dim myRootFolder As She1132.Folder3
Set myRootFolder = myshell .BrowseForFolder(O, Pick, 0)
Set my Fol der = my FSO. Get Fol der (my Root Fol der . Sel f . Path)
For Each myFile In myFolder.Files
Select Case UCase( Right(myFi 1 e. Name, 3))
Cas e D G N
Debug. Print myfile. Path
End Select
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.
Sub ProcessInFolderE( 1
Dim myFSO As New Scripting.FileSystem0bject
Dim myFolder As Scripting.Folder
Dim myshell As New Shell3P.Shell
I Processing All Files in a Folder and SubFolders I 607
Here is the
output of
this
Procedure:
hold the files that have been selected for batch processing. We want the
user to be able to choose which folder to select from. Buttons will be
used 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.
Private S u b btnBrowsecC1 i c k ( 1
Dim myFSO A s New Scripting.FileSystem0bject
Dim myFolder A s Scripting.Folder
Dim myFile A s Scripting.File
Dim myshell A s New Shell32.Shell
Dim myRootFolder A s She1132.Folder3
Set myRootFolder = myshell .BrowseForFolder(O, " P i c k " , 0)
If myRootFolder Is Nothing Then Exit S u b
Set myFolder = myFSO.GetFolder(myRootFolder.Se1f.Path)
txtCurrentFolder.Text = myRootFolder.Self.Path
1 s t Fi 1 es I n Fol der .C1 ear
For Each myFile In myFolder.Files
Select Case U C a s e ( R i g h t ( m y F i l e . N a m e , 3))
Case D G N
" "
If IsFileIn(myFile.Path, IstFilesToProcess)
= False Then
610 I Chapter 30: Batch Processing I
1 s t F i 1 e s I n F o l d e r . A d d I t e m myFi l e . P a t h
End I f
End S e l e c t
Next
End Sub
P r i v a t e Sub btnGo-Click0
D i m I A s Long
F o r I = 1 To 1stFilesToProcess.ListCount
Debug.Print lstFilesToProcess.List(1 - 1)
Next I
End Sub
P r i v a t e Sub btnIntoSelectedLClick0
MoveSelection I s t F i l e s I n F o l d e r , 1stFilesToProcess
End Sub
P r i v a t e Sub IstFilesInFolder-DblC1 i c k ( B y V a 1 C a n c e l As -
MSForms. R e t u r n B o o l e a n )
MoveSelection I s t F i l e s I n F o l d e r , 1stFilesToProcess
End Sub
I Creating a User Interface for File Selection I 611
P r i v a t e S u b I s t F i l e s T o P r o c e s s - D b l C l i c k ( B y V a 1 C a n c e l As -
MSForms.ReturnBoo1ean)
MoveSel e c t i o n 1 s t F i l e s T o P r o c e s s , 1 s t F i 1 e s I n F o l d e r
End S u b
F u n c t i o n I s F i l e I n ( F i 1 e P a t h A s S t r i n g , ListToCheck A s L i s t B o x ) As Boolean
D i m I As L o n g
IsFileIn = False
F o r I = 1 To L i s t T o C h e c k . L i s t C o u n t
If StrComp(ListToCheck.List(1 - l), F i l e p a t h ) = 0 Then
IsFileIn = True
Exit Function
End I f
Next I
End F u n c t i o n
F u n c t i o n M o v e S e l e C t i O n ( L i s t B o x F r o m As L i s t B o x , ListBoxTo As L i s t B o x )
D i m I As L o n g
F o r I = 1 To L i s t B o x F r o m . L i s t C o u n t
I f ListBoxFrom.Selected(1 - 1) = T r u e Then
If IsFileIn(ListBoxFrom.List(1 - 11, L i s t B o x T o )
= F a l s e Then
ListBoxTo.AddItem ListBoxFrom.List(1 - 1)
Else
End I f
End I f
Next I
For I = L i s t B o x F r o m . L i s t C o u n t To 1 S t e p - 1
I f ListBoxFrom.Selected(1 - 1) = T r u e Then
ListBoxFrom.RemoveItem I - 1
End I f
Next I
End F u n c t i o n
612 I Chapter 30: Batch Processing I
A few items worthy of mention that may not be apparent at first glance:
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.
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
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.
Although not in this project, the addition of a few lines of code
allows the user to select a CheckBox specifying to Search Sub-
Folders as well as the selected folder.
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
LOGGINGFILEBATCHPROCESSING
Log files are nothing new. They tell us what has been done, what hasnt
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:
Using a Log File; for example, an ASCII file with a .log file
extension.
Track Processing Activities in a Database.
Store Processing Information in the Registry.
Log activities over the Internet.
E-mail Transaction Logs to various recipients.
Sub T e s t L o g F i 1 e ( )
W r it e T o Log " D p e n i n g C : \ t e s t . d g n "
End Sub
Sub W r i t e T o L o g ( L o g F i 1 e T e x t As S t r i n g )
D i m m y V B E As O b j e c t
D i m x S p l i t As V a r i a n t
D i m F F i l e As L o n g
S e t myVBE = Application.VBE
xSpl i t = S p l it ( m y V B E . A c t i v e V B P r o j e c t . F i l e n a m e , " \ " )
xSplit(UBound(xSp1it)) = "BatchProcess.log"
FFile = FreeFile
Open J o i n ( x S p l i t , "\"I F o r Append As B F F i l e
P r i n t #FFile, LogFileText
C1 o s e # F F i 1 e
End Sub
'General D e c l a r a t i o n s Area
P u b l i c D e c l a r e F u n c t i o n GetComputerName L i b " k e r n e 1 3 2 " A l i a s -
"GetComputerNameA" ( B y V a l l p B u f f e r As S t r i n g , ~
n S i z e As L o n g ) As L o n g
Sub T e s t L o g F i 1 eB( 1
W r it e T o LogB " D p e n i n g C : \ t e s t . d g n "
End Sub
Sub W r i t e T o L o g B ( L o g F i 1 e T e x t As S t r i n g )
D i m m y V B E As O b j e c t
D i m x S p l i t As V a r i a n t
D i m F F i l e As L o n g
I Logging File Batch Processing I 615
S e t myVBE = Application.VBE
x S p l it = Spl i t ( m y V B E . A c t i v e V B P r o j e c t . Filename, \ I
xSplit(UBound(xSp1it)) = BatchProcess- & ThisComputerName & .log
FFile = FreeFile
Open J o i n ( x S p l i t , \ ) F o r Append As # F F i l e
P r i n t BFFile, LogFileText
C1 o s e B F F i 1 e
End Sub
Function ThisComputerNameO As S t r i n g
ThisComputerName = Space(255)
GetComputerName T h i s C o m p u t e r N a m e , L e n ( T h i s C o m p u t e r N a m e )
ThisComputerName = Left(ThisComputerName, I n S t r ( 1 , -
ThisComputerName, C h r ( 0 ) ) - 1)
End F u n c t i o n
Now, each computer running this code has its own Batchprocess .log
file. We could base the log file on the UserNamethat 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.
Sub Wri t e T o R e g i s t r y ( )
Savesetting "MicroStation VBA", "Batch Processing", ~
End Sub
Sub R e a d F r o m R e g i s t r y ( 1
D i m L a s t F i l e As S t r i n g
D i m C u r r e n t F i l e As S t r i n g
LastFile = GetSetting("MicroStation VBA", ~
http://www.google. com/search?q=MicroStation
Google returns thousands upon thousands of pages related to
MicroStation.
I Logging File Batch Processing I 617
h ttp://www.microstationlogging.com?filename=fiIea.dgn
this fictitious web site could log the file, the datehime, 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"
S u b LogToWeb()
Dim LocalFile A s String
Dim myInet A s New InternetExplorer
Local File = "filea.dwg"
myInet.Navigate -
"http://www.microstationlogging.com?filename=" & -
Local Fi 1 e
While myInet.Busy
DoEvents
Wend
End S u b
The code consists of only a few lines. LogToWeb navigates to the specified
URL, in this case, the fictitious website, www.rnicrostation/ogging.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.
I Chapter 30: Batch Processing I
Sub GetFromWebO
D i m I As Long
D i m L o c a l F i l e As S t r i n g
D i m m y I n e t As New I n t e r n e t E x p l o r e r
D i m TheURL As S t r i n g
D i m x S p l i t A As V a r i a n t
D i m x S p l i t B As V a r i a n t
D i m x S p l i t C As V a r i a n t
D i m x S p l i t D As V a r i a n t
D i m T r a i n i n g D a t e As D a t e
D i m T r a i n i n g L o c a t i o n As S t r i n g
D i m T r a i n i n g c o s t As S t r i n g
D i m M s g B o x T e x t As S t r i n g
The U R L = " h t t p : / / b e n t 1 e y in s t it u t e . b e n t 1 e y . c om/ " & ~
"courseinfo.aspx?course=TRC001810-1/0001"
m y I n e t . N a v i g a t e TheURL
While myInet.ReadyState <> READYSTATE-COMPLETE
DoEvents
Wend
xSplitA = Split(myInet.Document.body.innerHTML, "InPersonLabClasses")
x S p l it B = S p l it ( x S p l it A ( 11 , " <TR " 1
F o r I = L B o u n d ( x S p l i t B 1 To U B o u n d ( x S p 1 i t B )
xSplitC = Split(xSplitB(I1, "<TD>")
I f IsArray(xSplitC1 = T r u e Then
I f UBound(xSplitC1 >= 3 Then
x S p l it D = S p l it ( x S p l it C ( 11 , " < " )
TrainingLocation = xSplitD(0)
x S p l it D = S p l i t ( x S p l it C ( 3 ) , "<" )
Trainingcost = xSplitD(0)
MsgBoxText = MsgBoxText & T r a i n i n g D a t e & vbTab & -
The Bentley Institute sponsors VBA Training from time to time. We can
find training dates, locations, and costs on their web site. This macro is
designed to display this information in a MessageBox.
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.
If the Reference does not show up in the list, clicking the Browse button
and browsing to C:\Windows\system32\cdosys.d//does the trick.
620 I Chapter 30: Batch Processing I
CDO. What does it stand for? Collaborative Data Objects. In plain
English, e-mail.
Sub T e s t E m a i 1 ( )
D i m m y M a i l As New CDD.Message
myMa i1 . T o = " b a t c h@mic r o s t a t ion1 o g g i n g . com"
myMa i1 . From = " b a t c h@mic r o s t a t ion1 o g g i n g . com"
myMail . S u b j e c t = " M i c r o s t a t i o n VBA B a t c h Process Log"
m y M a i l .HTMLBody = " < b > F i l e name: f i l e a . d g n < / b > < b r > " & -
"Date: 1/1/2005"
myMai 1 . C o n f i g u r a t i o n . F i e l ds. I t e m ( " h t t p : //schemas . m i c r o s o f t . corn/" & -
"cdo/configuration/sendusing") = 2
myMai 1 . C o n f i g u r a t i o n . F i e l ds. I t e r n ( " h t t p : //schemas . m i c r o s o f t . corn/" & ~
"cdo/configuration/smtpserver") = -
" y o u r s m t p s e r v e r . com"
rnyMail.Configuration.Fields.Item("http://schernas.rnicrosoft.com/" & ~
"cdo/configuration/smtpserverport") = 25
myMai 1 . C o n f i g u r a t i o n . F i e l d s . U p d a t e
myMai 1 . S e n d
End Sub
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 T e s t E m a i 1 2 ( 1
D i m m y M a i l As New CDD.Message
myMa i1 .To = " b a t c h@mic r o s t a t io n 1 o g g i n g . com"
myMa i1 . From = " b a t c h@mic r o s t a t io n 1 o g g i n g . com"
myMail . S u b j e c t = " M i c r o s t a t i o n VBA B a t c h Process Log"
myMai 1 .HTMLBody = " < b > F i 1 e name: f i 1 ea . d g n < / b > < b r > " & -
"cdo/configuration/sendusing") = 2
rnyMail.Configuration.Fields.Item("http://schernas.rnicrosoft.com/" & ~
"cdo/configuration/smtpserver") = -
I Review I 621
" y o u r s r n t p s e r v e r . corn"
myMail.Configuration.Fields.Item("http://schemas.microsoft.com/" & ~
"cdo/configuration/smtpserverport") = 2 5
rnyMail.Configuration.Fie1ds.Update
.
myMa i1 AddAtta chment "C :\ t e s t dgn".
rnyMai 1 . S e n d
End Sub
The Standards Checker helps us to 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.
Before we take any more time on the Standards Checker we should note
that unhandled errors that occur in our Standards Checker routines can
cause severe errors. Some may even shut down Microstation completely.
So, we need to take care when working with Standards Checker code.
We should make sure we save our VBA Projects often and verify that
any DGN files open in Microstation are also saved.
In this Chapter:
Basics of implementing the Standards Checker
Standards Checker Settings
Checking for Standards
Standards Checker Reporting
Automatically Loading Custom Standards Checker Add-Ins
623
624 I Chapter 31: The Standards Checker I
General D e c l a r a t i o n s
Implements IStandardsChecker
P r i v a t e Sub IStandardsChecker-AddedCheckerToStandardsCheckerApps(
B y V a l A p p l i c a t i o n X M L N o d e As O b j e c t )
End Sub
P r i v a t e P r o p e r t y Get IStandardsChecker-CallForEachModelO As B o o l e a n
End P r o p e r t y
P r i v a t e Sub IStandardsChecker-CreateSettings()
End Sub
P r i v a t e Sub IStandardsChecker-DeleteSettings()
End Sub
P r i v a t e P r o p e r t y G e t I S t a n d a r d s C h e c k e r - D e s c r i p t i o n 0 As S t r i n g
End P r o p e r t y
P r i v a t e P r o p e r t y Get IStandardsChecker-DialogStringO As S t r i n g
End P r o p e r t y
I Basics of Implementing the Standards Checker I 625
P r i v a t e Sub I S t a n d a r d s C h e c k e r - E d i t S e t t i n g s ( B y V a 1 -
I s R e a d O n l y As B o o l e a n )
End Sub
P r i v a t e P r o p e r t y Get I S t a n d a r d s C h e c k e r - F o u n d s e t t i n g s o As B o o l e a n
End P r o p e r t y
P r i v a t e Sub IStandardsChecker-GetFixDetail(Fixes0 As S t r i n g , -
ByVal S e l e c t e d F i x As Long, F i x P r o p e r t i e s L a b e l As S t r i n g , -
F i x p r o p e r t i e s o As S t r i n g )
End Sub
P r i v a t e P r o p e r t y Get IStandardsChecker-Hassettings0 As B o o l e a n
End P r o p e r t y
P r i v a t e P r o p e r t y Get IStandardsChecker-IdentityStringO As S t r i n g
End P r o p e r t y
P r i v a t e Sub I S t a n d a r d s C h e c k e r - R u n C h e c k ( B y V a 1 -
ModelToCheck As ModelReference, -
ByVal F i r s t M o d e l As Boolean, ByVal O p t i o n s As Long)
End Sub
P r i v a t e P r o p e r t y Get I S t a n d a r d s C h e c k e r - V e r s i o n S t r i n g O As S t r i n g
End P r o p e r t y
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. Lets implement a Standards Checker that does nothing
other than implementing the most basic properties and uses
626 I Chapter 31: The Standards Checker I
Debug.Print statements so we can see the order in which the events take
place.
Standards Check A
The following code is placed in a Class Module named
clsStandChe&A.
O p t i o n E x p l ic i t
Implements IStandardsChecker
P r i v a t e Sub I S t a n d a r d s C h e c k e r - A d d e d C h e c k e r T o S t a n d a r d s C h e c k e r A p p s (
B y V a l A p p l i c a t i o n X M L N o d e As O b j e c t )
D e b u g . P r i n t "AddedCheckerToStandardsCheckerApps"
End Sub
P r i v a t e P r o p e r t y Get IStandardsChecker-CallForEachModelO As B o o l e a n
Debug.Print " C a l l ForEachModel"
IStandardsChecker-Call ForEachModel = True
End P r o p e r t y
P r i v a t e Sub IStandardsChecker-CreateSettings()
Debug.Print "CreateSettings"
End Sub
P r i v a t e Sub IStandardsChecker-DeleteSettings()
Debug.Print "DeleteSettings"
End Sub
P r i v a t e P r o p e r t y G e t I S t a n d a r d s C h e c k e r - D e s c r i p t i o n 0 As S t r i n g
Debug. P r in t " Desc r ip t io n "
IStandardsChecker-Description = "VBA StandChk A Desc"
End P r o p e r t y
P r i v a t e P r o p e r t y Get IStandardsChecker-DialogStringO As S t r i n g
Debug. P r in t " D i a 1 o g S t r in g "
IStandardsChecker-Dialogstring = "VBA StandChk A D i a l "
End P r o p e r t y
P r i v a t e P r o p e r t y Get I S t a n d a r d s C h e c k e r - F o u n d S e t t i n g s O As B o o l e a n
Debug. P r i n t " F o u n d S e t t i n g s "
End P r o p e r t y
P r i v a t e Sub IStandardsChecker-GetFixDetail(Fixes0 As S t r i n g , -
B y V a l S e l e c t e d F i x As L o n g , -
F i x P r o p e r t i e s L a b e l As S t r i n g , -
F i x p r o p e r t i e s o As S t r i n g )
Debug.Print "GetFixDetail"
End Sub
P r i v a t e P r o p e r t y Get IStandardsChecker-HasSettingsO As B o o l e a n
Debug. P r i n t " H a s S e t t i n g s "
IStandardsChecker-Hassettings = True
End P r o p e r t y
P r i v a t e P r o p e r t y Get IStandardsChecker-IdentityStringO As S t r i n g
Debug . P r in t " I d e n t ityS t r in g "
IStandardsChecker-IdentityString = "VBA-SC-A"
End P r o p e r t y
P r i v a t e Sub I S t a n d a r d s C h e c k e r - R u n C h e c k ( B y V a 1
M o d e l T o C h e c k As M o d e l R e f e r e n c e , -
B y V a l F i r s t M o d e l As B o o l e a n , -
B y V a l O p t i o n s As L o n g )
Debug . P r in t " I d e n t ityS t r in g "
End Sub
P r i v a t e P r o p e r t y Get IStandardsChecker-VersionStringO As S t r i n g
Debug. P r in t " Ve r s ion St r in g "
IStandardsChecker-Versionstring = "1.0.0.0"
End P r o p e r t y
Now we need to write some code that will add the above Class Module
to the Standards Checker in Microstation. This code is located in a
Code Module.
O p t i o n E x p l ic i t
P r i v a t e S t a n d C h k As c l s S t a n d C h e c k A
Sub Addchecker()
628 I Chapter 31: The Standards Checker I
Removechecker
S e t StandChk = New c l s S t a n d C h e c k A
StandardsCheckerController.AddStandardsChecker S t a n d C h k , 1 0 0 0
End Sub
Sub Removechecker()
I f N o t StandChk Is N o t h i n g Then
StandardsCheckerController.RemoveStandardsChecker S t a n d C h k
End I f
S e t StandChk = Nothing
End Sub
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 D i a 1 o g S t r i n g property. Prior
to creating the Class Module, the VBA Check A 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 lets talk about the entire Standards Checker
process.
I Basics of Implementing the Standards Checker I 629
UserForml.Show
End S u 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 IStandardsChecker-Hassettings 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?
STANDARDSCHECKERSETTINGS
Standards Checker Interfaces can make use of Settings:
IStandardsChecker-HasSettingsO
P r i v a t e P r o p e r t y Get As B o o l e a n
IStandardsChecker-Hassettings = T r u e
End P r o p e r t y
P r i v a t e Sub IStandardsChecker-EditSettings(ByVa1 I s R e a d O n l y As
Boo1 e a n )
UserForml.Show
End S u b
P r i v a t e Sub U s e r F o r m - I n i t i a l i z e 0
chkLabelsWritten.Value = ~
chkCleanFile.Va1ue = ~
chkFixErrors.Value = -
GetSetting("VBA Standards Checker", "Settings", "Fix Errors", False)
txtFilePath.Text = -
End Sub
P r i v a t e Sub b t n O K - C l i c k 0
If txtFilePath.Text = " " Then
MsgBox "A f i l e p a t h m u s t b e e n t e r e d . "
E x i t Sub
End I f
" La b e 1 s W r it t e n " . c h k La b e 1 s W r it t e n . Va 1 u e
" F i x E r r o r s " , c h k F i x E r r o r s . Va 1 u e
U n l o a d Me
End Sub
P r i v a t e Sub b t n C a n c e l L C l i c k 0
U n l o a d Me
End Sub
P r i v a t e Sub I S t a n d a r d s C h e c k e r - R u n C h e c k ( B y V a 1
M o d e l T o C h e c k As M o d e l R e f e r e n c e ,
B y V a l F i r s t M o d e l As B o o l e a n ,
B y V a l O p t i o n s As L o n g )
RoomLabelChecks ModelToCheck
End Sub
Sub R o o m L a b e l C h e c k s ( M o d e 1 T o S c a n As M o d e l R e f e r e n c e )
'Declare Variables
D i m myElementEnum As E l e m e n t E n u m e r a t o r
D i m m y s c a n c r i t e r i a As New E l e m e n t S c a n C r i t e r i a
D i m m y T a g s 0 As S t r i n g
ReDim m y T a g s ( 0 ) A s S t r i n g
D i m m y T a g s F i l t e r 0 As S t r i n g
D i m RoomTag As T a g E l e m e n t
D i m I As L o n g
D i m A u t o F i x As B o o l e a n
D i m L a b e l s I n F i l e As Boolean
D i m L e g i t i m a t e L a b e l s As Boolean
D i m m y F i l e R o o m s 0 As S t r i n g
ReDim m y F i l e R o o m s ( 0 ) As S t r i n g
D i m F F i l e As L o n g
D i m F i l e N a m e As S t r i n g
D i m L i n e I n As S t r i n g
' R e a d A l l Room L a b e l T a g s i n D r a w i n g
myScanCriteria.ExcludeAllTypes
myScanCriteria.IncludeType m s d E l e m e n t T y p e T a g
S e t myElementEnum = ModelToScan.Scan(myScanCriteria)
Whi 1 e myEl ementEnum.MoveNext
S e t RoomTag = myElementEnum.Current
S e l e c t Case UCase(RoomTag.TagDefinitionName)
Case " R O O M LABEL"
myTagsFilter = F i l t e r ( m y T a g s , RoomTag.Value, T r u e ,
VbBinaryCompare)
I f UBound(myTagsFi1ter) = -1 T h e n
myTags(UBound(myTags)) = RoomTag.Value
ReDim P r e s e r v e m y T a g s ( U B o u n d ( m y T a g s ) + 1)
End I f
End S e l e c t
Wend
I*********
" S e t t i n g s " ,~
" La b e 1 s W r it t e n " , " Fa 1 s e " )
LegitimateLabels = GetSetting("VBA Standards Checker", -
"Settings", -
" C 1 ea n F i 1 e " , " Fa 1 s e " )
' R e a d Room L a b e l F i l e
FFile = FreeFile
If D i r ( F i 1 e N a m e ) <> "I' Then
Open F i l e N a m e F o r I n p u t As I I F F i l e
W h i l e EDF(FFi1e) = False
Line Input {IFFile, myFileRooms(UBound(myFi1eRooms))
ReDim P r e s e r v e myFileRooms(UBound(myFi1eRooms) + 1)
Wend
I f UBound(myFi1eRooms) >= 1 Then
ReDim P r e s e r v e myFileRooms(UBound(myFi1eRooms) - 1)
End I f
C1 o s e # F F i 1 e
End I f
'Check f o r F i l e ' s E x i s t e n c e
If D i r ( F i 1 e N a m e ) = "I' Then
Debug.Print "The F i l e " & FileName & " does n o t e x i s t . "
I f AutoFix = T r u e Then
Open F i l e N a m e F o r O u t p u t As # F F i l e
F o r I = L B o u n d ( m y T a g s ) To UBound(myTags)
P r i n t #FFile, myTags(1)
Next I
C1 o s e # F F i 1 e
D e b u g . P r i n t "The F i l e " & FileName & -
" has been c r e a t e d . "
End I f
End I f
I Checking for Standards I 637
'Check f o r Tags i n F i l e
If LabelsInFile = T r u e Then
F o r I = L B o u n d ( m y T a g s ) To U B o u n d ( m y T a g s 1
myTagsFilter = Filter(myFi1eRooms. myTags(I1, True, -
vbBinaryCompare)
I f UBound(myTagsFi1ter) = -1 T h e n
D e b u g . P r i n t "Tag " & myTags(1) & -
" n o t found i n f i l e . "
I f AutoFix = T r u e Then
Open F i l e N a m e F o r A p p e n d As B F F i l e
P r i n t #FFile, myTags(1)
Debug.Print "Label " & myTags(1) & -
" added t o F i l e . "
C1 o s e # F F i 1 e
End I f
End I f
Next I
End I f
' C h e c k t o make s u r e L a b e l s i n t h e F i l e h a v e
' T a g s i n t h e DGN f i l e
If LegitimateLabels = T r u e Then
I f Dir(Fi1eName) = " " Then
Debug.Print " F i l e " & FileName & " does n o t e x i s t . "
Else
F o r I = L B o u n d ( m y F i 1 eRooms) To U B o u n d ( m y F i 1 eRooms)
m y T a g s F i 1 t e r = F i 1 t e r ( m y T a g s , m y F i 1 eRooms( I ) ,
-
True, ~
vbBinaryCompare)
I f UBound(myTagsFi1ter) = -1 Then
D e b u g . P r i n t " F i l e Room " & ~
myFileRooms(1) & -
" i s n o t i n t h e DGN F i l e . "
End I f
Next I
End I f
End I f
End Sub
STANDARDSCHECKERREPORTING
Lets expand our Standards Checker now and use some of the built-in
error reporting functionality. We are going to 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 will use the enumeration ErrorTypeto help describe the nature of
the Error we are reporting. As we see here, we have seven elements in
the enumeration.
P r i v a t e Enum E r r o r T y p e
NoError = 0
NotInFile = 1
NotInDGN = 2
FixedNotInFile = 11
FixedNotInDGN = 12
NotFixedNotInFile = 21
NotFixedNotInDGN = 22
End Enum
P r i v a t e Sub R e p o r t E r r o r ( E r r 0 r As E r r o r T y p e , RoomNumber As
String)
D i m mySCC As StandardsCheckerController
S e t mySCC = StandardsCheckerController
S e l e c t Case E r r o r
Case ErrorType.NotInDGN
mySCC.Tota1 Problems = mySCC.Tota1 Problems + 1
Case E r r o r T y p e . N o t I n F i l e
mySCC.Tota1 Problems = mySCC.Tota1 Problems + 1
Case E r r o r T y p e . F i x e d N o t I n D G N
mySCC.FixedProblems = mySCC.FixedProblems + 1
Case ErrorType.FixedNotInFi1e
mySCC.FixedProblems = mySCC.FixedProblems + 1
Case ErrorType.NotFixedNotInDGN
mySCC.IgnoredProblems = mySCC.IgnoredProblems + 1
Case ErrorType.NotFixedNotInFile
640 I Chapter 31: The Standards Checker I
mySCC.IgnoredProb1ems = mySCC.IgnoredProb1ems + 1
End Select
End S u b
The only thing we are doing differently in Roomlabel Checks is that we are
using the Rep0 r t E r r o r procedure when we encounter an 'error' or when
we 'fx' the 'error'.
' R e a d A l l Room L a b e l T a g s i n D r a w i n g
myScanCri t e r i a. E x c l udeAl1Types
myScanCriteria.IncludeType m s d E l e m e n t T y p e T a g
S e t myElementEnum = ModelToScan.Scan(myScanCriteria)
W h i l e myElementEnum.MoveNext
S e t RoomTag = myElementEnum.Current
S e l e c t Case UCase(RoomTag.TagDefinitionName)
Case " R O O M LABEL"
m y T a g s F i l t e r = F i l t e r ( m y T a g s , RoomTag.Value, T r u e , -
vbBinaryCompare)
I f UBound(myTagsFi1ter) = - 1 Then
myTags(UBound(myTags)) = RoomTag.Value
ReDim P r e s e r v e m y T a g s ( U B o u n d ( m y T a g s ) + 1)
End I f
End S e l e c t
I Standards Checker Reporting I 641
Wend
' R e a d Room L a b e l F i l e
FFile = FreeFile
I f Dir(Fi1eName) <> " " Then
Open F i l e N a m e F o r I n p u t As # F F i l e
W h i l e EOF(FFi1e) = False
L i n e I n p u t # F F i l e , myFi l e R o o m s ( U B o u n d ( m y F i 1 eRooms) 1
ReDim P r e s e r v e m y F i l e R o o m s ( U B o u n d ( m y F i 1 e R o o m s ) + 1)
Wend
I f UBound(myFi1eRooms) >= 1 Then
ReDim P r e s e r v e m y F i l e R o o m s ( U B o u n d ( m y F i 1 e R o o m s ) - 1)
End I f
C1 o s e B F F i 1 e
End I f
'Check f o r F i l e ' s E x i s t e n c e
I f Dir(Fi1eName) = " " Then
0 e b u g . P r i n t "The F i l e " & FileName & " does n o t e x i s t . "
I f AutoFix = T r u e Then
Open F i l e N a m e F o r O u t p u t As B F F i l e
For I = L B o u n d ( m y T a g s 1 To UBound(myTags)
P r i n t B F F i l e , myTags(1)
Next I
C1 o s e # F F i 1 e
I Chapter 31: The Standards Checker I
D e b u g . P r i n t "The F i l e " & FileName & -
" has been c r e a t e d . "
End I f
End I f
'Check f o r Tags i n F i l e
If LabelsInFile = T r u e Then
F o r I = L B o u n d ( m y T a g s 1 To U B o u n d ( m y T a g s )
myTagsFilter = Filter(myFi1eRooms. myTags(I), True, -
vbBinaryCompare)
I f UBound(myTagsFi1ter) = -1 Then
ReportError NotInFile, myTags(1)
D e b u g . P r i n t "Tag " & myTags(1) & " n o t found i n f i l e . "
If AutoFix = T r u e Then
ReportError FixedNotInFile, myTags(1)
Open F i l e N a m e F o r A p p e n d As # F F i l e
P r i n t B F F i l e , myTags(1)
Debug.Print "Label " & myTags(1) & -
" added t o F i l e . "
C1 o s e B F F i 1 e
Else
ReportError NotFixedNotInFile, myTags(1)
End I f
End I f
Next I
End I f
' C h e c k t o make s u r e L a b e l s i n t h e F i l e h a v e
'Tags i n t h e DGN f i l e
I f LegitimateLabels = T r u e Then
I f Oir(Fi1eName) = " " Then
0ebug.Print " F i l e " & FileName & " does n o t e x i s t . "
Else
F o r I = L B o u n d ( m y F i 1 e R o o m s ) To U B o u n d ( m y F i 1 e R o o m s )
m y T a g s F i l t e r = F i l t e r ( m y T a g s , myFileRooms(I), True, -
vbBinaryCompare)
I f UBound(myTagsFi1ter) = -1 T h e n
ReportError NotInDGN, myFileRooms(1)
ReportError NotFixedNotInDGN, myFileRooms(1)
D e b u g . P r i n t " F i l e Room " & m y F i l e R o o m s ( 1 ) & ~
End I f
Next I
End I f
End I f
End S u b
So, we have added a few lines of code. What does this get us?
After the
Standards
Checker is run,
we are shown:
Y
I Chapter 31: The Standards Checker I
We have 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.
P r i v a t e Sub R e p o r t E r r o r ( E r r 0 r A s E r r o r T y p e ,
RoomNumber A s S t r i n g )
D i m mySCC As StandardsCheckerController
D i m myProb As StandardsCheckerProblem
D i m myRep As S t a n d a r d s C h e c k e r R e p o r t
S e t mySCC = StandardsCheckerController
S e t myRep = mySCC.Report
S e l e c t Case E r r o r
Case E r r o r T y p e . N o t I n D G N
mySCC.TotalProblems = mySCC.TotalProblems + 1
S e t myProb = myRep.AddProblem("Room " & RoomNumber & -
Now, when we view the report generated after the Checker finishes, we
see the number of Problems that were identified. And if we expand the
I Standards Checker Reporting I 645
listing below the File Name we will see the specific Problems we added
above.
Case ErrorType.NotInFile
mySCC.TotalProblems = mySCC.TotalProblems + 1
Set myProb = myRep.AddProblem("Room & RoomNumber & "
Case ErrorType.FixedNot1nDGN
mySCC.FixedProblems = mySCC.FixedProblems + 3
646 I Chapter 31: The Standards Checker I
S e t myProb = myRep.AddProblem("Room " & RoomNumber & -
Case E r r o r T y p e . N o t F i x e d N o t I n D G N
S e t myProb = myRep.AddIgnoredProb1 em("Room " & ~
RoomNumber & ~
Case ErrorType.NotFixedNotInFile
S e t myProb = myRep.AddIgnoredProb1 em("Room " & ~
RoomNumber & ~
End Select
End Sub
Now, in addition to adding a "Problem" to the report stating that the
problem was not fured (the False parameter in Addproblem), we are also
adding Fixed Problems and Ignored Problems.
Here is the report now:
Now, in addition to seeing Problems that have not been fixed, we can see
problems that have been fixed and those that have been ignored.
I Automatically Loading Custom Standards Checker Add-Ins I 647
AUTOMATICALLYLOADINGCUSTOMSTANDARDS
CHECKERADD-INS
Standards Checker functionality can be automatically loaded through
two mechanisms. Both of these mechanisms make use of the
O n P r o j e c t L o a d Procedure that can be placed in a Microstation VBA
Project. The O n P r o j e c t L o a d Procedure is executed whenever a VBA
Project (.mvba file) is opened. The code in this Procedure is the same for
both autoloadmechanisms:
Sub OnProjectLoad( 1
Addchecker
End S u b
REVIEW
The Standards Checker Interface provides us 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
In this Chapter:
Declaring API Calls
Declaring Trpes
B Utilizing API Calls
DECLARING
API CALLS
Windows API calls can be declared in the General Declarations area of a
Code Module. Once declared, the API calls are used just as we would
use any other Function or Procedure.
Here is an example:
649
650 I Chapter 32:Using the Windows API I
P u b l i c D e c l a r e F u n c t i o n Beep L i b k e r n e 1 3 2 (ByVal dwFreq As Long, -
B y V a l d w D u r a t i o n As L o n g ) A s Long
S u b TestBeep( 1
Beep 4 0 0 0 , 2 5 0
Beep 2000, 2 5 0
Beep 1000, 2 5 0
Beep 5 0 0 , 2 5 0
End S u b
Four beeps are heard, each lasting 1/4 of a second (250 milliseconds)
and each at a different frequency. The higher the frequency the higher
the beep. Each beep is half the frequency of the previous frequency. This
results in four notes, each note is 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. Often times, the value they return tells us whether the
API call worked or if an error was encountered.
In addition to specifying the Functions 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 make use of Types: A Type is similar to an
Object in that it has specific properties or members: Often times we
declare a variable as one of these Types and then set some of its
I Utilizing API Calls I 651
Sub TestSystemInfo()
Dim mySystemInfo As SYSTEMLINFO
GetSystemInfo mySystemInfo
MsgBox mySystemInfo.dwNumberOfProcessors & " Processors."
End Sub
We will see examples of more Types declared as we look at more API
examples.
API CALLS
UTILIZING
There are hundreds of API calls available for our use. Those presented
here are not in any particular order and they are not 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 the screen resolution. In
any case, those listed here should prove helpful to many readers.
652 I Chapter 32:Using the Windows API I
GetLogicalDrives
P u b l i c Declare Function GetLogicalDriveStrings L i b "kerne132"
A l i a s "GetLogical DriveStringsA" ~
( B y V a l n B u f f e r L e n g t h As L o n g , -
B y V a l l p B u f f e r As S t r i n g ) As L o n g
Sub T e s t G e t L o g i c a l D r i v e s t r i n g s o
D i m D r i v e L e t t e r s As S t r i n g
D i m x S p l i t As V a r i a n t
D i m I As L o n g
DriveLetters = Space(255)
GetLogical Drivestrings Len(DriveLetters) , DriveLetters
xSplit = Split(DriveLetters, Chr(0))
For I = L B o u n d ( x S p l i t . 1 To U B o u n d ( x S p 1 i t ) - 2
MsgBox " D r i v e " & xSplit(1) & " Found."
Next I
End Sub
Often times, when we are using 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
GetLogicalDriveStrings,itgoesinwith255spacecharactersinit.When
it comes out, we are given a series of drive letter characters separated by
a Null Character (Chr(0)). 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 T e s t G e t L o g i c a l D r i v e S t r i n g s 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 G e t L o g i c a l D r i v e S t r i n g s 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 G e t D r i veType. This example
also uses G e t L o g i c a 1 Dr iveS t r ings.
P u b l i c D e c l a r e F u n c t i o n GetDriveType L i b "kerne132" A l i a s -
" G e t D r i v e T y p e A " ( B y V a l n D r i v e As S t r i n g ) As L o n g
P u b l i c Const DRIVE-CDROM = 5
I Utilizing API Calls I 653
P u b l i c Const DRIVE-FIXED = 3
Pub1 i c C o n s t DRIVE-RAMDISK = 6
P u b l i c C o n s t DRIVELREMOTE = 4
P u b l i c C o n s t DRIVELREMOVABLE = 2
Sub T e s t D r i v e T y p e ( 1
D i m D r i v e L e t t e r s As S t r i n g
D i m x S p l i t As V a r i a n t
D i m I As Long
DriveLetters = Space(255)
G e t L o g i c a l Dri v e S t r i n g s L e n ( D r i v e L e t t e r s ) , Dri v e L e t t e r s
xSplit = Split(DriveLetters, Chr(0))
For I = L B o u n d ( x S p 1 i t ) To U B o u n d ( x S p 1 i t ) - 2
Debug.Print "Drive " & xSplit(1) & " is a " & -
ReturnDriveType(CStr(xSplit(1)))
Next I
End Sub
Function ReturnDriveType(DriveLetter As S t r i n g ) As S t r i n g
D i m l n g D r i v e T y p e As Long
1n g D r i veType = GetDriveType(Dri veLetter)
S e l e c t Case 1 n g D r i v e T y p e
Case D R I V E - C D R O M
ReturnDriveType = "CD/DVD D r i v e "
Case D R I V E L F I X E D
ReturnDriveType = "Hard D r i v e "
Case DRIVE-RAMDISK
ReturnDriveType = "RAM D i s k "
Case DRIVE-REMOTE
ReturnDriveType = "Mapped D r i v e "
Case DRIVELREMOVABLE
ReturnDriveType = "Removable D r i v e "
Case E l s e
ReturnDriveType = 1ngDriveType
End S e l e c t
End F u n c t i o n
GetcomputerName
Public Declare Function GetComputerName L i b "kerne132"
A l i a s " G e t C o m p u t e r N a m e A " ( B y V a l l p B u f f e r As S t r i n g , -
n S i z e As L o n g ) As L o n g
Sub TestGetComputerNameO
D i m CompName As S t r i n g
CompName = Space(255)
GetComputerName CompName, Len(CompName)
CompName = L e f t ( C o m p N a m e , I n S t r ( 1 , CompName, C h r ( 0 ) ) - 1)
MsgBox CompName
End Sub
GetVersionEx
What Operating System is the computer running? We need only ask
GetVersionEx to find out.
P u b l i c T y p e OSVERSIONINFO
d w O S V e r s i o n I n f o S i z e As L o n g
d w M a j o r V e r s i o n As L o n g
d w M i n o r V e r s i o n As L o n g
d w B u i l d N u m b e r As L o n g
d w P l a t f o r m I d As L o n g
s z C S D V e r s i o n As S t r i n g * 128
End T y p e
P u b l i c Const VERLPLATFDRMLWIN32LWINDDWS = 3
P u b l i c C o n s t VERLPLATFORMLWIN3PLNT = 2
I Utilizing API Calls I 655
Sub TestOSVersionO
D i m m y V e r I n f o As O S V E R S I O N I N F O
D i m s t r S e r v i c e P a c k As S t r i n g
myVerInfo.dwOSVersionInfoSize = 148
GetVersionEx myVerInfo
S e l e c t Case m y V e r I n f o . d w P l a t f o r m I d
Case VER-PLATFORM-WIN32-WINDOWS
S e l e c t Case m y V e r I n f o . d w M i n o r V e r s i o n
Case 0
MsgBox "Windows 9 5 "
Case 1 0
MsgBox "Windows 9 8 "
Case 90
MsgBox "Windows M E "
End S e l e c t
Case VERLPLATFORMLWIN3ZLNT
S e l e c t Case m y V e r 1 n f o . d w M a j o r V e r s i o n
Case I s <= 4
MsgBox "Windows NT B u i l d " & -
myVerInfo.dwBui1dNumber
Case 5
S e l e c t Case m y V e r 1 n f o . d w M i n o r V e r s i o n
Case 1
strServicePack = myVer1nfo.szCSDVersion
strServicePack = Left(strServicePack,
I n S t r ( 1 , s t r S e r v i c e P a c k , C h r ( 0 ) ) - 1)
MsgBox "Windows X P B u i l d " & -
myVerInfo.dwBui1dNumber & vbCr & -
s t r S e r v i cePack
Case 2
MsgBox "Windows .NET S e r v e r B u i l d " & -
myVerInfo.dwBuildNumber
Case E l s e
MsgBox "Windows 2000 B u i l d " & -
myVerInfo.dwBuildNumber
End S e l e c t
End S e l e c t
End S e l e c t
End Sub
Sub T e s t S l eep( )
D i m S t a r t T i m e As D a t e
D i m EndTime As D a t e
StartTime = Now
Sleep 1000
EndTime = Now
& FormatDateTime(StartTime, v b L o n g T i m e )
MsgBox " S t a r t T i m e : " ~
End Sub
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? F i n d E x e c u t a b l e tells us the path to the program that is
registered to open a particular file type.
A l i a s "FindExecutableA" -
( B y V a l l p F i l e As S t r i n g , B y V a l 1 p D i r e c t o r y As S t r i n g ,
B y V a l l p R e s u l t As S t r i n g ) As Long
Sub T e s t F i n d E x e c u t a b l e ( 1
D i m s t r F i l e N a m e As S t r i n g
D i m s t r D i r N a m e As S t r i n g
D i m s t r E x e F i l e As S t r i n g
s t r F i 1 eName = " e u l a . p d f "
s t r D i rName = " C : \ P r o g r a m F i 1e s \ B e n t l e y \ M i c r o S t a t i o n "
strExeFile = Space(255)
F i n d E x e c u t a b l e s t r F i 1 eName, s t r D i rName, s t r E x e F i l e
I Utilizing API Calls I
strExeFile = Left(strExeFile, InStr(1, strExeFile, Chr(0)) - 1)
MsgBox s t r Exe F i 1 e
End Sub
GetDiskFreeSpace
GetDiskFreeSpace gives us information about the provided disk name.
" G e t D is k F r e e S p a c e A " -
1 p T o t a l N u m b e r O f C l u s t e r s As L o n g ) As L o n g
Sub TestFreeDiskSpace( 1
D i m myspace As Long
D i m 1 n g S e c t o r s P e r C l u s t e r As L o n g
D i m 1 n g B y t e s P e r S e c t o r As L o n g
D i m 1 n g F r e e C l u s t e r s As L o n g
D i m 1n g T o t a l C l u s t e r s A s Long
D i m FreeBytes As Double
D i m T o t a l B y t e s As Double
D i m P e r c e n t F r e e As D o u b l e
GetDiskFreeSpace " C : \ " , lngSectorsPerCluster,
IngBytesPerSector, IngFreeClusters, 1ngTotalClusters
F r e e B y t e s = FormatNumber(lngFreeC1usters / 1 0 0 0 * -
1 n g B y t e s P e r S e c t o r * l n g S e c t o r s P e r C l u s t e r , 2)
T o t a l B y t e s = FormatNumber(lngTotalC1usters / 1 0 0 0 * ~
1 n g B y t e s P e r S e c t o r * l n g S e c t o r s P e r C l u s t e r , 2)
PercentFree = Round((FreeBytes / T o t a l B y t e s ) * 1 0 0 , 2)
MsgBox "Free B y t e s : " & FormatNumber(FreeBytes, 2, F a l s e , F a l s e , T r u e ) -
& " K B " & vbCr & -
" T o t a l Bytes: " & FormatNumber(TotalBytes, 2, False, False, True) & -
" KB" & v b C r & -
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.
( B y V a l n I n d e x As Long) As Long
P u b l i c C o n s t SMKCMETRICS = 44
P u b l i c C o n s t SMKCMOUSEBUTTONS = 43
P u b l i c C o n s t SM-CXBORDER = 5
Publ i c C o n s t SM-CXCURSOR = 13
P u b l i c C o n s t SM-CXDLGFRAME = 7
P u b l i c C o n s t SMKCXDOUBLECLK = 36
P u b l i c Const SMKCXFIXEDFRAME = SM-CXDLGFRAME
P u b l i c Const SMKCXFRAME = 32
Publ i c C o n s t SM-CXFULLSCREEN = 16
P u b l i c C o n s t SM-CXHSCROLL = 21
P u b l i c C o n s t SM-CXHTHUMB = 10
P u b l i c Const S M K C X I C O N = 11
P u b l i c Const S M K C X I C O N S P A C I N G = 38
P u b l i c Const S M K C X M I N = 28
P u b l i c C o n s t SM-CXMINTRACK = 34
P u b l i c C o n s t SM-CXSCREEN = 0
P u b l i c Const S M K C X S I Z E = 30
P u b l i c Const SMKCXSIZEFRAME = SM-CXFRAME
P u b l i c C o n s t SMKCXVSCROLL = 2
P u b l i c C o n s t SM-CYBORDER = 6
P u b l i c C o n s t SM-CYCAPTION = 4
P u b l i c C o n s t SM-CYCURSOR = 14
P u b l i c C o n s t SMKCYDLGFRAME = 8
P u b l i c C o n s t SMKCYDOUBLECLK = 37
P u b l i c Const SMKCYFIXEDFRAME = SM-CYDLGFRAME
I Utilizing API Calls I 659
P u b l i c C o n s t SM-CY FRAME = 33
P u b l i c C o n s t SM-CYFULLSCREEN = 17
P u b l i c C o n s t SM-CYHSCROLL = 3
P u b l i c C o n s t SM-CYICON = 12
P u b l i c C o n s t SM-CYICONSPACING = 39
P u b l i c C o n s t SM-CYMENU = 15
P u b l i c C o n s t SM-CYKANJIWINDOW = 18
P u b l i c C o n s t SM-CYMINTRACK = 35
P u b l i c C o n s t SM-CYMIN = 29
P u b l i c C o n s t SM-CYSCREEN = 1
P u b l i c C o n s t SM-CYSIZE = 31
P u b l i c C o n s t SM-CYSIZEFRAME = SM-CYFRAME
P u b l i c C o n s t SM-CYVSCROLL = 20
P u b l i c C o n s t SM-CYVTHUMB = 9
P u b l i c C o n s t SM-DBCSENABLED = 42
P u b l i c C o n s t SM-DEBUG = 22
P u b l i c C o n s t SM-MENUDROPALIGNMENT = 40
P u b l i c C o n s t SM-MOUSEPRESENT = 19
P u b l ic C o n s t SM-SWAPBUTTON = 23
Sub TestSystemMetrics( 1
MsgBox " S c r e e n R e s o l u t i o n : " & vbCr &
GetSystemMetrics(SM-CXSCREEN) & " X " &
GetSystemMetrics(SM-CYSCREEN)
MsgBox "Mouse B u t t o n s : " &
GetSystemMetrics(SM-CMOUSEBUTTONS)
End Sub
GetTickCount
How long has it been since the computer was started? GetTickCount
answers this question in milliseconds.
Sub T e s t T i c k C o u n t ( 1
D i m S t a r t T i c k s As L o n g
D i m E n d T i c k s As L o n g
StartTicks = GetTickCount
660 I Chapter 32:Using the Windows API I
S l e e p 2000
EndTicks = GetTickCount
MsgBox S t a r t T i c k s & v b C r & E n d T i c k s
End Sub
GetUserName
Who is logged onto the computer?
Sub TestUserNameO
D i m UserName As S t r i n g
D i m x S p l i t As V a r i a n t
UserName = Space(255)
GetUserName UserName, L e n ( U s e r N a m e 1
xSplit = Split(UserName, Chr(0))
UserName = xSplit(0)
MsgBox UserName
End Sub
GetWindowsDirectory
Where is Windows Installed? C:\Winnt? C:\Windows?
GetWindowsDirectorytells us.
P u b l i c D e c l a r e F u n c t i o n GetWindowsDirectory L i b ~
"kerne132" A l i a s -
" G e t W i n d o w s D i r e c t o r y A " ( B y V a l l p B u f f e r As S t r i n g , -
B y V a l n S i z e As L o n g ) As L o n g
Sub TestWindowsDi r (1
D i m W i n d o w s D i r As S t r i n g
I Utilizing API Calls I 661
D i m x S p l i t As V a r i a n t
WindowsDir = Space(255)
GetWindowsDirectory WindowsDir, Len(WindowsDir)
xSpl i t = S p l it ( W i n d o w s D i r , C h r ( 0 ) )
WindowsDir = xSplit(0)
MsgBox W i n d o w s D i r
End S u b
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.
( B y V a l 1 p s z U s e r n a m e A s S t r i n g , B y V a l l p s z D o m a i n As S t r i n g , ~
B y V a l l p s z P a s s w o r d A s S t r i n g , B y V a l d w L o g o n T y p e As L o n g , ~
B y V a l d w L o g o n P r o v i d e r A s L o n g , p h T o k e n A s L o n g ) As L o n g
Function CurrentUserName( 1 As S t r i n g
D i m UserName A s S t r i n g
D i m x S p l i t As V a r i a n t
UserName = Space(255)
GetUserName UserName, L e n ( U s e r N a m e )
xSplit = Split(UserName, Chr(0))
UserName = xSplit(0)
CurrentUserName = UserName
End F u n c t i o n
Sub TestLogonUser( 1
D i m S u c c e s s f u l As Long
D i m PasswordEntry As S t r i n g
PasswordEntry = I n p u t B o x ( " R e - E n t e r Password:", "Logon V a l i d a t i o n " )
Successful = L o g o n U s e r ( C u r r e n t U s e r N a m e , " " , PasswordEntry, 2 , 0 , 0)
662 I Chapter 32:Using the Windows API I
S e l e c t Case S u c c e s s f u l
Case 1
MsgBox " Y o u h a v e b e e n v a l i d a t e d . " , vbInformation
Case E l s e
MsgBox " I n v a l i d Username/Password C o m b i n a t i o n . " , vbcritical
End S e l e c t
End Sub
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.
P u b l i c C o n s t MB-OK = &HO&
P u b l i c C o n s t MB-ICONSTOP = &H10&
P u b l i c C o n s t MB-ICONQUESTION = &H20&
P u b l i c C o n s t MB-ICONEXCLAMATION = &H30&
P u b l i c C o n s t MB-ICONASTERISK = &H40&
Sub TestMessageBeepO
MessageBeep MB-OK
Sleep 500
MessageBeep MB-ICONSTOP
Sleeo 500
I Utilizing API Calls I 663
MessageBeep MB-ICONQUESTION
Sleep 500
MessageBeep MBLICONEXCLAMATION
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.
Sub T e s t P l aySoundA()
PlaySound "C:\Windows\Media\chimes.wav", 0, SND-SYNC
PlaySound "C:\Windows\Media\chord.wav", 0, SND-SYNC
End Sub
This first example uses the "SND-SYNC" flag. This means the
chirnes.wuv file plays until it is finished and then the chordwuv file
plays.
Sub T e s t P l aySoundB()
PlaySound "C:\Windows\Media\chimes.wav", 0, SNDLASYNC
Sleep 150
PlaySound "C:\Windows\Media\chimes.wav", 0, SNDLASYNC
Sleep 200
PlaySound "C:\Windows\Media\chimes.wav", 0, SND-ASYNC
Sleep 250
PlaySound "C:\Windows\Media\chimes.wav", 0, SNDLASYNC
Sleep 300
PlaySound "C:\Windows\Media\chimes.wav", 0, SNDLASYNC
End Sub
When we use the "SND-ASYNC" flag, the specified file begins playing
and the code continues executing without waiting for the file to finish
664 I Chapter 32:Using the Windows API I
playing. By placing "Sleep" calls between each line of code, we hear 150
milliseconds of chimes.wuv 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 we will look at 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. Not until Microstation is closed down or
T e s t P l aySoundD is executed does the sound stop.
Sub T e s t P l aySoundC(
PlaySound "C:\Windows\Media\chord.wav", 0 , -
SNDLLOOP + S N D L A S Y N C
End Sub
Sub T e s t P l aySoundD(
PlaySound v b N u l l S t r i n g , 0, 0
End Sub
ShellExecute
In a previous example, we were able to discover which application was
registered to open a specific file. Shel 1 E x e c u t e actually opens the file
using the application registered to handle the file.
ByVal l p P a r a m e t e r s As S t r i n g , ByVal l p D i r e c t o r y As S t r i n g , -
B y V a l nShowCmd As L o n g ) As L o n g
Sub T e s t S h e l l E x e c u t e A O
Shel 1Execute 0, " O P E N " ,
"Greenstone. bmp", " ' I , "C:\Windows", vbMinimizedNoFocus
End Sub
Sub T e s t S h e l l E x e c u t e B ( )
Shel 1Execute 0, " O P E N " ,
I Utilizing API Calls I 665
SHGetFileInfo
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.
Sub TestGetFileInfoO
666 I Chapter 32:Using the Windows API I
D i m m y F I A s SHFILEINFO
SHGetFileInfo "C:\test.dgn", SHGFI-ATTRIBUTES, myFI, Len(myFI1, -
)
Debug. P r in t R e p l a c e ( my F I . s z T y p e Name, C h r ( 0 ) ,
" "
End Sub
test. dgn
Bentley MicroStation Design File
In this Chapter:
Using ActiveX Controls
UsingDLLs
USINGACTIVEXCONTROLS
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
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.
.
As we scroll through the list of items, we can 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 we can make
use of it. Some Controls require a License to use. Lets take a look at a
few Controls that are available for us to use.
I Using ActiveX Controls I 669
For demonstration
purposes, we will use the
wow 2005
Calendar Control 11.0
Control in our 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 along with
other controls we may want to use.
Clicking on the button displays the selected date.
P r i v a t e Sub btnDisplaySelection-Click0
MsgBox C a l e n d a r l . V a l u e
End Sub
r e g s v r 3 2 c : \ p r o g r a m f i 1e s \ M i c r o s o f t O f f i c e \ O f f i c e l l \ m s c a l .o c x
670 I Chapter 33: Using Third Party 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.
USINGEXISTINGDLLs
There are two fundamental ways to use DLLs in our programming. Each
method of using DLLs have been used in this book already.
Method 1: Declaring DLL Functions such as was done in the Windows
API chapter.
Method 2: Adding a Reference to the DLL 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.
I Using Existing DLLs I 671
She1132
Sub TestShell B ( 1
D i m m y s h e l l As New S h e l l 3 2 . S h e l l
D i m m y F o l d e r As S h e 1 1 3 2 . F o l d e r 3
D i m m y f o l d e r 2 As S h e 1 1 3 2 . F o l d e r 3
D i m m y I t e m As S h e l l 3 2 . F o l d e r I t e m
Set myFolder = myshell .BrowseForFolder(O, "Browse", 0, -
"C:\Program F i l e s \ B e n t l e y " )
For Each m y I t e m I n m y F o l d e r . I t e m s
S e l e c t Case m y I t e m . T y p e
Case " F i 1 e F o l d e r "
Debug.Print "Folder: " & myItem.Name
I Using Existing DLLs I 673
End Select
Next
End S u b
Now, when this is run, the
user must select either the
RootFolder or a folder in
the Root Folder's path.
Documentation
Licensing This is especially helpful
Microstation
when we ask the user to
select something from a
Project folder. It is faster
for the user because we
begin in the correct folder.
It is good for us because
we know the user will be selecting something from the root folder path.
Here are a few more examples. They are all harmless, even the
ShutdownWindows call. When we use ShutdownWindows, we should be
prompted as to what we want to do. Simply click Cancel and everything
will be OK. It would be a good idea to save open documents before
using this call just to be safe.
S u b Testshell C( )
Dim myshell A s New Shell32.Shell
myShell.FindComputer
End S u b
S u b Testshell D ( )
Dim myshell A s New Shell32.Shell
myshell .ShutdownWindows
End S u b
S u b Testshell E ( )
Dim myshell A s New Shell32.Shell
myshell .Open "C:\Program Files\Bentley"
End S u b
674 I Chapter 33: Using Third Party ActiveX Controls and DLLs I
pi
Sub TestFSOA( 1
Dim myFSO As New Scripting.Fi1eSystemDbject
Dim myFolder As Scripting.Folder
Dim myFile As Scripting.File
S e t rnyFol d e r = myFSO.GetFo1 d e r ( C : \ P r o g r a m F i 1 e s \ B e n t l e y \ M i c r o S t a t i o n )
For Each myFile In myFolder.Files
Select Case myFile.Type
Case Bentley Microstation Design File
Debug.Print myFile.Name
End Select
Next
End Sub
The Immediate Window displays the names of the files matching the
specified criteria.
In addition to looking at files in a folder, we can get the subfolders in a
given folder and all of its subfolders as well. Our next example provides
I Using Existing DLLs I 675
Sub T e s t F S O B ( )
D i m myFSO A s New Scripting.FileSystem0bject
D i m myFolder As S c r i p t i n g . F o 1 d e r
D i m mySubFolder As S c r i p t i n g . F o 1 d e r
S e t myFol d e r = myFSO.GetFolder("C:\Program F iles\Bentl ey")
F o r Each m y F o l d e r I n m y F o l d e r . S u b F o 1 d e r s
TraverseFolders myFolder
Next
End Sub
Sub T r a v e r s e F o l d e r s ( F o l d e r I n As S c r i p t i n g . F o l d e r )
D i m NextSubFolder As S c r i p t i n g . F o l d e r
Debug.Print FolderIn.Path
F o r Each N e x t S u b F o l d e r I n F o l d e r I n . S u b F o l d e r s
TraverseFolders NextSubFolder
Next
End Sub
C:\Program Files\Bentley\Documentation
C:\Program Files\Bentley\Licensing
C:\Program Files\Bentley\MicroStation
C:\Program Files\Bentley\MicroStation\assemblies
C:\Program Files\Bentley\MicroStation\assemblies\ECFramework
C:\Program Files\Bentley\MicroStation\assemblies\ECFramework\extensions
C:\Program Files\Bentley\MicroStation\assemblies\JSpace
C:\Program Files\Bentley\MicroStation\assemblies\JSpace\AddIns
C:\Program Files\Bentley\MicroStation\assemblies\JSpace\managed
C:\Program Files\Bentley\MicroStation\assemblies\JSpace\resource
C:\Program Files\Bentley\MicroStation\assemblies\JSpace\unmanaged
C:\Program Files\Bentley\MicroStation\config
C:\Program Files\Bentley\MicroStation\config\appl
C:\Program Files\Bentley\MicroStation\config\database
C:\Program Files\Bentley\MicroStation\config\system
C:\Program Files\Bentley\MicroStation\docs
Sub TestFSOC()
D i m myFSO A s New Scripting.FileSystem0bject
D i m myDrive As S c r i p t i n g . D r i v e
D i m I As Long
676 I Chapter 33: Using Third Party ActiveX Controls and DLLs I
F o r I = A s c ( " A " ) To A s c ( " Z " )
I f m y F S O . D r i v e E x i s t s ( C h r ( 1 ) ) Then
Set myDrive = myFSO.GetDrive(Chr(1) & " : \ ' I )
End I f
End I f
Next I
End Sub
Drive: 2
Share Name: \ \ D e v \ S t o r e
Volume: STORE
T o t a l Space: 81,956,655,104 Bytes
This example gets all "Network Drives" and displays information about
each drive.
For our next example, we will read an ASCII File using the File System
Object.
Sub TestFSOD( 1
D i m myFSO As New Scripting.FileSystem0bject
D i m m y F i l e As S c r i p t i n g . F i l e
D i m myTS As S c r i p t i n g . T e x t S t r e a m
D i m s t r W h o l e F i l e As S t r i n g
D i m x S p l i t As V a r i a n t
Set myFile = myFSO.GetFile( -
If we add a Watch to the variable xSplit we will see that the file has been
successfully read and split into the variable xSplit. We can now look at
each line in the file one by one.
Now we are going to use the File System Object to 'write' a file. We will
create a small HTML file.
Sub T e s t F S O E ( )
D i m myFSO A s New Scripting.FileSystem0bject
D i m m y F i l e As S c r i p t i n g . F i l e
D i m myTS As S c r i p t i n g . T e x t S t r e a m
S e t myTS = my F S O . C r e a t e T e x t F i 1 e ( " C : \ t e s t . h t m " , T r u e 1
my T S . W r it e L in e " < ht m 1> "
m y T S . W r i t e L i n e v b T a b & " < t r > < t d a1 i g n = c e n t e r > Z < / t d > " & ~
Nothing fancy, just a simple HTML file. We have written ASCII Text
files before but we have used standard VBA calls such as Open and
Print.
The last File System Object object we will look at is the Dictionary
Object. This object allows us to add item pairs (Key and Item) to a
ready-made collection.
Sub TestFSOF( 1
D i m myFSO As New F i l e S y s t e m D b j e c t
D i m myDir As S c r i p t i n g . F o l d e r
D i m m y D i c t i o n a r y As New D i c t i o n a r y
D i m I A s Long
S e t myDi r = myFSO.GetFo1 d e r ( C : \ P r o g r a m F i 1 e s \ B e n t l e y )
R e c u r s i v e F o l d e r myDir, m y D i c t i o n a r y
For I = 1 To m y D i c t i o n a r y . C o u n t
Debug.Print myDictionary.Keys(1 - 1) & v b T a b & ~
myDictionary.Items(1 - 1)
Next I
End Sub
Sub RecursiveFolder(Folder1n As S c r i p t i n g . F o l d e r , ~
D i c t i o n a r y I n As S c r i p t i n g . D i c t i o n a r y )
D i m m y F i l e As S c r i p t i n g . F i l e
D i m m y S u b D i r As S c r i p t i n g . F o l d e r
F o r Each m y F i l e I n F o l d e r I n . F i l e s
I f UCase( R i g h t ( m y F i 1 e . Name, 4) = .CHM Then
D i c t i o n a r y I n . A d d m y F i 1 e . Name, myFi l e . P a t h
End I f
Next
I Using Existing DLLs I 679
F o r Each m y S u b D i r I n F o l d e r I n . S u b F o 1 d e r s
RecursiveFolder mySubDir, D i c t i o n a r y I n
Next
End S u b
Sub TestSpeechA( 1
D i m m y v o i c e A s New S p e e c h L i b . S p V o i c e
myVoice.Speak "MicroSteyshen V , B, A, I s Great!"
End S u b
Sub TestCDOA( 1
D i m myMsg As New CDD.Message
D i m F i e l d B a s e As S t r i n g
FieldBase = "http://schemas.microsoft.com/cdo/configuration/"
myMsg.To = "youraddress@yourserver.com"
myMsg.From = "myaddress@myserver.com"
myMsg.Subject = " T e s t i n g CDO E m a i l "
myMsg. HTMLBody = "<b>VBA</b><br>For MicroStation"
m y M s g . C o n f i g u r a t i o n . F i e l d s . I t e m ( F i e l dBase & " s e n d u s i n g " ) = 2
m y M s g . C o n f i g u r a t i o n . F i e l d s . I t e m ( F i e l dBase & " s m t p s e r v e r " ) = ~
" s m t p . y o u r s e r v e r . com"
myMsg. C o n f i g u r a t i o n . F i e l d s . I t e m ( F i e l d B a s e & " s m t p s e r v e r p o r t " ) = 25
myMsg.Configuration. F i e l ds.Update
myMsg.Send
End Sub
Sending e-mail using VBA isn't difficult. It does take a few lines of code,
but this is because there are a few settings that need to be made. Once
the settings are in place, the e-mail is sent.
VBA
For UcroStation
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( 1
D i m myMsg As New CDO.Message
D i m F i e l d B a s e As S t r i n g
I Using Existing DLLs I 681
VEA
For MicroStabon
http://support.microsoft.com/kb/22435 7/
reveals another link to "Download the DsoFileSetup': This is what we
want. Why?
MicroStation design files are OLE Documents. Another term used to
describe this type of file is "Structured Storage" Documents. What does
this mean?
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.
These properties may look familiar. They are found 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.
Sub TestDSOA( 1
Dim myDSO As New D S O F i l e . 0 1 e D o c u r n e n t P r o p e r t i e s
myDSO.Open "C:\test.dgn", True
MsgBox m y D S 0 . S u r n r n a r y P r o p e r t i e s . A u t h o r
myDSO.Close
End Sub
The 'Author' of the file C:\test.dgn is "Bentley Systems, Inc.? It should be
stated here that 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 testdgn file.
Sub TestDSOB( 1
Dim myDSO As New D S O F i l e . 0 1 e D o c u r n e n t P r o p e r t i e s
my DSO .Open C : \test . dgn , Fa 1 se
" "
End Sub
If we writeproperties to a file, we must save it before we close it.
Summary
Here are the results
in Windows
Explorer Properties
diaIog box:
Sub TestDSOC()
Dim myDSO A s New DSOFile.0leDocumentProperties
my DSO . Open C : \test .dgn , True
Debug.Print my0SO.SummaryProperties.ApplicationName
Debug.Print my0SO.SummaryProperties.Author
Debug.Print myDSO.SummaryProperties.ByteCount
Debug.Print myDSO.SummaryProperties.Category
Debug.Print myDSO.SummaryProperties.CharacterCount
Debug.Print myDS0.SummaryProperties.CharacterCountWithSpaces
Debug.Print my0SO.SummaryProperties.Comments
Debug.Print myDSO.SummaryProperties.Company
Debug.Print myDSO.SummaryProperties.DateCreated
Debug.Print myDS0.SummaryProperties.DateLastPrinted
Debug.Print myDSO.SummaryProperties.0ateLastSaved
I Chapter 33: Using Third Party ActiveX Controls and DLLs I
D e b u g . P r i n t myDSO.SummaryProperties.HiddenS1ideCount
Debug.Print myDS0.SummaryProperties.Keywords
D e b u g . P r i n t myDSO.SummaryProperties.LastSavedBy
Debug. Pri n t myDSO. S u m m a r y P r o p e r t i e s . L i neCount
Debug.Print myDSO.SummaryProperties.Manager
Debug.Print myDSO.SummaryProperties.MultimediaC1ipCount
Debug.Print myDS0.SummaryProperties.NoteCount
D e b u g . P r i n t myDS0.SummaryProperties.PageCount
D e b u g . P r i n t myDSO.SummaryProperties.ParagraphCount
Debug.Print myDS0.SummaryProperties.PresentationFormat
Debug.Print myDSO.SummaryProperties.RevisionNumber
Debug.Print myDSO.SummaryProperties.SharedDocument
Debug.Print myDS0.SummaryProperties.SlideCount
Debug.Print myDS0.SummaryProperties.Subject
Debug.Print myDSO.SummaryProperties.Template
Debug.Print myDSO.SummaryProperties.Thumbnai1
Debug. P r i n t m y D S O . S u m m a r y P r o p e r t i e s . T i t l e
Debug.Print myDSO.SummaryProperties.Tota1EditTime
Debug.Print myDS0.SummaryProperties.Version
Debug.Print myDS0.SummaryProperties.WordCount
End Sub
Sub TestDSOD( 1
D i m myDSO As New DSOFile.0leDocumentProperties
myDSO.Open " C : \ t e s t . t x t " , T r u e
MsgBox m y D S O . S u m m a r y P r o p e r t i e s . A u t h o r
myDSO.Close
End Sub
I Using Existing DLLs I 685
TestDSOD looks very much like TestDSOA, only we are opening a different
file. Test.txt is a standard ASCII file. While in Windows Explorer, right-
click on the file and select Properties to display a few tabs.
If we compare this dialog box with the one a couple of pages ago we will
discover that this dialog does not have a Custom tab. This indicates to us
that the file is not a Structured Storage File and that the properties
shown are due to NTFS functionality. So, we 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 we know whether a file is OLE (Structured Storage)
or not?
S u b TestDSOE()
Dim myDSO A s New DSOFile.0leDocumentProperties
'First we try a txt file
myDS0.0pen "C:\test.txt", True
If myDSO.IsOleFile = False Then
MsgBox "The file is not Structured Storage."
Else
MsgBox "Structured Storage File Found.''
End If
I Chapter 33: Using Third Party ActiveX Controls and DLLs I
myDSO.Close
'Now f o r t h e D G N F i l e
myDSO.Open " C : \ t e s t . d g n " , True
I f myDSO. I s O l e F i 1 e = Fa1 s e T h e n
MsgBox " T h e f i l e i s n o t S t r u c t u r e d S t o r a g e . "
Else
MsgBox " S t r u c t u r e d S t o r a g e F i 1 e F o u n d . "
End I f
myDSO.Close
End S u b
S u b TestDSOF( 1
D i m myDSO As New DSOFile.0leDocumentProperties
my D S O . Open " C : \ t e s t . d g n " , Fa 1 s e
I f myDSO. I s O l e F i 1 e = T r u e Then
myDS0.CustomProperties.Add " F o r B o o k " , -
" L e a r n i n g M i c r o S t a t i o n VBA"
myDSO.Save
End I f
myDSO.Close
End S u b
........................ :
..................Book
;For ....... ; Learning MicroStation VBA
REVIEW
One of the powers of VBA is that we are not limited to the calls directly
exposed by VBA. We can make use of other programming components
developed by others to speed development and augment functionality.
We have discussed a few References that, when added, can add
significant power to the software we develop.
Opening the References dialog box in VBA, adding a Reference, and
opening the Object Browser is a great way to familiarize ourselves with
the functionality exposed by any Reference on our computers.
34 Working With Excel
In this Chapter
Connecting to Excel
Workbooks, Worksheets, Ranges, and Cells
Tag Extraction into Excel
CONNECTING TO EXCEL
There are three ways to connect to Excel. We will begin by using
Getobject:
Getobject
Sub TestExcelA( )
689
690 I Chapter 34: Working With Excel I
Dim myExcel As O b j e c t
S e t myExcel = G e t o b j e c t ( , E x c e l . A p p l i c a t i o n )
End Sub
If Excel is not
running, we see
an error when
w e attempt to
get Excel.
When we see this error, we know we just attempted to Get Excel and
Excel was not running. If Excel is running, the macro TestExcel A runs
without anyproblems. But what does TestExcel A do?
We declare a variable, myExcel as an Object. Then we Set the variable to
the return value of GetObj e c t . 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 doesnt
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.
OK
Sub T e s t E x c e l B ( 1
D i m myExcel A s Excel .Application
S e t myExcel = G e t o b j e c t ( , E x c e l . A p p l i c a t i o n )
End Sub
I Connecting to Excel I 691
Create0bject
If Microsoft Excel is not running or if we want to create a new instance
of the Excel.Application, we can use C r e a t e o b j e c t .
Sub T e s t E x c e l D ( )
Dim myExcel As Excel .Application
Set myExcel = CreateObject(Exce1 .Application)
myExcel .Visible = True
myExcel .Workbooks.Add
End Sub
T e s t Excel D creates a new instance of Excel. It does not matter whether or
not Excel had been running, a new instance of Excel is created.
C r e a t e o b j e c t can be useful if multiple instances of Excel are running.
When we use Get 0 b j ec t, we do not know beforehand which instance of
Excel we will get. When we use C r e a t eO b j e c t , we know exactly which
Excel.Application we are using because it is a new instance created.
692 I Chapter 34: Working With Excel I
New
When a Reference to the Excel Library has been added to our VBA
project, VBA understands what an Excel.Application Object is. If a
Reference has been added, we can use the New keyword to create an
Object.
Sub T e s t E x c e l D 2 ( )
D i m m y E x c e l As New E x c e l . A p p l i c a t i o n
m y E x c e l .V i s i b l e = True
myExcel .Workbooks .Add
End Sub
Sub T e s t E x c e l E ( )
D i m m y E x c e l As E x c e l . A p p l i c a t i o n
D i m mySheetA As W o r k s h e e t
D i m mySheetB As W o r k s h e e t
D i m mySheetC As W o r k s h e e t
D i m mySheetD As W o r k s h e e t
S e t myExcel = Getobject(, Excel . A p p l i c a t i o n )
S e t mySheetA = myExcel.ActiveSheet
S e t my S h e e t B = my Exc e 1 . A c t iv e W o r k b o o k . W o r k s h e e t s ( S imp 1 e G r id 1
S e t my Sh e e t C = . .
my Exce 1 A c t iv e W o r k bo o k W o r k s h ee t s ( C om p l exG r id 1
S e t mySheetD = myExcel.ActiveWorkbook.Worksheets(Sheet3)
D e b u g . P r i n t mySheetA.Name
D e b u g . P r i n t mySheetB.Name
D e b u g . P r i n t mySheetC.Name
D e b u g . P r i n t mySheetD.Name
S e t mySheetB = myExcel.ActiveWorkbook.Worksheets(1)
S e t mySheetC = myExcel.ActiveWorkbook.Worksheets(2)
S e t mySheetD = myExcel.ActiveWorkbook.Worksheets(3)
694 I Chapter 34: Working With Excel I
D e b u g . P r i n t mySheetB.Name
D e b u g . P r i n t mySheetC.Name
D e b u g . P r i n t mySheetD.Name
End Sub
Sub TestExcel F ( 1
D i m m y E x c e l As E x c e l . A p p l i c a t i o n
D i m mySheetA As W o r k s h e e t
S e t myExcel = Getobject(, "Excel . A p p l i c a t i o n " )
S e t mySheetA = myExcel.ActiveWorkbook.Worksheets("Simp1eGrid")
MsgBox mySheetA. R a n g e ( " B 1 " ) . T e x t
End Sub
Sub TestExcelG()
Dim myExcel As Excel.Application
Dim mySheetA As Worksheet
Set myExcel = Getobject(, "Excel.Application")
Set mySheetA = myExce1.ActiveWorkbook.Worksheets ("SimpleGrid")
MsgBox mySheetA. Cells (
End Sub
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 'X 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 TestExcel G( 1
D i m m y E x c e l As E x c e l . A p p l i c a t i o n
I Workbooks, Worksheets, Ranges, and Cells I 695
D i m mySheetA As Worksheet
S e t myExcel = G e t o b j e c t ( , "Excel . A p p l i c a t i o n " )
S e t my S h e e t A = my Exc e 1 . A c t iv e W o r k b o o k . W o r ks he e t s ( " S imp 1 e G r id "
MsgBox m y S h e e t A . C e l l s ( 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 Row and 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 T e s t E x c e l H ( )
D i m myExcel As E x c e l . A p p l i c a t i o n
D i m mySheetA As Worksheet
D i m CurRow As Long
D i m CurCol As Long
S e t myExcel = G e t o b j e c t ( , "Excel . A p p l i c a t i o n " )
S e t my S h e e t A = my Exc e 1 . A c t iv e W o r k b o o k . W o r ks he e t s ( " S imp 1 e G r id " 1
F o r CurRow = 1 To 7
F o r CurCol = A s c ( " A " ) To A s c ( " F " )
D e b u g . P r i n t m y S h e e t A . R a n g e ( C h r ( C u r C o 1 ) & CurRow)
N e x t CurCol
N e x t CurRow
End Sub
Sub T e s t E x c e l J()
D i m myExcel As E x c e l . A p p l i c a t i o n
D i m mySheetA As Worksheet
D i m CurRow As Long
D i m CurCol As Long
S e t myExcel = G e t o b j e c t ( , "Excel . A p p l i c a t i o n " )
S e t my S h e e t A = my Exc e 1 . A c t iv e W o r k b o o k . W o r ks he e t s ( " S imp 1 e G r id "
F o r CurRow = 1 To 7
F o r CurCol = 1 To 6
Debug.Print mySheetA.Cells(CurRow, CurCol)
N e x t CurCol
N e x t CurRow
End Sub
Both TestExcel H and TestExcel J print the values of a grid of cells to the
Immediate Window. TestExcelH can do this easily because we are
696 I Chapter 34: Working With Excel I
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 Ato IV: Writing code that flows from Z to %A 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
Row and Column numerically is easy to do but difficult to translatethe
Cell Column to a lettered Column in Excel. What is the lettered Column
Name associated with column 21 l?
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 A1 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 RlC1 reference style: When
RlC1 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 Cells I 697
counting out &A, XB, AC to find out what the Column Index is of
Column DC?
Sub T e s t E x c e l L ( )
D i m myExcel As E x c e l . A p p l i c a t i o n
D i m mySheetA As Worksheet
D i m CurRow As Long
D i m CurCol As Long
D i m m y C e l l As E x c e l .Range
S e t myExcel = G e t o b j e c t ( , Excel . A p p l i c a t i o n )
S e t mySheetA = myExcel . A c t i v e W o r k b o o k . W o r k s h e e t s ( S i mpl eG r id
698 I Chapter 34: Working With Excel I
F o r CurRow = 1 To 7
For CurCol = 1 To 6
Set myCell = mySheetA.Cells(CurRow, CurCol)
D e b u g . P r i n t m y C e l l . A d d r e s s ( T r u e , T r u e , x l A l ) & vbTab &
myCell .Address(True, True, x l R l C 1 )
Next CurCol
N e x t CurRow
End Sub
Sub T e s t E x c e l M ( 1
D i m m y E x c e l As E x c e l . A p p l i c a t i o n
D i m m y s e l e c t i o n As E x c e l . Range
S e t myExcel = GetDbject(, Excel . A p p l i c a t i o n )
Set myselection = myExcel . S e l e c t i o n
Debug.Print mySelection.Address(True, T r u e , x l R l C 1 )
End Sub
R2C2: R7C2
The colon (:) tells us B2 through B7 have been selected. There are
other ways to select cells in Excel.
Sub T e s t E x c e l N ( )
D i m m y E x c e l As E x c e l . A p p l i c a t i o n
D i m m y s e l e c t i o n As E x c e l .Range
D i m m y s h e e t As E x c e l . W o r k s h e e t
D i m S t a r t R o w As L o n g : D i m S t a r t C o l As L o n g
D i m EndRow As L o n g : D i m EndCol As Long
D i m X As L o n g : D i m Y As Long
Dim X S p l i t A As Variant: Dim X S p l i t B As V a r i a n t : Dim X S p l i t C As V a r i a n t
S e t myExcel = Getobject(, Excel . A p p l i c a t i o n )
S e t mySel e c t i on = myExcel . S e l e c t i o n
S e t mysheet = myExce1.ActiveSheet
X S p 1 it A = S p 1 it ( my Se 1 e c t io n .Add r e s s ( T r u e , T r u e , x 1 R 1 C 11 , : )
XSp 1 it B = Sp 1 it ( X Sp 1 it A ( 0 ) , C )
XSp 1 it C = Sp 1 it ( X Sp 1 it A ( 1) , C )
StartRow = Replace(XSplitB(O), R, I )
StartCol = XSplitB(1)
700 I Chapter 34: Working With Excel I
End R o w = Rep 1 ace ( X S p 1 it C ( 0 1 , " R" , " " 1
EndCol = XSpl it C ( 1 1
For Y = S t a r t R o w To EndRow
For X = S t a r t C o l To EndCol
Debug.Print mySheet.Cells(Y, X1.Text
Next X
Next Y
End Sub
Sub TestExcel P ( 1
D i m myExcel As E x c e l . A p p l i c a t i o n
D i m m y s e l e c t i o n As E x c e l . Range
S e t myExcel = G e t o b j e c t ( , "Excel . A p p l i c a t i o n " )
Set m y s e l e c t i o n = myExcel . S e l e c t i o n
D i m m y C e l l As Range
F o r Each m y C e l l I n m y s e l e c t i o n
D e b u g . P r i n t m y C e l l . A d d r e s s & vbTab & m y C e l l . T e x t
Next
End Sub
$B$E 200
$C$2 300
$D$E 400
$B$3 400
$C$3 600
TestExcel N and TestExcel P are very much alike. They accomplish the
same thing. In TestExcel 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
'M" style, we will extract it in (Row, Col) style.
Sub TestExcel Q( 1
D i m myExcel As E x c e l . A p p l i c a t i o n
D i m m y s e l e c t i o n As E x c e l . Range
D i m m y C e l l As Range
D i m s t r A d d As S t r i n g
I Workbooks, Worksheets, Ranges, and Cells I 701
Sub T e s t E x c e l R ( )
D i m m y E x c e l As E x c e l . A p p l i c a t i o n
D i m m y s h e e t As W o r k s h e e t
S e t myExcel = Getobject(, "Excel . A p p l i c a t i o n " )
S e t mysheet = myExce1.ActiveSheet
' G i v e B 1 a new V a l u e
mySheet.Range("Bl").Value = 70
' G i v e B2 a new F o r m u l a
mysheet. Range("B2"). Formul a = "=B$l*$A2*52"
' C o p y B2 t o t h e Windows C l i p b o a r d
m y s h e e t . R a n g e ( " B 2 " ) .Copy
' S e l e c t B2 t h r o u g h F7
myS h e e t . Range ( " B2 " , " F 7 " ) . Se 1 e c t
'Paste copied formula t o selected c e l l s
mySheet.Paste
' S e l e c t B2
mySheet.Range("B2") . S e l e c t
' R e s e t C u t / C o p y Mode
m y E x c e l .CutCopyMode = False
702 I Chapter 34: Working With Excel I
End Sub
Sub T e s t E x c e l S ( 1
D i m m y E x c e l As E x c e l . A p p l i c a t i o n
D i m myWB A s E x c e l . W o r k b o o k
D i m myWSA As E x c e l . W o r k s h e e t
D i m myWSB As E x c e l . W o r k s h e e t
D i m myWSC As E x c e l . W o r k s h e e t
D i m R a d V a l u e As D o u b l e
D i m I n c h V a l u e As D o u b l e
D i m F e e t V a l u e As D o u b l e
D i m CurRow As L o n g
S e t myExcel = Getobject(, "Excel . A p p l i c a t i o n " )
S e t myWB = myExcel . A c t i v e W o r k b o o k
S e t myWSA = myWB.Worksheets("Sheetl"1
S e t myWSB = myWB.Worksheets("Sheet2")
S e t myWSC = myWB.Worksheets("Sheet3")
' M e r g e H e a d e r Rows
myWSA. Range ( " A 1 " , " 1 .M e r g e T r u e
D1"
my W S B . Ra n g e ( " A 1" , D 1" 1 . Me r g e T r u e
"
'Add T i t l e s
myWSA.Range("Al"1.Value = "Degrees"
myW SA . Range ( " A 1 " 1 . Ho r iz o n t a 1 A1 ignmen t = x l Center
myWSB . Range ( " A 1 " 1 . V a l u e = " I n c h e s "
my W S B . Ra n g e ( " A 1" 1 . H o r iz o n t a 1 A 1 ig n me n t = x 1C e n t e r
myW S C . Range ( " A 1 " 1 . Va 1 u e = " Feet "
my W SC . Ra n g e ( " A 1" 1 . H o r iz o n t a 1 A 1 ig n me n t = x 1C e n t e r
'Add Values
myWSA.Range("A2"1.Value = "Degrees"
myWSA. Range ( " B2 " 1 . V a l u e = " Radi ans "
I Workbooks, Worksheets, Ranges, and Cells I 703
CurRow = 3
For RadValue = 0 To 360 S t e p 5
myWSA.Range("A" & CurRow) = RadValue
myWSA.Range("B" & CurRow).Value = RadValue * Atn(1) * 4 / 180
CurRow = CurRow + 1
N e x t RadVal u e
CurRow = 3
myWSB.Range("A2") = "Inch"
myWSB. R a n g e ( " B 2 " ) = "Centimeter"
For Inchvalue = 1 T o 36
myWSB.Range("A" & CurRow) = InchValue
myWSB.Range("B" & CurRow).Formula = "=A" & CurRow & " * 2.54"
CurRow = CurRow + 1
Next I n c h V a l ue
CurRow = 3
myWSC. R a n g e ( " A 2 " ) = "Feet"
myWSC.Range("B2") = "Miles"
For FeetValue = 0 To 20000 S t e p 1000
myWSC.Range("A" & CurRow) = FeetValue
myWSC.Range("B" & C u r R o w ) . F o r m u l a = "=A" & CurRow & " / 5280"
CurRow = CurRow + 1
Next FeetVal ue
End S u b
Level
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 RAND function re-calculates the values whenever the Worksheet is
recalculated. The values dont stick, so one persons values will differ
from another persons values.
I Workbooks, Worksheets, Ranges, and Cells I 705
Now that we have seen the formulas, let's take a look at the values
generated.
For this example, the number of rows is not fxed. 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 T e s t E x c e l T ( )
Dim myExcel As Excel .Application
Dim myWSA As Excel .Worksheet
Dim CurRow As L o n g
CurRow = 2
Set myExcel = Getobject(, "Excel .Application")
Set my W SA = my Ex c e 1 .Act i v e W o r kb o o k .W o r ks h e e t s ( Sh e e t 1 1
" "
myWSA.Cells(CurRow, 3 )
CurRow = CurRow + 1
Wend
myWSA.Calculate
End Sub
706 I Chapter 34: Working With Excel I
If we have a specific number of rows we want to extract, we could use a
Fo r I ... Next structure. But since the number of rows may vary, we use a
Whi 1 e ... Wend structure.
The last line of code forces the Worksheet to recalculate, which
generates new random numbers for us.
Let's build upon T e s t Excel T by drawing inside Microstation.
Sub TestExcel U( 1
D i m m y E x c e l As E x c e l . A p p l i c a t i o n
D i m myWSA As E x c e l . W o r k s h e e t
D i m CurRow As L o n g
D i m m y p o i n t As P o i n t 3 d
D i m m y T e x t N o d e As T e x t N o d e E l e m e n t
D i m m y R o t M a t r i x As M a t r i x 3 d
CurRow = 2
S e t myExcel = GetDbject(, "Excel . A p p l i c a t i o n " )
S e t myWSA = m y E x c e l . A c t i v e w o r k b o o k . Works h e e t s ( " S h e e t l " )
W h i l e m y W S A . C e l l s ( C u r R o w , 1) <> " "
myP0int.X = m y W S A . C e l l s ( C u r R o w , 1)
myP0int.Y = m y W S A . C e l l s ( C u r R o w , 2)
myP0int.Z = myWSA.Cells(CurRow, 3 )
S e t myTextNode = CreateTextNodeElementl(Nothing, mypoint, ~
my R o t M a t r ix )
m y T e x t N o d e . A d d T e x t L i n e CurRow - 2
ActiveModelReference.AddElement m y T e x t N o d e
CurRow = CurRow + 3
Wend
myWSA.Calculate
End S u b
Our Worksheet has a column for "Level': In the above example we are
not making use of it. Let's build upon TestExcelU 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 Checklevel that creates a
Level if it does not exist. We will use this Function inside T e s t Excel V.
F u n c t i o n CheckLevel ( L e v e l N a m e As S t r i n g ) As L e v e l
On E r r o r Resume N e x t
S e t CheckLevel = ActiveDesignFile.Levels(Leve1Name)
I Tag Extraction I 707
If E r r . N u m b e r <> 0 Then
S e t CheckLevel = A c t i v e D e s i g n F i l e . A d d N e w L e v e 1 ( L e v e l Name)
End If
Err.Cl e a r
End F u n c t i o n
Sub T e s t E x c e l V ( )
D i m m y E x c e l As E x c e l . A p p l i c a t i o n
D i m myWSA As E x c e l . W o r k s h e e t
D i m CurRow As Long
D i m m y p o i n t As P o i n t 3 d
D i m m y T e x t N o d e As T e x t N o d e E l e m e n t
D i m m y R o t M a t r i x As M a t r i x 3 d
CurRow = 2
S e t myExcel = Getobject(, "Excel . A p p l i c a t i o n " )
S e t my W S A = my Ex c e 1 .A c t iv e W o r k b o o k . W o r k s h e e t s ( " S h e e t 1"
W h i l e myWSA.Cells(CurRow, 1) <> " "
myP0int.X = m y W S A . C e l l s ( C u r R o w , 1)
myP0int.Y = myWSA.Cells(CurRow, 2 )
myP0int.Z = myWSA.Cells(CurRow, 3 )
S e t myTextNode = CreateTextNodeElementl(Nothing, ~
mypoint, myRotMatrix)
myTextNode.AddTextLine CurRow - 2
CheckLevel CStr(myWSA.Cells(CurRow, 4))
m y T e x t N o d e . L e v e 1 = CheckLevel(myWSA.Cells(CurRow, 4))
ActiveModelReference.AddElement myTextNode
CurRow = CurRow + 1
Wend
myWSA.Calculate
End Sub
TAGEXTRACTION
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 E x p o r t F o l derTagsToHTML. In this
708 I Chapter 34: Working With Excel I
macro, we had created an HTML document that displays the Tag
information of files in a specific folder.
Sub T e s t E x c e l W ( 1
Dim myDGN As DesignFile
Dim myFSO As New Scripting.FileSystemDbject
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 myFi 1 ter As New El ementScanCri teri a
'New D e c l a r a t i o n s
D i m myExcel As E x c e l . A p p l i c a t i o n
D i m myWS As E x c e l .Worksheet
D i m CurRow As Long
'New Code
S e t myExcel = New E x c e l . A p p l i c a t i o n
myExcel . V i s i b l e = True
myExcel .Workbooks.Add
S e t myWS = myExcel.ActiveSheet
CurRow = 2
"Examples\Building\Dgn")
For Each myFile In myFolder.Files
Select Case myFile.Type
Case "Bentley Microstation Design File"
' F i l e Name and Merge C e l l s
myWS.Cells(CurRow, 1) = myFile.Path
rnyWS.Range("A" & CurRow & " : F " & CurRow).MergeCells = True
rnyWS.Range("A" & CurRow. "F" & CurRow) . B o r d e r A r o u n d -
, xlThick
'Header
CurRow = CurRow + 1
I Tag Extraction I
myWS. Range("B" & CurRow) = "Tag S e t Name"
myWS.Range("B" & CurRow).Font.Bold = True
myWS. Range("C" & CurRow) = "Tag Name"
myWS.Range("C" & CurRow).Font.Bold = True
myWS.Range("D" & CurRow) = "Tag V a l u e "
myWS.Range("D" & CurRow).Font.Bold = True
myWS.Range("E" & CurRow) = " I D H i g h "
myWS.Range("E" & CurRow).Font.Bold = True
myWS.Range("F" & CurRow) = "ID Low"
myWS.Range("F" & CurRow).Font.Bold = True
CurRow = CurRow + 1
'Open t h e F i l e
S e t myDGN = Application.OpenDesignFileForProgram( -
myFile.Path, True)
F o r Each myTagSet I n myDGN.TagSets
S e l e c t Case UCase(myTagSet.Name)
Case U C a s e ( T a r g e t T a g s e t 1
rnyFi 1 t e r . E x c l u d e A l 1 T y p e s
myFi 1 t e r . I n c l u d e T y p e rnsdElementTypeTag
S e t myElemEnum = ~
rnyDGN.Mode1 s(l) . S c a n ( r n y F i 1 t e r )
Whi 1 e rnyEl ernEnurn.MoveNext
S e t myTag = myElemEnum.Current
' W r i t e t o Excel
myWS.Cells(CurRow, 2) = TargetTagset
myWS.Cells(CurRow, 3) = -
m y T a g . T a g D e f i n i tionName
myWS.Cells(CurRow, 4) = myTag.Value
myWS.Cells(CurRow, 5) = myTag.ID.High
myWS.Cells(CurRow, 6) = myTag.ID.Low
CurRow = CurRow + 1
Wend
End S e l e c t
Next
myDGN. C1 o s e
End S e l e c t
Next
End S u b
710 I Chapter 34: Working With Excel I
The code here is very similar to that in Chapter 28. We open each file
ForProgram,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 headersto 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 BSI300 to BSI300A:
we could make the change in Excel and run the macro T e s t E x c e l X to
update the BSI300AE3O1-Elevations.dgnfile.
Sub TestExcel X ( 1
D i m myDGN As D e s i g n F i l e
D i m myTag As T a g E l e m e n t
I Tag Extraction I 711
REVIEW
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.
Working With Databases
(ActiveX Data Objects)
Access, Oracle, DB2, SQL Server. When theres 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:
Primer on ActiveX Data Objects
UDL File Basics
Connections and Recordsets
Structured Query Language Essentials
Extending ActiveX Data Objects
Examining Database Schema
Excel Files as Databases
713
714 I Chapter 35: Working With Databases (ActiveX Data Objects) I
PRIMER ON ACTIVEXDATAOBJECTS
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 what 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 and 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 ActiveXData Objects,we must add
a Reference to it. Lets do so right now.
Execute
ExecuteComplete
InfoMessage
IsolationLevel
Mode
Openschema
Properties
Provider
RollbackTrans
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 flucefointsmdb. 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).
14029
UDL FILEBASICS
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.
.
3 In the View tab, uncheck the Hide extensions for known file types:
4 Right-click in the folder where the UDL file is to be created and
select New > Text Document.
View
Arrange Icons By
Refresh
Undo Rename
0 KB Text Document
7 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.
8 Open the UDL file by double-clickingit in Windows Explorer.
Sub TestConnectionA( 1
D i m myDB As New A D O D B . C o n n e c t i o n
myDB.0pen f i l e n a m e = C : \ M i c r o S t a t i o n VBA\PlacePoints.udl
myDB. C1 o s e
End S u b
Sub TestConnectionB( 1
D i m myDB As A D O D B . C o n n e c t i o n
S e t myDB = New A D O D B . C o n n e c t i o n
myDB.0pen f i l e n a m e = C : \ M i c r o S t a t i o n VBA\PlacePoints.udl
myDB. C1 o s e
End S u b
Sub T e s t C o n n e c t i o n C ( )
D i m myDB As A D O D B . C o n n e c t i o n
S e t myDB = New A D O D B . C o n n e c t i o n
myDB.Open " f i 1 e name=C: \ M i c r o s t a t i o n VBA\Pl a c e p o i n t s . u d l "
Sub T e s t C o n n e c t i o n D ( )
D i m myDB As A D O D B . C o n n e c t i o n
S e t myDB = New A D O D B . C o n n e c t i o n
myDB.Open " f i 1 e name=C: \ M i c r o s t a t i o n VBA\Pl a c e p o i n t s . u d l "
Sub T e s t C o n n e c t i o n E ( )
D i m myDB As A D O D B . C o n n e c t i o n
S e t myDB = New A D O D B . C o n n e c t i o n
myDB.Open " f i 1 e name=C: \ M i c r o s t a t i o n VBA\Pl a c e p o i n t s . u d l "
Sub TestConnectionF( 1
D i m myDB As ADODB.Connection
S e t myDB = New ADODB.Connection
myDB.Open " f i l e n a m e = C : \ M i c r o S t a t i o n VBA\PlacePoints.udl"
myDB. E x e c u t e "A1 t e r T a b l e P1 a c e N o t e s Add T h e N o t e Memo"
myDB. C1 o s e
End Sub
Sub TestConnectionG( 1
D i m myDB As ADODB.Connection
S e t myDB = New ADODB.Connection
myDB.Open " f i l e n a m e = C : \ M i c r o S t a t i o n VBA\PlacePoints.udl"
myDB.Execute "Update PlaceNotes S e t TheNote = 'Reviewed' " &
" Where N o t e B y = 'JKW"'
myDB. C1 o s e
End Sub
Sub TestConnectionH( 1
D i m myDB As ADODB.Connection
S e t myDB = New ADODB.Connection
myDB.Open " f i l e n a m e = C : \ M i c r o S t a t i o n VBA\PlacePoints.udl"
S e l e c t Case m y D B . S t a t e
Case a d s t a t e c l o s e d
MsgBox " C o n n e c t i o n i s C1 o s e d . "
Case a d s t a t e c o n n e c t i n g
MsgBox " C o n n e c t i o n i s C o n n e c t i n g . "
Case a d S t a t e E x e c u t i n g
722 I Chapter 35: Working With Databases (ActiveX Data Objects) I
MsgBox "Connection is Executing."
Case adStateFetching
MsgBox "Connection is Fetching."
Case adstateopen
MsgBox "Connection is Open. "
End Select
myDB.Close
End Sub
TestConnectionH opens a Connection and then looks at each of the
possible States by using a Sel e c t Case statement.
In TestConnectionJ, 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 T e s t C o n n e c t i o n J ( )
If myDB.State = adstateclosed Then
myDB.Open "file name=C:\MicroStation VBA\PlacePoints.udl"
End If
MsgBox "Use the Connection Object Here"
End Sub
It should be noted here that in TestConnectionJ we are not declaring the
variable myDB or setting it to a New AD0DB.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,
'General Declarations
Dim myDB as New ADDDB.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 T e s t C o n n e c t i o n K ( )
Dim myDB As ADDDB.Connection
Set myDB = New ADDDB.Connection
I Connections, Recordsets, and More I 723
myDB. C1 ose
End Sub
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.
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 TestConnectionL( 1
Dim myDB As ADODB.Connection
Dim ConnectionStringVals(0 T o 2) As String
Set myDB = New ADODB.Connection
ConnectionStringVals(0) = "Provider=Microsoft.Jet.OLEDB.4.0"
ConnectionStringVals(1) = "User ID=Admin"
ConnectionStringVals(2) = "Data Source=" & ~
"C:\Microstation VBA\PlacePoints.mdb"
myDB.0pen J o i n ( C o n n e c t i o n S t r i n g V a 1 s , " ; " )
MsgBox myDB.State
myDB. C1 ose
End Sub
In TestConnectionL we are opening the same database as we were by
using the UDL file f/ucefoints.ud/ 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 T e s t R e c o r d s e t A ( 1
D i m myDB As A D O D B . C o n n e c t i o n
D i m myRS As New R e c o r d s e t
S e t myDB = New A D O D B . C o n n e c t i o n
myDB.Open " f i l e n a m e = C : \ M i c r o S t a t i o n VBA\PlacePoints.udl"
myRS.Open " S e l e c t * f r o m P o i n t s Where C o u n t y = 'Ventura"', -
myDB, a d O p e n D y n a m i c , a d L o c k O p t i m i s t i c
W h i l e myRS.EOF = False
Debug . P r in t my RS ( " Des c r ip t io n " 1
myRS . M o v e N e x t
Wend
myRS.Close
myDB.Close
End Sub
Windmill Canyon
Womens Improvement Club of Hueneme
Wood Canyon
Wood Creek Park
Wood Ranch 1027 D a m
Wood Ranch Golf Club
Wood Ranch Reservoir
Woodside Linear Park
Woodside Park
World University
Xahaagua (historical)
Xocotoc (historical)
Yerba Buena Beach
Yerba Buena School
Ypuc (historical )
In our first example, we use the Recordset Object to return all fields in
all records where the field 'County' has a value of 'Venturd 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 T e s t R e c o r d s e t A we can see that we use a Whi 1 e ... Wend
statement and we look at the EOF (End of File) property. As long as the
I Connections, Recordsets, and More I 725
Sub T e s t R e c o r d s e t B ( 1
D i m myDB As A D D D B . C o n n e c t i o n
D i m myRS As New R e c o r d s e t
S e t myDB = New A D O D B . C o n n e c t i o n
myDB.Open " f i l e n a m e = C : \ M i c r o S t a t i o n VBA\PlacePoints.udl"
myRS.Open " S e l e c t * f r o m P o i n t s Where C o u n t y = 'Ventura"', -
myDB, a d O p e n F o r w a r d O n l y , a d L o c k R e a d O n l y
MsgBox m y R S . R e c o r d C o u n t
my RS . C1 o s e
myDB. C1 o s e
End Sub
Sub TestRecordsetC( 1
D i m myDB As A D D D B . C o n n e c t i o n
D i m myRS As New R e c o r d s e t
S e t myDB = New A D D D B . C o n n e c t i o n
myDB.Open " f i l e n a m e = C : \ M i c r o S t a t i o n VBA\PlacePoints.udl"
myRS.Open " S e l e c t * f r o m P o i n t s Where C o u n t y = 'Ventura"', -
myDB, a d o p e n s t a t i c , adLockReadOnly
MsgBox m y R S . R e c o r d C o u n t
myRS.Close
myDB.Close
End Sub
Sub T e s t R e c o r d s e t D ( )
D i m myDB As ADODB.Connection
D i m myRS As New R e c o r d s e t
S e t myDB = New ADODB.Connection
myDB.0pen " f i l e n a m e = C : \ M i c r o S t a t i o n VBA\PlacePoints.udl"
myRS.Open " S e l e c t * f r o m P o i n t s Where C o u n t y = 'Ventura'", -
myDB, adOpenDynamic, a d L o c k O p t i m i s t i c
W h i l e myRS.EOF = False
Debug . P r in t my RS ( " De s c r ip t ion " )
myRS. M o v e N e x t
Wend
myRS .Move F i rs t
W h i l e myRS.EOF = False
D e b u g . P r i n t myRS("Cel1Name")
myRS. M o v e N e x t
Wend
my RS . C1 o s e
myDB. C1 o s e
End Sub
Sub T e s t R e c o r d s e t E ( )
D i m myDB As ADODB.Connection
D i m myRS As New R e c o r d s e t
S e t myDB = New ADODB.Connection
728 I Chapter 35: Working With Databases (ActiveX Data Objects) I
myDB.Open " f i 1 e name=C: \ M i c r o s t a t i o n V B A \ P l a c e p o i n t s . u d l "
In this example, we are only looking for records where the County =
'Venturd We use "Order by CellName" so the Recordset is 'sorted' by the
CellName field. Then we use the F i n d 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'.
Sub T e s t R e c o r d s e t F ( 1
D i m myDB As A D O D B . C o n n e c t i o n
D i m myRS As New R e c o r d s e t
S e t myDB = New A D O D B . C o n n e c t i o n
myDB.Open " f i l e n a m e = C : \ M i c r o S t a t i o n VBA\PlacePoints.udl"
myRS.Open " S e l e c t D e s c r i p t i o n , C e l l N a m e f r o m P o i n t s " & -
I Connections, Recordsets, and More I 729
myDB, a d O p e n D y n a m i c , a d L o c k O p t i m i s t i c
Debug. P r in t my RS . G e t S t r in g ( adC 1 ip S t r in g , - 1, " I " , vbC r
my RS . C1 o s e
myDB. C1 o s e
End Sub
Sub T e s t R e c o r d s e t H ( )
D i m myDB As A D O D B . C o n n e c t i o n
D i m myRS As New R e c o r d s e t
S e t myDB = New A D O D B . C o n n e c t i o n
myDB.0pen " f i l e n a m e = C : \ M i c r o S t a t i o n VBA\PlacePoints.udl"
myRS.Open "P1 a c e N o t e s " , myDB, a d O p e n D y n a m i c , a d L o c k O p t i m i s t i c
myRS.AddNew
myRS("P1aceID") = 4
730 I Chapter 35: Working With Databases (ActiveX Data Objects) I
my RS ( Note By 1 = J KW
" " " "
myRS("NoteDate"1 = Now
myRS("TheNote"1 = "New Note"
myRS. Update
myRS.Close
myDB.Close
End Sub
Sub T e s t R e c o r d s e t J ( 1
Dim myDB As ADODB.Connection
Dim myRS As New Recordset
Set myDB = New ADODB.Connection
myDB.Open "file name=C:\MicroStation VBA\PlacePoints.udl"
myRS.Open "Select * from PlaceNotes Where PlaceID = l " , -
myDB, adOpenDynamic, adLockOptimistic
While myRS.EOF = False
myRS("PlaceID"1 = 14
myRS. Update
myRS .MoveNext
Wend
myRS.Close
myDB.Close
End Sub
In T e s t R e c o r d s e t H , we add a new record to the table. In T e s t R e c o r d s e t J ,
we query the database and change the PlaceID value in each record
retrieved by the S Q L 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 S Q L statements that can be
I SQL Essentials I 731
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 State 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:
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.
P r i v a t e Sub U s e r F o r m - I n i t i a l i z e 0
D i m myDB As New ADODB.Connection
D i m myRS As New ADODB.Recordset
myDB.Open " f i l e n a m e = C : \ M i c r o S t a t i o n VBA\Pl a c e P o i n t s . u d l "
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://geonumes.usgs.gov/stuteguz/
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
cmbPointTrpe and 1stDescription controls. If we didnt Clear the
ComboBoxes, 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
cmbStateComboBox. Since the State field is a String (Text), we use the
Apostrophe () to begin and end the value.
Order By
The Order By statement allows us to specify how we want to sort the
Recordsets records. Multiple fields can be specified. We use XSC for
Ascending and DESC for a Descending sort.
P r i v a t e Sub c m b S t a t e - C l i c k ( 1
D i m myDB A s New A D O D B . C o n n e c t i o n
D i m myRS A s New A D O D B . R e c o r d s e t
cmbCounty.Clear
cmbPoi n t T y p e . C1 e a r
1 s t D e s c r i D t i o n . C1 e a r
I SQL Essentials I 733
W h i l e myRS.EOF = F a l s e
cm b C o u n t y .Add It em my R S ( " C o u n t y " )
myRS. M o v e N e x t
Wend
my RS . C1 o s e
myDB. C1 o s 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 ComboBox
with only those Point Types that appear in records with the selected
State and the selected County.
P r i v a t e Sub c m b c o u n t y - C l i c k 0
D i m myDB As New A D O D B . C o n n e c t i o n
D i m myRS As New A D O D B . R e c o r d s e t
cmbPointType.Clear
1s t D e s c r i p t i o n . C l e a r
myDB. Open " f i l e n a m e = C : \ M i c r o S t a t i o n VBA\Pl a c e P o i n t s . u d l "
End Sub
When the user clicks on the PointTrpe 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
P r i v a t e Sub c m b P o i n t T y p e c C l i c k 0
D i m myDB As New ADODB.Connection
D i m myRS As New ADODB.Recordset
1 s t D e s c r i p t i o n . C1 e a r
myDB.Open " f i 1 e name=C: \ M i c r o s t a t i o n V B A \ P l a c e p o i n t s . u d l "
"and PointType = "' & cmbPointType.Text & " ' " & -
"Order by D e s c r i p t i o n ASC", myDB
W h i l e myRS.EOF = False
1 s t D e s c r i p t i o n . A d d I t e m myRS( " D e s c r i p t i o n " )
1s t D e s c r i p t i o n . L i s t ( 1 s t D e s c r i p t i o n . L i s t c o u n t - 1, 1) =
my RS ( " Un iq u e ID "
myRS . M o v e N e x t
Wend
myRS.Close
myDB.Close
End Sub
All of the above code forms the framework for allowing the user to
select Places from the database.
At this point, CA
the Form
looks like this:
P r i v a t e Sub c m d R e p o r t - C l i c k 0
If 1stDescription.Text = " " Then E x i t Sub
D i m myDB As New ADODB.Connection
D i m myRS As New ADODB.Recordset
D i m m y F i e l d As F i e l d
D i m F F i l e As Long
myDB.Open " f i l e n a m e = C : \ M i c r o S t a t i o n VBA\Pl a c e P o i n t s . u d l "
P r i v a t e Sub c m d A d d N o t e - C l i c k 0
I f 1stDescription.Text = " " Then E x i t Sub
f r m A d d N o t e . T a g = 1 s t D e s c r i p t i o n . L i s t ( 1 s t D e s c r i p t i o n ._
L i s t I n d e x , 1)
frmAddNote.Show
End Sub
Here is the
Form
frmAddNote
When the user clicks the OK button, the following code is executed:
P r i v a t e Sub btnOK-C1 i c k ( 1
I f frmAddNote.Tag = " " Then
MsgBox " A d d N o t e n o t e x e c u t e d c o r r e c t l y . "
U n l o a d Me
End I f
I f txtNoteBy.Text = "I' Then
MsgBox " P l e a s e e n t e r N o t e By."
E x i t Sub
End I f
I f txtNote.Text = "I' Then
MsgBox " P l e a s e e n t e r N o t e . "
E x i t Sub
I SQL Essentials I 737
End I f
U n l o a d Me
End S u b
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 (ActiveX Data Objects) I
P r i v a t e Sub btnDraw-C1 i c k (
If 1stDescription.ListCount = 0 Then E x i t Sub
D i m myDB As New A D D D B . C o n n e c t i o n
D i m myRS As New ADDDB.Recordset
D i m m y p o i n t As P o i n t 3 d
D i m m y c i r c l e As A r c E l e m e n t
D i m m y T e x t As T e x t E l e m e n t
D i m R o t M a t r i x As M a t r i x 3 d
D i m I As Long
myDB.0pen " f i 1 e name=C: \ M i c r o S t a t i o n V B A \ P l a c e p o i n t s . u d l "
F o r I = 1 To 1stDescription.ListCount
I f lstDescription.Selected(1 - 1) = T r u e Then
myRS.0pen " S e l e c t * f r o m P o i n t s Where U n i q u e I D = " & -
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 ListIndex
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
P r i v a t e Sub b t n D r a w - C l i c k 0
If 1stDescription.ListCount = 0 Then E x i t Sub
D i m myDB As New ADODB.Connection
D i m myRS As New ADODB.Recordset
D i m m y p o i n t As P o i n t 3 d
D i m m y c i r c l e As A r c E l e m e n t
D i m m y T e x t As T e x t E l e m e n t
D i m R o t M a t r i x As M a t r i x 3 d
D i m I As Long
myDB. Open " f i l e n a m e = C : \ M i c r o S t a t i o n VBA\Pl a c e P o i n t s . u d l "
F o r I = 1 To 1 s t D e s c r i p t i o n . L i s t C o u n t
If lstDescription.Selected(1 - 1) = T r u e Then
myRS.Open " S e l e c t * f r o m P o i n t s Where U n i q u e I D = " & -
ActiveModelReference.AddElement m y T e x t
End I f
myRS.Close
End I f
Next I
myDB.Close
End Sub
740 I Chapter 35: Working With Databases (ActiveX Data Objects) I
F u n c t i o n C h e c k L e v e l ( L e v e 1 N a m e As S t r i n g ) As L e v e l
On E r r o r Resume N e x t
S e t C h e c k L e v e l = ActiveDesignFile.AddNewLeve1 ( L e v e l Name)
I f E r r . N u m b e r <> 0 Then
S e t C h e c k L e v e l = ActiveDesignFile.Levels(Leve1Name)
End I f
End F u n c t i o n
We only add two lines of code to the Click Event of btnDrawand we add
a Function named C h e c k l e v e l . Now, all Places selected are added to the
ActiveModelReference on a specific Level. The Level matches the
selected Point Trpe.
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.
EXTENDINGACTIVEXDATAOBJECTS
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.
Lets 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. Lets take a look at another way to work with them now.
Many web sites are making use of RSS technology. When a button is
shown with the initials RSS or XML,the button links to the sites RSS
I Extending ActiveX Data Objects I 741
feed. This feed 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.ud/. The
Provider is "Microsoft OLE DB Simple Provider': The Data Source for
this UDL file will be "MSXML2.DSOControl.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
Item
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 ReadRSSA()
D i m MyDB As New ADODB.Connection
D i m MyRS As New ADODB.Recordset
D i m C h a n n e l RS As New ADODB.Recordset
D i m I t e m R S As New ADODB.Recordset
W h i l e MyRS.EOF = False
S e t ChannelRS = MyRS("channel").Value
W h i l e ChannelRS.EOF = False
S e t ItemRS = ChannelRS("item").Value
W h i l e 1temRS.EOF = False
Debug . P r in t It em RS ( " t i t 1 e " )
Debug. P r in t v bTa b & ItemRS ( " 1 in k " )
Debug . P r in t v bTa b & ItemRS ( " d e s c r ip t io n " 1
1temRS.MoveNext
Wend
742 I Chapter 35: Working With Databases (ActiveX Data Objects) I
Channel RS.MoveNext
Wend
MyRS .MoveNext
Wend
End S u b
When the selected item is double-clicked we use the Shel 1 Execute API
command to open the default browser on the system and the selected
story appears.
Here is the code:
P r i v a t e Sub 1 s t R S S - D b l C l i c k ( B y V a 1 C a n c e l As M S F o r m s . R e t u r n B o o l e a n )
ShellExecute 0, "OPEN", lstRSS.List(lstRSS.ListIndex, 11, " ' I . " ' I . 0
End Sub
P r i v a t e Sub U s e r F o r m - I n i t i a l i z e 0
D i m MyDB As New A D O D B . C o n n e c t i o n
D i m MyRS As New A D O D B . R e c o r d s e t
D i m C h a n n e l R S As New ADODB.Recordset
D i m I t e m R S As New A D O D B . R e c o r d s e t
W h i l e MyRS.EOF = False
S e t ChannelRS = MyRS("channel").Value
W h i l e ChannelRS.EOF = False
S e t ItemRS = ChannelRS("item").Value
W h i l e 1temRS.EOF = False
1 s t R S S .Add It e m Rep1 a c e ( ItemRS ( " t it 1 e " 1 , v b L f , " " )
1 s t RS S . L i s t ( 1 s t RSS . L is tC o u n t - 1 , 1 ) = I tern RS ( " 1 in k " )
1temRS.MoveNext
Wend
ChannelRS.MoveNext
Wend
MyRS. M o v e N e x t
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.
EXAMININGDATABASE
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.
ActiveX Data Objects gives us the ability to look at the Schema or in
other words, Database Structure of a given database.
Sub TestSchemaA( 1
D i m myDB As New ADODB.Connection
D i m myRS As New ADODB.Recordset
myDB.Open " f i l e n a m e = C : \ M i c r o S t a t i o n VBA\PlacePoints.udl"
S e t myRS = myDB.OpenSchema(adSchemaCo1umns)
W h i l e myRS.EOF = False
D e b u g . P r i n t myRS("TABLE-NAME") & "1" & -
m y R S ( CHARACTER-MAXIMUM-LENGTH )
myRS . M o v e N e x t
Wend
E n d Sub
MSy5Acce55Object5IDatalTruell2813992
MSy5Acce550bject5IIDITruel3I
MSy5Acce55XMLIIdlFal5el31
~~y5~cce55XMLILValueITrue112810
MSy5A~~e55XMLIObjectGuidITrue1721
MSy5Acce55XMLIObjectNameITrue~130165
MSy~Mce55XML\Pr0perty\True\l30\65
~~y5~~~e55XMLIValuelTrue11301255
Pla~eN0te51NOteBYITrUe~l30~50
~lace~ote51NoteDatelTruel71
~ l a c e ~ o t I eP1i a c e r o lTPUe I 3 I
PlaceN0te5~TheN0te~True~l30~0
~ l a c e ~ o t I eUni
i q u e r o I Fa1 5 1 I 3 I
P o i n t s ICellNai7IelTrue1130130
P0int5~C0~ntylTr~ell30l50
Points ~ D e 5 ~ r i p t i o n ~ T r ~ e ~ l 3 O ~ l O O
~oint51~levationlTrue13I
Point5~FederalStatu5ITrue~130~30
Point51LatDecITruel5 I
P o i n t s I L a t D M S l T r u e I 1 3 0 I8
Point51LonOecITruel5I
Point51LonDMSITruell30l8
Points lPol ntType lTrue I130 I20
P0int5~P0~~latl0nlTruel3l
~oint51~ef-LatDecITrueI5l
Point5~Ref~LatDMSITrUe~l30~8
~oint51~ef-LonDecITrueI5l
Point5~Ref~LOnDMSITrUe~l30~8
Point51StatelTruell3Ol2
Points ITypeAlTruel3 I
Point5lTypeBlTrue113013
Point51UniqueID1Fa15e13I
Point51USGS-IDITruel3I
EXCELFILESAS 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. Lets begin by identifying an Excel file (As) we
want to work with. The towerdut.x/s 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.
750 I Chapter 35: Working With Databases (ActiveX Data Objects) I
Lets create a UDL file named xce/.ud/.
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 buttons dialog box looks for Microsoft Access Databases
(.mdb). We can select the *.* option in the Files of type combo box
and then select the Excel (.As) file.
towerdat XIS
All Files [x 1
I Excel Files as Databases I 751
1 R477
Since working with Excel is new territory, how can we tell what we have
to work with? Let's modify our previous "OpenSchemd' procedures to
work with the xce/.ud/file.
Sub TestSchemaCO
D i m myDB As New ADODB.Connection
D i m myRS As New ADODB.Recordset
D i m F F i l e As L o n g
my DB . Open " f i1 e name=C : \ M i c r o S t a t io n VBA\ E x c e 1 . u d 1 "
S e t myRS = myDB.OpenSchema(adSchemaColumns)
FFile = FreeFile
Open " C : \ D b S c h e m a . t x t " F o r O u t p u t As I l F F i l e
W h i l e myRS.EOF = False
P r i n t #FFile, myRS("TABLE_NAME") & "1'' & -
myRS( "CHARACTER-MAXIMUM-LENGTH" )
myRS . M o v e N e x t
Wend
C1 o s e # F F i 1 e
End Sub
Sub T e s t D B E x c e l A ( )
D i m myDB As New ADODB.Connection
D i m myRS As New ADODB.Recordset
D i m m y F i e l d As F i e l d
myDB.0pen " f i l e n a m e = C : \ M i c r o S t a t i o n VBA\Excel.udl"
myRS.Open "CTOWERDAT$I", myDB, -
adOpenDynamic, a d L o c k O p t i m i s t i c
W h i l e myRS.EOF = False
F o r Each m y F i e l d I n m y R S . F i e l d s
Deb u g . P r in t my F ie 1 d . N a me & " I "
& my F ie 1 d . V a 1 u e
Next
D e b u g . P r i n t vbCr
X I 18325
mvRS. M o v e N e x t Y I22791
2 I514
Wend DATASETlxy=18325,22791,514
Cable XI18290
End Sub Cable Y122929
Cable 21894
Sub T e s t D B E x c e l B ( )
D i m myDB As New ADODB.Connection
D i m myRS As New ADODB.Recordset
D i m C e n P t As P o i n t 3 d
D i m m y c i r c l e As A r c E l e m e n t
D i m R o t M a t r i x As M a t r i x 3 d
myDB.0pen " f i l e n a m e = C : \ M i c r o S t a t i o n VBA\Excel.udl"
myRS.Open "CTOWERDAT$I", myDB, -
adOpenDynamic, a d L o c k O p t i m i s t i c
W h i l e myRS.EOF = False
CenPt.X = myRS("X")
Cen P t . Y = my R S ( " Y " )
CenPt.Z = myRS("Z")
Set mycircle = CreateArcElementZ(Nothing, -
754 I Chapter 35: Working With Databases (ActiveX Data Objects) I
CenPt, 4, 4, R o t M a t r i x , 0 , 360)
A c t i v e M o d e l R e f e r e n c e . A d d E l e m e n t myCi r c l e
myRS . M o v e N e x t
Wend
End S u b
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 Objects 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.
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:
A Brief Introduction to Mathcad
Adding a Reference and using the Object Browser
Basic Macros that communicate with Mathcad
Region Objects - The Basis for All Calculations
The Mathcad Object Model
Driving Microstation Geometry from Mathcad
755
756 I Chapter 36: Microstation Leveraging Mathcad via VBA I
A BRIEFINTRODUCTION 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
materials 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.
ADDINGA 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.
f
I Adding a Reference and using the Object Browser I 757
Once a Reference has been added, we can use the VBA Object Browser
to browsethe Mathcad Object Model.
R e gions
Here is a portion
of the RoughLength := 18R + 5in
Worksheet. Jambshin :=
1.
-UI
2
Strikeshin := l i n
2
M d e n g t h := 300cm
MhLength := 5Ocm
Sub T e s t M a t h c a d A ( 1
Dim myMCA As Mathcad.Application
Dim myMCW As Mathcad.Worksheet
Set myMCA = Getobject(, "Mathcad.Application")
Set myMCW = myMCA.ActiveWorksheet
End Sub
We saw the Application and Activeworksheet objects in the Object
Browser. If we run the macro T e s t M a t h c a d A 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 Reference and using the Object Browser I 759
Sub TestMathcadA()
D i m myMCA As M a t h c a d . A p p l i c a t i o n
D i m myMCW As M a t h c a d . W o r k s h e e t
D i m myMCV As M a t h c a d . V a l u e
S e t myMCA = Getobject(, Mathcad.Application)
S e t myMCW = myMCA.ActiveWorksheet
S e t myMCV = myMCW.GetVa1 ue(RoughLength)
MsgBox myMCV.AsString & vbCr & myMCV.Type
End Sub
Sub TestMathcadB()
D i m myMCA As M a t h c a d . A p p l i c a t i o n
D i m myMCW As M a t h c a d . W o r k s h e e t
D i m myMCV As M a t h c a d . N u m e r i c V a l u e
S e t myMCA = Getobject(, Mathcad.Application)
S e t myMCW = myMCA.ActiveWorksheet
S e t myMCV = myMCW.GetValue(RoughLength)
MsgBox m y M C V . A s S t r i n g & v b C r & ~
myMCV.Type
End Sub
S u b TestMathcadC()
D i m myMCA As M a t h c a d . A p p l i c a t i o n
D i m myMCW As M a t h c a d . W o r k s h e e t
D i m myMCV As M a t h c a d . S t r i n g V a 1 u e
S e t myMCA = Getobject(, Mathcad.Application)
S e t myMCW = myMCA.ActiveWorksheet
S e t myMCV = myMCW.GetValue(RoughLength)
MsgBox m y M C V . A s S t r i n g & v b C r & -
myMCV.Type
End Sub
762 I Chapter 36: Microstation Leveraging Mathcad via VBA I
Instead of declaring myMCV as a NumericValue we declare it as a
StringValue: What happens?
Sub T e s t M a t h c a d D ( 1
D i m myMCA As M a t h c a d . A p p l i c a t i o n
D i m myMCW As M a t h c a d . W o r k s h e e t
D i m myMCV As M a t h c a d . V a l u e
D i m myMCNV As M a t h c a d . N u m e r i c V a l u e
D i m myMCSV As M a t h c a d . S t r i n g V a 1 u e
S e t myMCA = Getobject(, Mathcad.Application)
S e t myMCW = myMCA.ActiveWorksheet
S e t my MC V = my M C W . G e t V a 1 u e ( Rough L e n g t h 1
S e l e c t Case myMCV.Type
Case N u m e r i c
S e t myMCNV = myMCV
MsgBox m y M C N V . A s S t r i n g & v b C r & -
myMCNV.Type
Case S t r i n g
S e t myMCSV = myMCV
MsgBox m y M C S V . A s S t r i n g & v b C r & ~
myMCSV. V a l u e
End S e l e c t
End Sub
BASICMACROS
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.
Lets 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 1worksheet.
Sub T e s t M a t h c a d E ( )
D i m myMCA As M a t h c a d . A p p l i c a t i o n
D i m myMCW As M a t h c a d . W o r k s h e e t
D i m myMCV As M a t h c a d . V a l u e
D i m myMCNV As M a t h c a d . N u m e r i c V a 1 u e
S e t myMCA = Getobject(, Mathcad.Application)
S e t myMCW = myMCA.ActiveWorksheet
S e t myMCV = myMCW.GetValue(RoughLength)
S e t myMCNV = myMCV
MsgBox R o u g h L e n g t h : & myMCNV.Rea1 / 0 . 0 2 5 4 & Inches.
End Sub
Sub T e s t M a t h c a d F ( )
D i m myMCA As M a t h c a d . A p p l i c a t i o n
D i m myMCW As M a t h c a d . W o r k s h e e t
764 I Chapter 36: Microstation Leveraging Mathcad via VBA I
D i m myMCNV As M a t h c a d . N u m e r i c V a l u e
S e t myMCA = Getobject(, "Mathcad.Application")
S e t myMCW = myMCA.ActiveWorksheet
myMCW.SetValue " J a m b S h i m " , 0 . 3 7 5 * 0.0254
myMCW. R e c a l c u l a t e
S e t myMCNV = my MCW . G e t Va 1 u e ( " F i n is h L e n g t h " )
MsgBox " F i n i s h L e n g t h : " & myMCNV.Rea1 / 0 . 0 2 5 4
End Sub
Sub T e s t M a t h c a d F 2 ( )
D i m myMCA As M a t h c a d . A p p l i c a t i o n
D i m myMCW As Mathcad.IMathcadWorksheet2
D i m myMCNV As M a t h c a d . N u m e r i c V a 1 u e
S e t myMCA = Getobject(, "Mathcad.Application")
S e t myMCW = myMCA.ActiveWorksheet
myMCW.SetVa1 u e " J a m b S h i m " , "3/8"
myMCW. R e c a l c u l a t e
S e t myMCNV = my MCW . G e t Va 1 u e ( " F i n is h L e n g t h " 1
MsgBox " F i n i s h L e n g t h : " & myMCNV.Rea1 / 0 . 0 2 5 4
End Sub
I Basic Macros that Communicate With Mathcad I 765
Jambshim:= I
Strikeshim := li,
2
M d e n g t h := 300cm
MhLength := 5Ocm
Sub TestMathcadG()
D i m myMCA As M a t h c a d . A p p l i c a t i o n
D i m myMCW As M a t h c a d . W o r k s h e e t
D i m myMCR As M a t h c a d . R e g i o n
S e t myMCA = Getobject(, Mathcad.Application)
S e t myMCW = myMCA.ActiveWorksheet
MsgBox myMCW.Regions.Count
F o r Each myMCR I n myMCW.Regions
MsgBox myMCR.Type
Next
End S u b
mcBitmapRegion = 2
mcMathRegion = 1
766 I Chapter 36: Microstation Leveraging Mathcad via VBA I
mcMetafileRegion = 3
mcOLERegion = 4
mcTextRegion = 0
In the Sample 1 Worksheet, we see a lot of mcMathRegionRegions.
An mcMathRegionis a Region that involves numeric calculations and
variable assignments. So, even if a variable is holding a String value, it
qualifies as a MathRegion.
:._I_~...~.,...~.,.I. .,...
1,.1~.,,~ ~ .I.,. .,...
1,.1~.,,~ ~ .I.,. 1 1 . ~ ~
liMathcadRegion2
..........................................................................................
iMathcadWorksheet2
iMathcadWorksheets2
iMetadata
MathcadOid
Mathinteface
MatrixVaiue
MCAppOption
I Region Objects -The Basis for All Calculations I
Here are the Properties for the IMathcadRegion2 Object. One of them is
the "MathInterface" property. When we click on "MathInterface" in the
Classes list, we see the following:
Sub T e s t M a t h c a d H ( )
Dim myMCA As Mathcad.Application
Dim myMCW As Mathcad.Worksheet
Dim myMCR As Mathcad.IMathcadRegion2
Dim myMCI As Mathcad.MathInterface
Set myMCA = Getobject(, "Mathcad.Application")
Set myMCW = myMCA.ActiveWorksheet
For Each myMCR In myMCW.Regions
Set myMCI = myMCR.MathInterface
Debug.Print myMCI.XML
Next
End Sub
When we run this macro, the XML property for each MathInterface of
each IMathcadRegion2 Object is written to the Immediate Window.
Here is an example of what is written when we run T e s t M a t h c a d E:
This is the data that is associated with the RoughLength variable in our
Worksheet. Let's take a look at a few more:
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.
THEMATHCAD
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.
A ppIicat ion
Act iveWind ow Height TOP
Act iveWorksheet Left Version
Application Name Visible
C IoseAlI Parent Width
DefaultFilePath Path Windows
FuIIName 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 G e t o p t i o n Method, the HWND property (often used in
Windows API programming), and the S e t O p t i on Method.
DRIVING
MICROSTATION FROM MATHCAD
GEOMETRY
We have seen a few examples of getting and setting Mathcad variables
using VBA. Now, lets look at an example of how Mathcad can be used to
drive the creation of our Microstation geometry.
Width := 1R + 5m HoleDia := 1 2 m
Height _=2R i. 2m M d p a c m g := 5Om
OutsideBufFer := 5Om
Overd - OutsideBufFer.2
CalculateQumtity(0verQ :=
HoleDia i. M d p a c m g
QtyWidth := bc(CdculateQumtity(Width)) + 1
QtyHeight := bc(CdculateQumtity(Height)) 4 1
Overd - OutsideBufFer.2
Actu&pach&OverQ := - HoleDia
QtyWidth
Spacinfln := ActualSpacin&Width)
SpacrngYIn := Actudpacm&Height)
HoleDiaIn := HoleDia
OutsideBufFerIn := OutsideBufFer
Widthln := Width
+.
Heighffn := Height
Widthln = 17in
Heighffn = 26m
QtyWidth = 6
QtyHeight = 10
Spacinfln = 1.705in
SpacingYIn = 3.205m
HoleDiaIn = 0.472m
OutsideBufFerIn = 1.969in
<ml:define xmlns:ml="http://schemas.mathsoft.com/mathZO">
<ml :id xml :space="preserve">HeightIn</ml :id>
<ml :id xml :space="preserve">Height</ml :id>
</ml :define>
<ml:eval xmlns:ml="http://schemas.rnathsoft.com/math20">
<ml :id xml :space="preserve">WidthIn</ml :id>
<ml :unitOverride><ml :id xml :space="preserve">in</ml :id>
</ml :uni tOverride>
<result xmlns="http://schemas.mathsoft.com/mathZO">
<uni tedVal ue>
<ml : real>l7</ml :real >
<unitMonomial xmlns="http://schemas.mathsoft.com/unitslO">
<unitReference u n i t = " i n c h " > < / u n i t R e f e r e n c e >
</uni tMonomial>
</uni tedVal ue>
</result>
</ml :eval>
Here we can see a "define" Region and an "eval" Region. Notice how the
"eval" Region has an " i d value of "WidthIn" 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 G e t A l 1 Regions.
Function GetAllRegionsO As S t r i n g
On Error GoTo e r r h n d
Dim myMCA As Mathcad.IMathcadApplication2
Dim myMCW As M a t h c a d . I M a t h c a d W o r k s h e e t 2
774 I Chapter 36: Microstation Leveraging Mathcad via VBA I
D i m myMCR As M a t h c a d . I M a t h c a d R e g i o n 2
D i m m y M C I As M a t h c a d . M a t h I n t e r f a c e
D i m D o m S t r i n g s O As S t r i n g
ReDim D o m S t r i n g s ( 0 )
S e t myMCA = Getobject(, "Mathcad.Application")
S e t myMCW = myMCA.ActiveWorksheet
D i m myRegs As M a t h c a d . R e g i o n s
S e t myRegs = myMCW. R e g i o n s
F o r Each myMCR I n myRegs
S e t myMCI = myMCR.MathInterface
' D e b u g . P r i n t myMCI.XML
' D e b u g . P r i n t myMCI.UnitsXML
DomStrings(UBound(DomStrings)) = myMCI.XML
ReDim P r e s e r v e DomStrings(UBound(DomStrings) + 1)
DomStrings(UBound(DomStrings)) = m y M C I . U n i t s X M L
ReDim P r e s e r v e DomStrings(UBound(DomStrings) + 1)
Next
GetAllRegions = J o i n ( D o m S t r i n g s , vbCr & vbCr & vbCr)
E x i t Function
errhnd:
MsgBox E r r . N u m b e r & v b C r & E r r . D e s c r i p t i o n
End F u n c t i o n
F u n c t i o n G e t E v a l s O As V a r i a n t
D i m myDOM As New DOMDocument60
D i m myDOM2 As New DOMDocument60
D i m myNode As IXMLDOMNode
D i m m y N o d e L i s t As IXMLDOMNodeList
D i m EvalName As S t r i n g
D i m E v a l V a l u e As D o u b l e
D i m A l l R e g i o n s As S t r i n g
D i m x S p l i t 0 As S t r i n g
D i m E v a l A r r a y O As S t r i n g
I Driving Microstation Geometry from Mathcad I 775
ReDim E v a l A r r a y ( 0 ) As S t r i n g
A1 1 R e g i o n s = GetAll Regions
xSplit = Split(AllRegions, vbCr & vbCr & vbCr)
F o r I = L B o u n d ( x S p 1 i t ) To U B o u n d ( x S p 1 i t )
If xSplit(1) <> " " Then
myD0M.loadXML x S p l i t ( 1 )
I f myDOM.firstChi1d.baseName = " e v a l " Then
Eval Name = rnyDOM.getE1ementsByTagNarne("rn1 : i d " ) (0).Text
Eval Value = myDOM.getElementsByTagName("m1 : r e a l " ) (0).Text,
D e b u g . P r i n t E v a l Name
Debug.Print Eval Value
EvalArray(UBound(Eva1Array)) = EvalName & "=" & EvalValue
ReDim P r e s e r v e EvalArray(UBound(Eva1Array) + 1)
End I f
End I f
Next I
I f UBound(Eva1Array) > 0 Then
ReDim P r e s e r v e E v a l A r r a y ( U B o u n d ( E v a 1 A r r a y ) - 1)
End I f
GetEvals = EvalArray
End F u n c t i o n
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 DrawFromMathcadO
D i m A l l E v a l s O As S t r i n g
D i m P a r t w i d t h As Double
D i m P a r t H e i g h t As D o u b l e
D i m O u t s i d e B u f f e r As D o u b l e
D i m H o l e D i a As D o u b l e
D i m S p a c i n g X As D o u b l e
D i m S p a c i n g Y As D o u b l e
D i m QtyX As D o u b l e
D i m QtyY As D o u b l e
D i m F i l t e r R e t u r n O As S t r i n g
AllEvals = GetEvals
He i g h t I n=" ,
"
" "
" "
Qty Hei g h t=" ,
"
QtyX, QtyY
End Sub
The Array AllEvals 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 CDbl function. Each of these parameter values are
placed into their own variable. These variables are then used to call a
procedure named DrawPart.
I Driving Microstation Geometry from Mathcad I 777
Sub D r a w P a r t ( W i d t h As D o u b l e , H e i g h t As D o u b l e , -
O u t B u f f e r As D o u b l e , H o l e D i a As D o u b l e , ~
S p a c i n g X As D o u b l e , S p a c i n g Y As D o u b l e , ~
Q t y W i d t h As D o u b l e , Q t y H e i g h t As D o u b l e )
D i m m y L i n e As L i n e E l e m e n t
D i m myCi r c l e As E l 1 i p s e E l e m e n t
D i m XPos As D o u b l e
D i m YPos As D o u b l e
D i m R o t M a t r i x As M a t r i x 3 d
Point3dFromXY(O, H e i g h t ) )
ActiveModel Reference.AddElement myLine
P o i n t 3 d F r o m X Y ( O , 0))
ActiveModel Reference.AddElement myLine
H o l e D i a / 2, R o t M a t r i x )
ActiveModel Reference.AddElement m y c i r c l e
778 I Chapter 36: Microstation Leveraging Mathcad via VBA I
YPos = YPos + SpacingY
Next Y
XPos = XPos + SpacingX
Next X
End S u b
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.
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
O a O O O 0
0 0 0 0 0 0
O O O O 0 O
I Driving Microstation Geometry from Mathcad I 779
Spacmfln := ActualSpacm&Width,QtyWidth)
SpacingYIn := ActualSpacm&Height,PtyHeight)
0 0 0 0 0
0 0 Q 0 Q
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 Q
0 0 0 a 0
0 Q 0 0
Q 0 0 0
The Buffer area looks right. But the spacing between the holes isnt
meeting our criteria. A review of the Formulas and Functions in
Mathcad shows us that the Cal cul a t e Q u a n t it y 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.
Overall - OutsideBufFer-2 - HoleDia
CalculateQuantitdOveraU) := +I
HoleDia f MaxSpacing
We fix the formula in Mathcad and run the macro from within
MicrostationsVBA 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.
Now, it looks like the spacing between the holes is meeting our criteria
of MaxSpacing = 50mm. 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
Exter naI A ppIicat ions
In this Chapter:
ActiveX / COM Basics
References, Early Binding, and Late Binding
Getobject, Setobject, and New
What does WithEventsdo for us?
When to run macros from within Excel and when to run them
from within Microstation
Controlling Microstation from within Excel
783
784 I Chapter 37: Accessing Data from External Applications I
Sub RunInternetExplorer( 1
Dim myInet As Object
Set myInet = CreateObject("1nternetExplorer.Application")
myInet.Visible = True
myInet.Navigate2 " h t t p : / / w w w . b e n t l e y . c o m "
MsgBox "At Bentley's Website."
myInet.Navigate2 " h t t p : / / w w w . m i c r o s o f t . c o m "
MsgBox "At Microsoft's Website."
End Sub
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,
EARLYBINDING, AND LATEBINDING
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.
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 Applications 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.
3corn-dmi 1041
1025 1042
1028 1054
1031 2052
1033 3076
1037 aPPm mt
Sub RunInternetExplorer()
Dim myInet As Object
Set my I net = C r ea t eO b j ec t ( " I n t e r n e t Exp 1 o re r .App 1 i c a t i on " )
myInet.Visible = T r u e
myInet.Navigate2 "http://www.bentley.com"
MsgBox "At Bentley's Website."
myInet.Navigate2 "http://www.microsoft.com"
I References, Early Binding, and Late Binding I 787
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 mylnet 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 r n e t E x p l o r e r ( )
D i m myInet As I n t e r n e t E x p l o r e r
S e t myInet = C r e a t e o b j e c t ("1nternetExplorer.Application":
myInet.
MsgBox myMSApp.Caption
Sub ConnectToMicroStationBO
D i m myMSAppCon As MicroStationDGN.ApplicationDbjectConnector
D i m myMSApp As MicroStationDGN.Application
S e t myMSAppCon = -
C r e a t eO b j e c t ( " M i c r oS t a t i on DG N .A p p 1 ic a t i on 0 b je c t Con n e ct o r " 1
S e t myMSApp = myMSAppCon.Application
myMSApp.Visible = True
MsgBox myMSApp.Caption
S e t myMSApp = Nothing
S e t myMSAppCon = Nothing
End Sub
Sub ConnectToMicroStationCO
D i m myMSAppCon As New ~
MicroStationDGN.Application0bjectConnector
D i m myMSApp As MicroStationDGN.App1ication
S e t myMSApp = myMSAppCon.Application
myMSApp.Visible = True
MsgBox myMSApp.Caption
S e t myMSApp = Nothing
790 I Chapter 37: Accessing Data from External Applications I
S e t myMSAppCon = Nothing
End Sub
S u b ConnectToMi c r o S t a t i o n D ( 1
D i m myMSAppCon As M i c r o S t a t i o n D G N . A p p l i c a t i o n O b j e c t C o n n e c t o r
D i m myMSApp As M i c r o S t a t i o n D G N . A p p l i c a t i o n
S e t myMSAppCon = New ~
MicroStationDGN.ApplicationDbjectConnector
S e t myMSApp = myMSAppCon.Application
myMSApp.Visible = True
MsgBox myMSApp.Caption
S e t myMSApp = Nothing
S e t myMSAppCon = Nothing
End Sub
S e t myMSAppCon = New M i c r o S t a t i o n D G N . A p p l i c a t i o n O b j e c t C o n n e c t o r
Sub ConnectToMicroStationE()
D i m myMSAppCon As MicroStationDGN.ApplicationObjectConnector
D i m myMSApp As MicroStationDGN.Application
' A t t a c h t o e x i s t i n g o r c r e a t e new i f no e x i s t i n g M i c r o s t a t i o n
On E r r o r Resume N e x t
S e t myMSAppCon = G e t o b j e c t ( , -
"MicroStationDGN.App1 i c a t i o n O b j e c t C o n n e c t o r " )
I f Err.Number = 4 2 9 Then
S e t myMSAppCon = -
CreateObject("MicroStationDGN.Application0bjectConnector")
Err.Cl e a r
End I f
On E r r o r GoTo 0
S e t myMSApp = myMSAppCon.Application
myMSApp.Visible = True
MsgBox myMSApp.Caption
S e t myMSApp = Nothing
S e t myMSAppCon = Nothing
End Sub
End Sub
End Sub
When we declare the variable myMSApp, we do so with the W i t h E v e n t s
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
RUN MACROS
FROM EXCELOR 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.
Is Microstation installed on this computer? If we develop in
MicroStationsVBA 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 External Applications I
in Excel, the code is housed in Excel and it is possible that
Microstation is not even installed.
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
Microstations VBA environment.
l l Are we using G e t o b j e c t or C r e a t e o b j e c t ? 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 GetOb j e c t is used inside Microstations 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 GetOb j e c t returns and the program will either crash
or it uses data other than what the user is expecting. By the way,
when using C r e a t e o b j e c t , we should always set the Visible
property to True so we dont 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.
Lets create a new Excel Workbook. We will then get into Excels VBA
environment by clicking Tools > Macro > Visual Basic Editor.
Next, lets insert a new Module.
(General) (Occlarations)
VBAProlect (Bookl)
McrosoFt Excel Oblectr
Sheet1 (Sheetl)
Sheet2 (Sheet2)
Sheet3 (Sheet3)
ThisWor!&ook
Modules
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 BentleyMicrostation DGN #.# Object Library.
With the Reference in place, it is time to write some simple code that
communicates with Microstation.
Sub XLMSAO
Dim myMSAppCon As MicroStationDGN.ApplicationDbjectConnector
Dim myMSApp As MicroStationDGN.App1ication
Dim myLevel As Level
Dim CurRow As Long
results o f .?iE~!.;!l!i~erra!~.....
.GiBuilding_SiteTerrain
.,...... ..........
running .!jEx~.;!ln4T_e_!ra!nMesh
X LMSA: .?iFrame ....i.........
.BiBuildiniSiteMesh
.,...... ...........
, ........+..........
.4iLinks
.~
!Default i
The Level number and Name are placed into Excels Sheetl:
Now, instead of reading Levels from Microstations ActiveDesignFile we
are going to create Levels based on what is in Excel. Lets create a new
design file in Microstation before we continue.
.. 4IFrame
..
..
.
~.
..
..
..
..
..
..
..
. ...............
..?i!Ij!!B.
. !!eM h Level.. ...
/Links Level
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 XLMSBO
D i m myMSAppCon As M i c r o S t a t i o n D G N . A p p l i c a t i o n 0 b j e c t C o n n e c t o r
D i m myMSApp As M i c r o S t a t i o n D G N . A p p l i c a t i o n
D i m m y L e v e l As L e v e l
D i m CurRow As L o n g
S e t myMSAppCon = Getobject(, -
MicroStationDGN.App1 i c a t i o n O b j e c t C o n n e c t o r )
S e t myMSApp = myMSAppCon.Application
I Controlling Microstation from within Excel I 797
CurRow = 2
While Sheetl.Cells(CurRow, 1) <>
5 Drag and drop Custom Menu Item into the new toolbar we just
created.
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 X LMSA 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, lets 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
something
like this:
I Controlling Microstation from within Excel I 801
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. If we '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 Book7.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 XLMSAO
D i m myMSAppCon As MicroStationDGN.ApplicationObjectConnector
D i m myMSApp As MicroStationDGN.Application
D i m m y L e v e l As L e v e l
D i m CurRow As L o n g
S e t myMSAppCon = Getobject(, ~
Sub X L M S C O
Dim myMSAppCon As MicroStationDGN.ApplicationObjectConnector
Dim myMSApp As MicroStationDGN.Application
Dim myLevel As Level
Dim CurRow As Long
.
804 I Chapter 37: Accessing Data from External Applications I
4 Lets browse to the new .xla file we just created.
7 Now, the Menu Item we added to our custom toolbar is still pointing
to Book7.xls. We want it to run the macro in the Add-In file we just
created. Lets 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
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 XctiveSheet 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 Excels or Microstations 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 GetOb j e c t to get existing instances of the application we
wish to attach to and C r e a t e o b j e c t or New if we want to create a new
instance of the application with which we want to work.
.
38 Writing VB6
Applications
We have written a lot of code in VBA. Now its time to write some code
in Visual Basic 6.
In this Chapter:
B Differences between VBA and VB6
B VB6 Project Basics
Controlling Microstation with VB6
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.
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, .cls, and .frm
807
808 I Chapter 38: Writing VB6 Applications I
files) and a single Project file (.vbp) which brings them all
together.
Microstation VBA projects must be run from within
Microstations VBA environment. Excels 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).
Since VBA Projects are self-contained, changes 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, .cls, .frm) in one project do not negatively
impact any other project.
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.
Since VBA programs must be run from within the host
application, we know the host application is on the users
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.
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 users 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 users machine.
The VBA environment is installed with applications such as
Microstation and Microsoft Excel. VB6 must be purchased and
then installed to use it.
I VB6 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:
L
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.
Projectl (Projectl)
The IDE of VB6 is nearly identical to that of VBA. Here, we can see the
Project, Properties, Toolbox, and User Form.
Lets take a couple of controls
from the Toolbox and place
them on the Form. We will Combo1
The Make Project dialog box displays and here we specify a file location
and a file name.
VB98
VB6
Proiectl
Lets talk about this dialog box. We can see the Project Name is being
shown as Projectl: Lets 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 dont want to start with Sub Main because we
dont 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:
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.
VB6
Project1,vbp
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
froject2.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.
Lets remove Project2 by going to the VB menu and selecting File >
Remove Project2. Now, we will open Project1 again.
I VB6 Project Structure I 815
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. Lets 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 Controlling Microstation with VB6 I 817
P r i v a t e Sub F o r m - L o a d 0
D i m m y M S t a t i o n C As MicroStationDGN.Application0bjectConnector
D i m m y M S t a t i o n As MicroStationDGN.App1ication
D i m m y L e v e l As M i c r o S t a t i o n D G N . L e v e 1
D i m m y C e l l As M i c r o S t a t i o n D G N . C e l 1 I n f o r m a t i o n
D i m myCellInfoEnum As MicroStationDGN.CellInformationEnumerator
S e t myMStationC = Getobject(, -
M i c r o S t a t i o n D G N . A p p 1 icationObjectConnector)
Set myMStation = myMStationC.App1ication
Populate Levels
F o r Each m y L e v e l I n myMStation.ActiveDesignFile. L e v e l s
Combol.AddItem myLevel.Name
Next
Populate Cell s
I f myMStation.IsCel1LibraryAttached = T r u e Then
Set myCellInfoEnum = ~
myMStation.GetCellInformationEnumerator(True, True)
818 I Chapter 38: Writing VB6 Applications I
While myCellInfoEnum.MoveNext
Set myCell = myCellInfoEnum.Current
ComboP.AddItem myCell.Name
myCellInfoEnum.MoveNext
Wend
End If
Frame
End S u b
Level 1
Level 2
Level 3
Level 4
Level 5
Level 6
Level 7
Level 8
Level 9
Level 10
Level 11
Level 12
I Controlling Microstation with VB6 I 821
'General D e c l a r a t i o n s Area
D i m m y M S t a t i o n C As MicroStationDGN.Application0bjectConnector
D i m m y M S t a t i o n As M i c r o S t a t i o n D G N . A p p 1 i c a t i o n
P r i v a t e Sub F o r m - L o a d 0
S e t myMStationC = Getobject(, -
"MicroStationDGN.App1icationObjectConnector")
Set myMStation = myMStationC.App1ication
End Sub
P r i v a t e Sub D r i v e l - C h a n g e 0
Dirl.Path = Drivel.Drive
L i s t l .C1 e a r
End Sub
P r i v a t e Sub D i r l - C h a n g e ( )
F i 1e l . P a t h = D i r l .Path
L i s t l .C1 e a r
End Sub
P r i v a t e Sub F i l e l - C l i c k 0
D i m m y L e v e l As M i c r o S t a t i o n D G N . L e v e 1
D i m myDF As M i c r o S t a t i o n D G N . D e s i g n F i l e
S e t myDF = myMStation.OpenDesignFileForProgram(
Dirl.Path & "\" & Filel.FileName, True)
L i s t l .C1 e a r
F o r Each m y L e v e l I n m y D F . L e v e l s
822 I Chapter 38: Writing VB6 Applications I
L i s t l . A d d I t e m m y L e v e l .Name
Next
myDF.Close
End S u b
Sample Node
Sample Node
Sample Node
Samole Node
General D e c l a r a t i o n s Area
I Controlling Microstation with VB6 I 825
"MicroStationDGN.App1 i c a t i o n O b j e c t C o n n e c t o r " )
Set myMSApp = myMSAppCon.Application
'Models
Set myNode = tvl .Nodes .Add(, , "tvlModel s " , "Models")
For Each myModel In myMSApp.ActiveDesignFile.Mode1s
tvl.Nodes.Add "tvlModels". tvwChild. "modL" & myModel .Name.
myModel .Name
Next
myNode.Sorted = True
' Level s
Set myNode = tvl.Nodes.Add(, , "tvlLevels", "Levels")
For Each myLevel In myMSApp.ActiveDesignFi 1 e. Levels
tvl.Nodes.Add "tvlLevels", tvwChild, "lvl-" & myLevel .Name, -
myLevel .Name
Next
myNode.Sorted = True
End Sub
Private Sub t v l C l i c k 0
RaiseEvent SelectionChanged(tvl.Selected1tem)
End Sub
Function G e t L e v e l O As MicroStationDGN.Leve1
Dim xSplit As Variant
xSpl i t = Spl it(tvl.SelectedItem. Full Path, " \ " I
Select Case UCase(xSplit(0))
Case MODE LS
" "
826 I Chapter 38: Writing VB6 Applications I
Set GetLevel = Nothing
Cas e " LEV E LS "
I f UBound(xSp1it) > 0 Then
Set GetLevel =
myMSApp.ActiveDesignFile.Levels(xSplit(1))
Else
Set GetLevel = Nothing
End I f
End S e l e c t
End F u n c t i o n
F u n c t i o n G e t M o d e l O As M i c r o S t a t i o n D G N . M o d e l R e f e r e n c e
D i m x S p l i t As V a r i a n t
"\")
x S p l i t = S p l it ( t v 1 . S e l e c t e d I t e m . F u l l P a t h ,
S e l e c t Case U C a s e ( x S p l i t ( 0 ) )
Cas e " MO D E LS "
I f UBound(xSp1it) > 0 Then
S e t GetModel =
myMSApp.ActiveDesignFile.Models(xSplit(l1)
Else
S e t GetModel = Nothing
End I f
Cas e " LEV E LS "
S e t GetModel = Nothing
End S e l e c t
End F u n c t i o n
P r i v a t e Sub U s e r C o n t r o l L T e r m i n a t e O
S e t myMSApp = Nothing
S e t myMSAppCon = Nothing
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.
Function GetModelO As
MicroStationDGN.ModelReference
Similar to GetLevel, GetModel returns a Model Object if one is selected
in the Tree View.
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. Sample Node
Sample Node
Next, we are going to close the Sample Node
Sample Node
Control by clicking the Close
button at the top of the window.
I Controlling Microstation with VB6 I 829
Models
Levels
When we run our new Project, the Form is displayed and the Tree View
is available.
At this point, we have created a new ActiveX Control and have added a
new Project that makes use of the Control. Lets 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
P r i v a t e Sub msvba-model t r e e l - S e l e c t i o n C h a n g e d ( -
S e l e c t e d N o d e As MSComctl L i b . Node)
D i m m y L e v e l As M i c r o S t a t i o n D G N . L e v e 1
D i m myModel As M i c r o S t a t i o n D G N . M o d e l R e f e r e n c e
MsgBox S e l e c t e d N o d e . T e x t & v b C r & S e l e c t e d N o d e . F u l l P a t h
S e t myLevel = msvba-model t r e e 1 . G e t L e v e l
If m y L e v e l Is N o t h i n g = F a l s e Then
MsgBox m y L e v e l .Name & v b T a b & m y L e v e l .Number, , LEVEL
End If
= msvba-modeltreel.GetMode1
S e t myModel
If myModel Is N o t h i n g = F a l s e T h e n
MsgBox myModel .Name & vbTab & myModel . D e s c r i p t i o n , , MODEL
End If
End Sub
Now, type:
I Controlling Microstation with VB6 I 831
MSCustornLog Control
MSDTHostCtrl Class
MSDTHostCtrl Class
MSDVDAdrn Class
MSFlexGriMizard Subwizard
Msie Control
MSNCSALog Control
MsnMusicStatusUi Class
MSODBCLog Control
MSREdit Class
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
<F5> button to Run the form.
832 I Chapter 38: Writing 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.
As L o n g ) As L o n g
P r i v a t e D e c l a r e F u n c t i o n GetComputerName L i b " k e r n e 1 3 2 " A l i a s -
" G e t C o m p u t e r N a m e A " ( B y V a l l p B u f f e r As S t r i n g , -
n S i z e As L o n g ) As L o n g
P r i v a t e D e c l a r e F u n c t i o n GetDiskFreeSpace L i b "kerne132" A l i a s -
"GetDiskFreeSpaceA" ( B y V a l 1pRootPathName As S t r i n g , ~
1 p S e c t o r s P e r C l u s t e r As L o n g , -
1 p B y t e s P e r S e c t o r As Long,-
1 p N u m b e r O f F r e e C l u s t e r s As L o n g ,
1 p T o t a l N u m b e r O f C l u s t e r s As L o n g ) As L o n g
P r i v a t e D e c l a r e F u n c t i o n G e t L o g i c a l D r i v e S t r i n g s L i b "kerne132" A l i a s~
" G e t L o g i c a l D r i v e S t r i n g s A " ( B y V a l n B u f f e r L e n g t h As L o n g , -
B y V a l l p B u f f e r As S t r i n g ) As L o n g
P r i v a t e D e c l a r e F u n c t i o n Logonuser L i b "Advapi32" A1 i a s "LogonUserA" ~
( B y V a l 1 p s z U s e r n a m e As S t r i n g , -
B y V a l 1 p s z D o m a i n As S t r i n g , -
B y V a l 1 p s z P a s s w o r d As S t r i n g , -
B y V a l d w L o g o n T y p e As L o n g , -
B y V a l d w L o g o n P r o v i d e r As L o n g ,
p h T o k e n As L o n g ) As L o n g
Private Declare Function ShellExecute L i b "she1132.dll" A l i a s -
"She1 1 E x e c u t e A " -
ByVal l p F i l e As S t r i n g , -
ByVal l p p a r a m e t e r s As S t r i n g ,
ByVal 1 p D i r e c t o r y As S t r i n g ,
ByVal nShowCmd As L o n g ) As Long
C o n s t SM-CXSCREEN = 0
C o n s t SM-CYSCREEN = 1
P r i v a t e Sub CommandlLCl i c k (
D i m myMsVBA As New m s v b a L W i n A P I . S y s t e m
D i m X S As Long
D i m Y S As Long
D i m X D r i v e s As V a r i a n t
D i m I As L o n g
XDrives = myMsVBA.LogicalDrives
MsgBox J o i n ( X D r i v e s , 'I, " 1 , , " D r i v e s On S y s t e m "
I Compiling and Distributing Applications I 837
myMsVBA.DiskFreeSpace(CStr(XDrives(I)), 01, , ~
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.
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.
Lets 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.
API Text Viewer
Dependency File
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 project
files and installed on the users computer.
SETUP1.EXE
SHELLLNK.TLB
ST6UNST.EXE
VB6 Runtime and OLE Automation
VB6STKIT.DLL
5 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).
844 I Chapter 38: Writing VB6 Applications I
6 Next, we supply the title for the Setup package:
8 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 Compiling and Distributing Applications I 845
k
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 Review I 847
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 MicrostationsVBA environment. All of the code
we have created and worked with inside of Microstation VBA can be
portedto VB6 with very little difficulty.
848 I Chapter 38: Writing 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
Microstations VBA environment, we can type
Application.ActiveDesignFile . . . . .
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 .NET2003 and Visual Studio .NET 2005. The .NETfamily of
development products can produce standalone .EXEfiles and can
create DLLfiles that are accessible through COM. However, .NET
cannot be used to create ActiveX Controls. So, f a new Control is
needed in MicroStationS V B A environment, it should be created in
VB6.
39 Using VB.NET
In this Chapter:
An Introduction to the VB.NET Environment
El You can do this in VB.NET!
VBA / VB.NET Cross Reference
Distributing VB.NET Applications
849
850 I Chapter 39: Using VB.NET I
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.
From the Start Page of Visual Studio 2005, we can create or open new
Projects. In addition to the ability to create new projects and open
existing ones, we can see the RSS feed from Microsoft's MSDN Visual
Basic homepage. (We discussed RSS technology in an earlier chapter.)
I VB.NET Introduction I 851
My Templates
2 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.
852 I Chapter 39: Using VB.NET I
3 Double-click on our Project Name in the Solution Explorer and
then select the References tab to display all the references of the
current project.
myMSApp = myMSAppCon.Application
MsgBox(myMSApp.Caption)
myMSApp = Nothing
myMSAppCon = Nothing
End Sub
End Class
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 dont need to use Setwhen 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.
HEIGHT
NORTH
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 ListView 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.
P u b l i c C l a s s Form1
P u b l i c E x e P a t h As S t r i n g
P u b l i c F i x e d H e i g h t As L o n g
P r i v a t e Sub F o r m l L L o a d ( B y V a 1 s e n d e r As S y s t e m . O b j e c t ,
B y V a l e As S y s t e m . E v e n t A r g s ) H a n d l e s MyBase.Load
D i m m y F i l e As S y s t e m . I O . F i l e I n f o
D i m m y F o l d e r As New
System.IO.DirectoryInfo(Application.Executab1ePath)
D i m m y L s t V I As L i s t V i e w I t e m
D i m myImg As Image
D i m myThumb As Image
E x e P a t h = myFolder.Parent.Ful1Name
F o r Each m y F i 1 e I n m y F o l d e r . P a r e n t . G e t F i l e s ( " * . b m p " )
myImg = Image.FromFile(myFile.FullName)
myThumb = -
myImg.GetThumbnailImage(
ImageListl.ImageSize.Width,
ImageListl.ImageSize.Height, -
Nothing, Nothing)
myT humb . T a g = my F i 1 e . Name. ToUppe r . Rep1 a c e ( " . BM P " , " "
ImageListl.Images.Add( -
" " ) ,
my F i 1 e . Name . Rep 1 a c e ( " . bmp " , myT h umb
m y L s t V I = lstvCells.Items.Add(myThumb.Tag,
1 m a g e L i s t l . I m a g e s . C o u n t - 1)
Next
End Sub
I Chapter 39: Using VB.NET I
x S p l i t = Split(TextBoxl.Text, vbCrLf)
F o r Each s t r w e b s i t e I n x S p l i t
I f strWebsite.StartsWith("Website A d d r e s s : " ) = T r u e Then
D i m a As P r o c e s s S t a r t I n f o = New P r o c e s s S t a r t I n f o ( -
Mid(strWebsite, InStr(strWebsite, " 1 + 2))
'I:
Process.Start(a)
End I f
Next
End Sub
P r i v a t e Sub F o r m l L R e s i z e ( B y V a 1 s e n d e r As O b j e c t ,
B y V a l e As S y s t e m . E v e n t A r g s ) H a n d l e s M e . R e s i z e
I f F i x e d H e i g h t = 0 Then
F i x e d H e i g h t = Me. H e i g h t
Else
Me.Height = FixedHeight
End I f
End Sub
End C l a s s
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.
D i m m y C e l l E l e m As MicroStationDGN.CellE1ement
D i m C e l l o r i g i n As M i c r o S t a t i o n D G N . P o i n t 3 d
ProgressBarl.Visib1e = True
ProgressBarl.Value = 10
Try
myMSAppCon = G e t o b j e c t ( , ~
P r o g r e s s B a r l . V a l u e = 20
myMSApp = m y M S A p p C o n . A p p l i c a t i o n
P r o g r e s s B a r l . V a l u e = 40
C e l l o r i g i n = myMSApp.CommandState.LastView.Center
P r o g r e s s B a r l . V a l u e = 60
m y C e l 1 E l em = m y M S A p p . C r e a t e C e l 1 E l e m e n t 3 ( -
lstvCells.SelectedItems(O).Text, C e l l o r i g i n , T r u e )
P r o g r e s s B a r l . V a l u e = 80
myMSApp.ActiveModelReference.AddElement~myCellE1em~
ProgressBarl.Value = 100
C a t c h e x As E x c e p t i o n
S e l e c t Case E r r . N u m b e r
Case 4 2 9 ' M i c r o S t a t i o n n o t s t a r t e d
M s g B o x ( " M i c r o S t a t i o n i s n o t s t a r t e d . " & vbCr & ~
A DGN BROWSERAPPLICATION
Our next application is also a
Windows Application. It is
titled DGN Browser. It makes
use of a TreeView control, a Levels
A-F6-G-BldgExtl
CommandButton, a TextBox, a A-FB-G-BldgMisc
A-G251-G-WallExtl
Folder Browser Dialog, a A-2000-0-Dim
A-2000-G-Anno
NotifyIcon, an Image List, and A-2001 -G-lden
BS1300AElOl-Plan.dgn
BS1300AE201-Elevations.dgn
BS1300AE301-Sections.dgn
Heres what it looks BS1300AE501-Details.dgn
like when BS1300AE701-RCPlan.dgn
BS1300AE9.Atriurn.dgn
Microstation is not BS1300AE9-Core.dgn
running: BS1300AE9-Shell.dgn
BS1300C-9-Site.dgn
BS13OOG1001-Cover.dgn
BS1300G19-3DMaster.dgn
BS13001-9-lnterior.dgn
BS1300S-9.Atriurn.dgn
BS1300S-9-Structural.dgn
BS130W-9-Sign.dgn
I A DGN Browser Application I 863
P r i v a t e Sub D i s p l a y D G N s ( B y V a 1 P a t h I n As S t r i n g )
D i m myDI As 1 O . D i r e c t o r y I n f o
D i m myFI A s 1 O . F i l e I n f o
D i m myNode As T r e e N o d e
D i m F i l e c o u n t e r As L o n g
tvl.Nodes.Clear0
myDI = New IO.DirectoryInfo(PathIn)
I f N o t m y D I . E x i s t s Then
MsgBox("The p a t h " & P a t h I n & " does n o t e x i s t . " ,
MsgBoxStyle.Cri t i c a l
TextBoxl .Text = " "
E x i t Sub
End I f
LastPath = PathIn
ToolTipl.SetToolTip(TextBox1, P a t h I n )
Filecounter = 0
ProgressBarl.Visib1e = True
F o r Each myFI I n r n y D I . G e t F i l e s
Filecounter = Filecounter + 1
ProgressBarl.Value = Filecounter * 10
ProgressBarl.Refresh0
S e l e c t Case r n y F I . E x t e n s i o n . T o U p p e r
Case " . D G N "
myNode = tvl.Nodes.Add(myFI.Name)
rnyNode.Irnage1ndex = 0
rnyNode.SelectedImageIndex = 0
I f M i c r o S t a t i o n E r r o r = F a l s e Then
GetFileComps(myFI.FullNarne, rnyNode)
End I f
End S e l e c t
I f F i l e c o u n t e r = 1 0 Then F i l e c o u n t e r = 0
Next
P r o g r e s s B a r l . V i s i b l e = Fa1 s e
tvl.Sort0
End Sub
P r i v a t e Sub F o r m l L L o a d ( B y V a 1 s e n d e r A s S y s t e m . O b j e c t ,
B y V a l e A s S y s t e r n . E v e n t A r g s ) H a n d l e s MyBase.Load
P r i v a t e Sub G e t F i l e C o m p s ( B y V a 1 F i l e I n A s S t r i n g , -
B y V a l F i l e N o d e As T r e e N o d e )
D i m myLevel As MicroStationDGN.Leve1
D i m myModel A s M i c r o S t a t i o n D G N . M o d e l R e f e r e n c e
I A DGN Browser Application I 865
Mi c r o S t a t i on D G N .Ap p 1 i cat i on 0b j e c t C o n n e c t o r 1
" "
myMSApp = myMSAppCon.Application
Catch
MicroStationError = True
Exit Sub
End Try
Dim DGNFile A s M i c r o S t a t i o n D G N . D e s i g n F i 1 e
Try
DGNFile = myMSApp.OpenDesignFileForProgram( ~
FileIn, True)
Catch
Fi 1 eNode. ForeCol or = Color. Red
Exit Sub
End Try
LevelNode = FileNode.Nodes.Add("Levels")
LevelNode.ImageIndex = 1
LevelNode.SelectedImageIndex = 1
ModelNode = FileNode.Nodes.Add("Models")
ModelNode.ImageIndex = 3
ModelNode.SelectedImageIndex = 3
For Each myLevel In DGNFile.Levels
tmpNode = LevelNode.Nodes.Add(myLeve1.Name)
tmpNode.ImageIndex = 2
tmpNode.SelectedImageIndex = 2
Next
For Each myModel In DGNFile.Models
tmpNode = ModelNode.Nodes.Add(myMode1.Name)
If myModel . Is3D Then
tmpNode.ImageIndex = 4
tmpNode.SelectedImageIndex = 4
Else
tmpNode.ImageIndex = 5
tmpNode.SelectedImageIndex = 5
End If
Next
DGNFile = 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.
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,
I VBA 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:
Substring
Tacharkray
Totower
ToLowerInvariant
ToString
As we can see here, we can use the ToUpper method of a String Object
instead of using U C a s e (see below).
m y S t r i n g . T o U p p e r i/B.NET
ToUpper is a method of the String Object. And how about the String
Objects properties? Yes, there are Properties as well. In VB.NET, we use
the Length property of a String Object instead of using the Len 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. Heres an
example using the FileInfo Objects Open method:
IS
In VB.NET:
MsgBox("This i s a test.")
my F S . C1 o s e ( )
End S u b
S u b ReadFi 1 eB( 1
D i m myFS As New I D . S t r e a m R e a d e r ( ~
"C:\MicroStation VBA\points.txt")
Debug.Print(myFS.ReadToEnd1
my F S . C1 o s e ( )
End S u b
C1 o s e # F F i 1 e
End S u b
"C:\MicroStation VBA\pointsZ.txt")
my FW. W r it e L i n e ( "1.1.0.1" 1
my FW . W r it e L i n e ( "2,2,0,2 " 1
E-mailing in VB.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:
S u b SendMai 1A ( 1
870 I Chapter 39: Using VB.NET I
Dim mySMTP As New System.Net.Mail.SmtpC1ient
mySMTP.Host = "yoursmtpserver.com"
my SM T P . Sen d ( m e@ ben t 1 ey . com , y o u@ ben t 1 ey . co m , -
" " " "
Sub SendMai 1 B ( )
Dim mySMTP As New System.Net.Mail.SmtpClient
Dim myMessage As New System.Net.Mail.MailMessage
Dim myAttachment As New System.Net.Mail.Attachment(
"C : \Mi cr oSta t i o n VBA\poi n ts . txt"
mySMTP.Host = "yoursmtpserver.com"
myMessage.To.Add( "you@bentl ey. com" )
myMessage.From = New -
System.Net.Mail.MailAddress("me@bentley.com"~
myMessage.Subject = "Emailing a n Attachment"
myMessage.Body = "<b>HTML</b> Email is also easy."
myMessage.IsBodyHtm1 = True
myMessage.Attachments.Add(myAttachment)
mySMTP.Send(myMessage)
End Sub
The first example, SendMa i1A shows us that with only three lines of code,
we can send an e-mail. SendMai 1 B does things a little differently.
S e n d M a i l B 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.
C:\NicroStation VBA\BatchProcessing
C:\NicroStation VBA\cd material
C: \NicroStation VBA\docs
C:\NicroStation VBA\Documents
C:\NicroStation VBA\Fonts
C: \NicroStation VBA\ from mark
C:\NicroStation VBA\FTP in VB Dot Net
C:\NicroStation VBA\pics
C:\NicroStation VBA\Source Code
C:\NicroStation VBA\USCS
C:\NicroStation VBA\VB6
C:\NicroStation VBA\FTP in VB Dot Net\CS
C:\NicroStation VBA\FTP in VB Dot Net\VB
C:\NicroStation VBA\FTP in VB Dot Net\VB\FtpSample
C:\NicroStation VBA\FTP in VB Dot Net\VB\FtpSample\bin
C:\NicroStation VBA\FTP in VB Dot Net\VB\FtpSample\obj
C:\NicroStation VBA\FTP in VB Dot Net\VB\FtpSample\obj\Debug
C:\NicroStation VBA\FTP in VB Dot Net\VB\FtpSample\obj\Debug\TempPB
C:\NicroStation VBA\FTP in VB Dot Net\CS\FTPSample
S u b F i n d F i 1 esA( )
Dim myDI A s New ID.DirectoryInfo("C:\MicroStation V B A " )
Dim myFI A s 1O.FileInfo
For E a c h myFI In rnyDI.GetFiles("*.DGN", -
1O.SearchOption.AllDirectories)
Debug.Print(rnyFI.Ful1Name)
Next
End S u b
872 I Chapter 39: Using VB.NET I
In this example, we get all .dgn files beginning in the C:\MicroStution
VBA path and its subdirectories.
C:\BicroStation VB1\BatchPr~cessing\Fil=B.dgn
C: \BicroStation VB1\BatchPr~cessing\BatchD\Pile W. dgn
C: \BicroStation VB1\BatchPr~cessing\BatchD\Pile X.dgn
C: \BicroStation VB1\BatchPr~cessing\BatchD\Pile Y.dgn
C: \BicroStation VB1\BatchPr~cessing\BatchD\Pile 2. dgn
C: \BicroStation VBA\BatchProcessing\BatchC\File U.dgn
C: \BicroStation VBA\BatchProcessing\BatchC\File V.dgn
C: \BicroStation VB1\BatchPr~cessing\BatchB\Pile R. dgn
C: \BicroStation VB1\BatchPr~cessing\BatchB\Pile S. dgn
C: \BicroStation VB1\BatchPr~cessing\BatchB\Pile T. dgn
C: \BicroStation VB1\BatchPr~cessing\Batchli\Pile F. dgn
C: \BicroStation VB1\BatchPr~cessing\Batchli\Pile C. dgn
C: \BicroStation VB1\BatchPr~cessing\Batchli\Pile H.dgn
C: \BicroStation VB1\BatchPr~cessing\Batchli\Pile 2. dgn
C: \BicroStation VB1\BatchPr~cessing\Batchli\Batchli-3\Fil=P. dgn
C: \BicroStation VB1\BatchPr~cessing\Batchli\Batchli-3\Fil=Q. dgn
C: \BicroStation VB1\BatchPr~cessing\Batchli\Batchli-e\Pil=N.dgn
C: \BicroStation VB1\BatchPr~cessing\Batchli\Batchli-e\Pil=N.dgn
C: \BicroStation VB1\BatchPr~cessing\Batchli\Batchli-1\Pil=K.dgn
C: \BicroStation VB1\BatchPr~cessing\Batchli\Batchli-1\Pil=1. dgn
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:
B y V a l n I c o n I n d e x As I n t e g e r ) As I n t e g e r
" F i n d E x e c u t a b l e A " ( B y V a l l p F i l e As S t r i n g , -
B y V a l 1 p D i r e c t o r y As S t r i n g , B y V a l l p R e s u l t As S t r i n g ) As L o n g
Sub S a v e F i 1 eIcons( 1
D i m myDI As New ID.DirectoryInfo("C:\MicroStation VBA")
D i m myFI As 1 O . F i l e I n f o
D i m I c o n F i l e As 1 O . F i l e I n f o
D i m m y I c o n As B i t m a p
874 I Chapter 39: Using VB.NET I
Dim tmpImage As Image
Dim myPictureBox As New PictureBox
For Each myFI In myDI.GetFiles
IconFile = New IO.FileInfo(myDI.FullName & " \ " & -
my F I . Ex t e n s i on . Rep 1 a c e ( . ,
" " ) &
" " .bmp )
" "
System.Drawing.Imaging.1mageFormat.Bmp)
Else
'Now look at file
myIcon = IconFromFile(myFI.Ful1Name)
If myIcon Is Nothing = False Then
my I con. Save ( c : \Mi c r oS t a t i on VBA\ &
" " ~
System.Drawing.Imaging.1mageFormat.Bmp)
Else
'Use Default Windows Icon
myIcon = G e t D e f W i n I c o n O
my I con. Save ( c : \Mi c r oS t a t i on VBA\ &
" " ~
my F I . Exten s i on. Repl ace ( . , " " & . bmp " "
" " 9 -
System.Drawing.Imaging.1mageFormat.Bmp)
End If
End If
If myIcon Is Nothing = False Then
myPictureBox.1mage = myIcon
tmpImage = myPictureBox.Image
tmp I ma g e . Sa v e ( C : \Mi c ro St a t i on V BA \ &
" " ~
End If
End If
Next
End Sub
Now, for the Functions IconFromExtension, IconFromFi 1 e, and
GetDefWi nIcon. Each of these functions use another function,
I conFromVa1 ue , which parses the results of the Defaul t I con value when it
is retrieved from the Registry.
I f MyKey I s N o t h i n g Then
Return Nothing
E x i t Function
End I f
' F i r s t l o o k f o r Value
DefValue = MyKey.GetValue("Defau1 t I c o n " )
I f D e f V a l u e I s N o t h i n g = F a l s e Then
Return IconFromValue(DefVa1ue)
E x i t Function
End I f
' N e x t l o o k f o r Key
D e f Va 1 u e = My Key. G e t Va 1 u e ( " "
I f D e f V a l u e I s N o t h i n g = F a l s e Then
M y D e f a u l t K e y = myCR.OpenSubKey(DefValue)
I f M y D e f a u l t K e y I s N o t h i n g = F a l s e Then
' L o o k f o r Key
my De f a u 1 t Ic o n = My D e f a u 1 t Key. Open S u b Key ( " D e f a u 1 t Ic o n " 1
I f m y D e f a u l t I c o n I s N o t h i n g = F a l s e Then
Return ~
F u n c t i o n I c o n F r o m F i l e ( B y V a 1 F i l e I n A s S t r i n g ) As B i t m a p
D i m myDI A s 1 O . D i r e c t o r y I n f o
D i m myFI A s New I O . F i l e I n f o ( F i l e I n )
D i m myExe A s S t r i n g
D i m n I c o n A s Long
D i m t m p B i t m a p As B i t m a p
myDI = New IO.DirectoryInfo(myF1.DirectoryName)
myExe = S p a c e ( 2 5 5 )
FindExecutable(myFI.Name, m y D I . F u l l N a m e , myExe)
nIcon = ExtractIcon(0, myExe.Substring(0, ~
m y E x e . I n d e x O f ( C h r ( O ) ) ) , 0)
I f n I c o n > 0 Then
tmpBitmap = Bitmap.FromHicon(nIcon)
Return tmpBitmap
Else
Return Nothing
End I f
End F u n c t i o n
F u n c t i o n G e t D e f W i n I c o n O As B i t m a p
876 I Chapter 39: Using VB.NET I
Dim nIcon As Long
Dim tmpBitmap A s Bitmap
nIcon = ExtractIcon(0, "C:\Windows\System32\shell32.dll", 0)
tmpBitmap = Bitmap.FromHicon(nIcon)
Return tmpBi tmap
End Function
Function IconFromValue(ByVa1 ValueIn As String) As Bitmap
Dim x S p l i t 0 As String
Dim tmpBitmap A s Bitmap
Dim nIcon As Long
tmpBitmap = Nothing
xSpl i t = Spl i t(Va1 ueIn, " , " )
If xSplit(0) = " "Then
Return Nothing
Exit Function
End If
Select Case xSpl it. Length
Case 1 '.ico file
If xSpl i t ( 0). Conta i n s ( " % " ) = Fa 1 se Then
If xSplit(0) <> "I' Then
tmpBitmap = Bitmap.FromFile(xSplit(0))
End If
End If
Case 2 '.exe or .dll file
nIcon = ExtractIcon(0, xSplit(O), xSplit(1))
If nIcon > 0 Then
tmpBitmap = Bitmap.FromHicon(nIcon)
End If
Case Else
Return Nothing
End Select
Return tmpBi tmap
End Function
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 877
DISTRIBUTING
VB.NET APPLICATIONS
Distributing a VB.NET Application is a matter of a few mouse clicks
because it is built into VB.NET.
ConfigurationManager..?
2 In our example, we w
ill create a Setup that is to be run from CD-
ROM or DVD-ROM.
.
I Distributing VB.NET Applications I 879
Ready to Publish!
The wizard will now publish the application bared on your choices,
3 Clicking Finish causes the Publish Wizard to create the setup file.
It 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.
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 Getobject. 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 Microstations VBA environment, it is a good
idea to become familiar with the .NET world.
Additional Sources
VBA RESOURCES
GENERAL
http://msdn.microsoft.com/vba
VBA Overview, Whitepapers, etc.
10
http://bentleyinstitute.bentley.com/catalo-.aspx?discipline=
Programming Classes offered by Bentley
Mastering Microsoft VBA; ISBN: 0782144365
VBA Developers Handbook; ISBN: 0782 129781
Google, Yahoo, or other Internet Search for VBA or Visual Basic for
Applications
http://discussion.bentley.com
Look for the bentley.microstation.v8.vba discussion group.
VB and VBA in a Nutshell; ISBN: 1565923588
SQL STATEMENTS
sa-
http://msdn.microsoft.com/library/en-us/tsqlref/ts
ses 9sfo.asp?frame=true
SQL Statement Explanations and Examples from Microsoft
881
882 I Additional Sources I
VB.NET
http://msdn.microsoft.com/vbasic/
Visual Basic 2005 Programmer's Reference (Programmer to
Programmer); ISBN: 0764571982
MATHCAD
www.mathcad.com
XML
XML in a Nutshell, Third Edition, ISBN: 0596007647
XML Programming Bible, ISBN: 0764538292
INDEX
Symbols
alignment 362
buttons 367368
creating 735
horizontal 365368
vertical 368370
AlignSelected method 366368
alphabetizing 87
ampersand (&) 100
API Calls 649651
API Types 650651
apostrophe (') 6
Append procedure 131 483
Application, MicroStation 73
Application Object 4244 188189 191
193 784
list of properties and methods in 194222
in Mathcad 769770
ApplicationObjectConnector 222
applications
building in VB.NET 854855
communicating with other 784
compiling in VB6 838
compiling Microsoft Visual Studio 2005 854
compiling VB6 837838
distributing VB6 839
distributing VBA 501503
distributing VB.NET 877
ArcElement 7576
arcs, creating 301
ArcTangent 110
arrays 60 77
changing the size of 416
dynamic 371372 447
returning, in a function code 60
storing file extensions to 446
ASC function 9698
ASCII Text files 19 96
accessing, in VB.NET 868
batch processing in 599603
exporting tags using 580584
reading, using File System Object 676677
reading and writing to 130131
tab-delimited 481483
asterisk symbol (*) 105106
use in SQL 736
Atn (ArcTangent) function 110
AttachmentLog.txt 523
AttachmentModified event 525
attachments 519526
detaching of 524
displaying 381382 386
e-mail 620621 680681
list of object properties and methods for 521522
list of properties and methods in 222227
Auto List Members 190
autocomplete 154
Auto-Load 11 494 647
Auto-Run 494495
buttons
alignment 366368
Browse 384
Cancel 171 443
Change Current Selection 357358
Close 358359
Command 164
creating 798802
Draw In MicroStation 737738
Pick 172 173
Run Macro 4
seed file selection 442
Select 356357
Spin 167
Step Into 12
Toggle 163
unassigned cursor 337
ByRef (By Reference) 64 68
ByVal (By Value) 64 65 68
classes 52 67 784
see also objects
adding 524
default file name of 394
implementing 5253
terminating declared 396
Cleanup event 399
Clear 17 161 732
click events 29
CommandButton 836837
CLng function 111114
Close and return to MicroStation
function 17
Close Button 358359
CloseMode parameter 360361
.cls files 16
clsImageInsertion 389390
clsLineElem 474477
clsModelEvents code 527529
clsStandCheckA code 626627
adding to Standards Checker 627628
clsTimeTrack 491493
cmdCancel_Click 171
code 18 5254 516
controlling execution of 26 135
repeating 137138
use of, within VBA Project Manager 4 6
viewing 2930
Code Modules 30 45 51
516
controls (Cont.)
TreeView 822826
use of frame with 164
ControlTipText property 158 360
converting
strings to numbers 111
supplied parameters to doubles 112
coordinates 351
CopyFileToZipFile procedure 508509
Cos (cosine) function 107108
counting
characters 9091
elements 374
Create button 13
Create3dLines 291294
CreateArcElement 301303
CreateCellElement 305307
CreateCircle 298300
CreateDatabaseLink 564565
CreateDesignFile 307309
CreateDialog 459
CreateEllipseElement 297298 300301
CreateLineElement 289295
CreateObject 691 789 790
CreatePolygon 296297
CreateShapeElement 295296
CreateTextElement 303304
cursor buttons, unassigned 337
cursor movement 426
cursor type constants 725
enumerations (Cont.)
MsdCopyViewPort 249
MsdDatabaseLinkage 249
MsdDataEntryRegionJustification 249
MsdDesignFile Format 250
MsdDevelopableElementOutputType 250
MsdDialogBoxResult 250
MsdDimAccuracy 250251
MsdDimAlignment 251
MsdDimAlternateThresholdCompanion 251
MsdDimAngleMeasure 251
MsdDimBallAndChainAlignment 251
MsdDimBallAndChainChainType 252
MsdDimCustomSymbol 252
MsdDimDMSPrecisionMode 252
MsdDimLabelLineFormat 252
MsdDimMLNoteFrameType 252
MsdDimMLNoteJustification 252
MsdDimNoteHorizontalAttachment 252253
MsdDimNoteLeaderType 253
MsdDimNoteTextRotation 253
MsdDimNoteVerticalAttachment 253
MsdDimNoteVerticalJustification 253
MsdDimPlacementTextPosition 253
MsdDimRadialMode 253
MsdDimStackedFractionalAlignment 254
MsdDimStackedFractionType 254
MsdDimStyleProp 254260
MsdDimSuperscriptMode 260
MsdDimSymbolType 260
enumerations (Cont.)
MsdDimTerminatorArrowhead 260
MsdDimTerminatorMode 260
MsdDimTerminatorType 260261
MsdDimTextField 261
MsdDimTextFormat 261
MsdDimTextFrameType 261
MsdDimTextJustification 261
MsdDimTextLocation 261
MsdDimTextOrientation 262
MsdDimThousandOpts 262
MsdDimToleranceType 262
MsdDimType 262263
MsdDimValueAngleFormat 263
MsdDimValueAnglePrecision 263
MsdDimVerticalTextOptions 263
MsdDrawingMode 263
MsdElementCachePurpose 263
MsdElementClass 263264
MsdElementSubtype 264
MsdElementType 264265
msdElementType 312313
MsdError 265271
MsdFileAccessMode 271
MsdFillMode 271
MsdFontType 271
MsdGeoReferenceSisterFileType 271
MsdGlobalLineStyleScale 271
MsdLevelChangeType 271272
MsdLevelElementAccess 272
enumerations (Cont.)
MsdLimits 272
MsdMeasurementBase 272
MsdMeasurementSystem 272
MsdMemberTraverseType 272
MsdMessageCenterPriority 273
MsdModelChangeType 273
MsdModelType 273
MsdNestOverrides 273
MsdNewLevelDisplay 274
MsdRasterBlockType 274
MsdRasterDisplayOrderCommand 274
MsdRasterDisplayPriorityPlane 274
MsdRasterModificationType 274
MsdRasterWorldFile 274
MsdReferenceSystem 274275
MsdRenderingMode 275
MsdStandardsCheckerReplaceChoice 275
MsdStandardsCheckerReplaceOptions 275
MsdStatusBarArea 275
MsdTagType 275
MsdTangentElementOutputType 276
MsdTangentInterpolationType 276
MsdTextDirection 276
MsdTextJustification 276
MsdTextNodeLineSpacingType 276
MsdV7Action 276
MsdViews 277
MsdXDatumType 277
events (Cont.)
DataPoint 407408
Before Detach 524 526
Dynamics 399404 407408 426427
ElementChanged 539540
Form Load 817
IAttachmentEvents Interface 519
ILevelChangeEvents Interface 531532
ILocateCommandEvents Interface 396397
IModelActivate 527528
IModelChange 527530
Initialize 183184
IPrimitiveCommandEvents 392393 406407 420
ISaveAsEvents Interface 510
ISaveAsEvents_BeforeRemap 510511
IViewUpdateEvents_AfterRedraw 493
KeyDown 160
Keyin 416
KeyPress 96 155 160
169170 364365
KeyUp 160
LineAdded 468469
LocateFailed 399
LocateFilter 398 400
LocateReset 399
ModelChange 527530
MouseDown 155
MouseMove 359
OnDesignFileClosed 286 493 507
509510
events (Cont.)
OnDesignFileOpened 286 493 506
QueryClose 360361
RoomLabelCheck 635638
RunCheck 634
Scroll 166
Show 287
Start 399
UserFormInitialize 168169 360
examples folder 39
Excel 437 793794
Add-In files 803805
connecting to 689690
customizing 798
as databases 749754
MicroStation 793795
opening 753
starting or creating new instance of 691
XML file exported from 593594
Excel Function RAND 704
Excel.Application Object 692
.exe files 815
EXE files 839
Execute method 719
execution of code
breaking 91
immediate 2627
Exit Do statement 139 364
Exit function statement 139
Exit Sub statement 139
exponents 106
export file 16
exporting
field names 735
tag information 580584
user forms to a design file 377379
ExtCount 447448
Extended Property 749754
Extensible MarkupLanguage. see
XML
ExtractIcon 873
GetSystemMetrics 658659
GetTags 573
GetThreeVals 65
GetTickCount 659660
Getting All Files 871872
GetType functionality 544547
GetUserName 660
GetValue 759760 772773
GetVersionEx 653655
GetWindows Directory 660661
graphical elements 289
Graphical User Interfaces (GUIs) 7 49 96
grid 20
group collection method 324325
grouping 474478
groups, creating named 323
hardcoding 47 351
headers 178
height property 157
help files
Bentley website 4142 44
Object Browser 190191
Visual Basic 21 34
help menus 21 30 34
High Security Mode 502503
.htm files, creating 582584
Hungarian notation 79
Join function 95
macros 35 20
recording 348351
running 793794
running a MVBA 5
running Excel 797805
using Mathcad 757759
Macros dialog box 12
Make Project dialog box (V6) 811
Make Project Group 837838
mapping standards 485488
Match Properties Form 362
Mathcad
advantages of using 772
communicating with 763766
overview 755756
Mathcad Object Model 769770
MathInterface property 767
MathRegion 766
mcMathRegion 765766
MDL Applications 387
mdlDialog_fileCreate Function 439440
mdlDialog_fileCreateFromSeed
Function 441442
mdlDialog_fileOpen function 432434 453
mdlDialog_openAlert Function 443
mdlDialog_openInfoBox Function 443444
methods (Cont.)
Pages.Count 165
ScanCriteria collection 316322
Show 183184
ShowError 332
ToUpper 867
Update 729730
Microsoft Access 556557
Microsoft ADO Ext. 2.8 for DDL
and Security Reference 554
Microsoft CDO for Windows 2000
Library 680681
Microsoft Excel #.# Object Library 805
Microsoft Internet Controls
Reference 617 787
Microsoft OLE DB Simple Provider 740
Microsoft Package and Deployment
Wizard 841847
Microsoft Scripting Runtime 385 484 576
606 674
Microsoft Shell Controls and
Automation 605 606 671
Microsoft Visual Basic Help 21 34
MicrosoftSpeechObjectLibrary 679
MicroStation 18
MicroStation
attaching to project in Visual Studio 2005 852853
controlling with VB6 816
creating new instances of 790791
Excel versus 793795
Options 20
Order By statement (SQL) 732733
Order of Operations 116
Outdent 17
Output 483
overloaded methods 867868
PCE_TestLine 420424
personalizing help files 38
pFileExts 447450
Pi function 59
Pick button 172 173 363
PictureBox control 873
pipe symbol (|) 729
Place Block 341343 350
Place Line command 341343
PlaySound function 662663
plus symbol (+) 104
Point List Reader 174177
Point3d type 279 283 463
PointElems array 292
points
capturing 337
user selection of 363364 414416
PointsByLine function 341344
PointsByRectangle function 345346
PointString 416 417
PolarPoint function 6263
polygons, creating 296 412414
Poly Test 412414
PopulateFileList 384385
populating
a collection with levels 474
ComboBoxes 817818
porting 847848
Preserve (keyword) 416
Print 17
Private (keyword) 66
Private Sub Form1_Load 859
Private Sub Form1_Resize 860
Private Sub Linklabel_LinkClicked 860
Private Sub lstvCells_DoubleClick 860
Private Sub
1stvCells_ItemSelectionChanged 859860
problem report 645647
Procedure 19
procedures 6 55 5758
66
processing 606608
processor time, maximizing 426429
project, creating a VBA 4647
Project Explorer 18
Project Explorer 23
Project Groups in VB6 827828 837838
project manager 3
projects
creating in Microsoft Visual Studio 851
distributing 500
protecting 498499 811
un-signed 502503
prompt area 330
properties 20 152
application object 189
Custom file 686687
implementing, for classes 52
viewing, of variables 192
writing 682683
Record Macro 11
RecordCount property 726
records, adding new 737
records, database 565566
Recordsets 724 731
rectangles, drawing 410412 523
recursive execution 606608 675
Redo 17
References 20 500501 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 681682
Mathcad 756
Microsoft ADO Ext. 2.8 for DDL and
Security 554
Microsoft Internet Controls 617 787
Region Objects 766767 773
Regions, in Mathcad 765767
registry editor 382
RegSvr32.exe 830831
relative coordinates 351
Release folder 854
remainder value 114
Remove 17
Save button 22
SaveAs functionality 510517
SaveSetting function 128
saving
class modules into VBA Project 461462
projects with CreateDialog method 459
ScanCriteria collection method 316322
scope 6668
Scroll Bars 166
Scroll event 166
search and replace function 9294
search tab 37
searching 311
folders for .dgn files 382
sub folders for .dgn files 382
security issues 309 502503
Seed file 441442
Select All 17
select button 356
Select case statement 118119 314
error handling 142147
Select statement (SQL) 731732
selecting folders 382384
selection sets, working with 332
selectNodes 591592
SendCommand 348
SendDragPoints 341
sending files to CD writer 512513
settings
retrieving 382 383
saving to Registry 383
storing 382
Settings dialog box 630
SetUp program 841846
SetValue call 764
Sgn function 115
shapes, creating 295
sharing, handling folder 495496
Shell32 BrowseForFolder 672674
ShellExecute 664665
SHGetFilelnfo 665
shortcuts 844
Show method 183184
ShowCommand 339340
ShowError method 332
ShowEvents procedure 287
ShowPrompt 339340 359360
ShowTempMessage 331
Sin (sine) function 107108
Sleep function 656
SND_ASYNC flag 663664
sorting
bubble 8890 372373 377
in SQL 732733
text 8790
sounds 663664
Space function 435
spaces, removing 86
storing (Cont.)
point selections 366
settings 387 632
StrComp function 8788
StrConv function 85
string data type 72 83
string type variables 3233 70
strings
buffered 652 653
concatenate strings 100
counting character 9091
in VB.NET 866867
String Value in Mathcad 761
Structured Storage Documents 681682
Style property 169
SUB (keyword) 55
subfolders 870871
subtraction 105
Summary Properties 682684
Tab Order 18
Tab strips 164
tab width 20
tab-delimited files 483
TabIndex property 157
tablename field 552
tables, creating 555563
TabStop property 157
Tag property 158
tags 571578
extracting information 707711
TagSetName 573
Tagsets 575576
TakeFocusOn Click property 164
Tan (tangent) function 107110
temporary message 331
temporary storage location 513
terminology 32
Test Connection message box 560561
TestCadInputN 352
testing
ActiveX DLL 836
clsTimeTrack 493
default path 451452
DrawLinePerp method 467
file extensions 447450
startpoint and endpoint 464
TestMatchProperties 361
text alignment 362
text boxes 3738 364
text elements
capitalizing 397398 400401
comparing 87
creating 303
determining number of 369370
text files 9596
TextBox control 158159
TextElement Object 76
TheAttachment parameter 521
Thumb 166
time, tracking 490493
Timer function 124
Title parameter 120
Toggle button 163
ToMiles function 107108
Tool Tip 32
Toolbars 18
toolbars 2223 798
Toolbox 18 840
controls in 27 152 668
Tools Menu 20
Top property 156
ToUpper method 867
tracking 615
transaction logs 619620
translating standards 485487
traversing a folder and subfolder 870871
Trim function 86
Try function 861
.txt files 445
Type Libraries 20 785
types 279283
API 650651
available in MicroStation VBA 281282
MicroStation-specific variable 73
returning 6263
standard VBA variable 7073
UCase function 84
UDL files 557560 563 715719
connecting to Excel file 750751
opening a connection using 722723
RSS and 741
UnassignedCB input 337
underscore character (_) 60
Undo 17
Unload Me 171
un-signed projects 502503
unsupported levels procedures,
finding 480482
Update method 729730
Upper case function 84
user feedback 375376
providing 379380
User forms 49 860
exporting to a new design file 377379
VB6 812
user input, getting 334337
user interface exercises 167174
user interfaces 566567 608613
UserForm 19
code 286
Initialize event 168169 360
toolbar 23
users, capturing current 287
VB6
differences between VBA and 807808
MicroStation 816
structure of projects 809
VB6 Virtual Machine file 839
VBA
differences between VB6 and 807808
overview 15
string functions in 84
variable types in 7073
windows 23
VBA editor 12
VBA Files From Levels form 380381
VBA IDE 1530
VBA Project Manager 35 7 913
elements of 10
VBAPM 35
vbCr constant 100
VB.NET 850
vbProperCase 85
vbTab constant 101
vertical order 369371
View Code 2930
View menu 18
visible property 157 164
Visual SQL Query Builder 557 562
Visual Studio 2005 850
voice capabilities 679
Watch Window 18
watch window 25 130
watches, adding 192
WeekDayName function 8586
WeekDayNumber function 8586
Where statement (SQL) 732
While ... Wend statement 137138
Width property 157
window menu 21
Windows API Calls 872
Windows API functions 45 382384 649650
Windows Registry 615616 632633 872
Windows Status Notification Area 863
WithEvents (keyword) 286 506 792793
WithEvents form 469
Wordwrap property 159
workbooks 692693
worksheets 692694 701702
wrappers 832
Write Out File 177178
Write-Only property 449
writing files
ASCII 130132
using File System Object 676677
from the Windows Registry 615616