microstationv8iss1vbaforadvancedusers trn014750 1 0001
DESCRIPTION
microstationTRANSCRIPT
TRN014750-1/0001
MicroStation VBA for Advanced UsersMicroStation V8i (SELECTseries 1)
Bentley Institute Course Guide
Trademarks
AccuDraw, Bentley, the “B” Bentley logo, MDL, MicroStation and SmartLine are registered trademarks; PopSet and Raster Manager are trademarks; Bentley SELECT is a service mark of Bentley Systems, Incorporated or Bentley Software, Inc.
AutoCAD is a registered trademark of Autodesk, Inc.
All ther brands and product names are the trademarks of their respective owners.
Patents
United States Patent Nos. 5,8.15,415 and 5,784,068 and 6,199,125.
Copyrights
©2000‐2009 Bentley Systems, Incorporated. MicroStation ©1998 Bentley Systems, Incorporated. All rights reserved.
MicroStation VBA for Advanced Users ii Dec-09Copyright © 2009 Bentley Systems, Incorporated
Dec-09
Table of Contents
Course Overview ____________________________________ 1Course Description ____________________________________1Target Audience_______________________________________1Prerequisites _________________________________________1Course Objectives _____________________________________2Modules Included _____________________________________2
Building Commands _________________________________ 3Module Overview _____________________________________3Module Prerequisites __________________________________3Module Objectives_____________________________________4Introductory Knowledge ________________________________4
Questions ________________________________________4Answers__________________________________________4
Adding Elements – PrimitiveCommands____________________4Modifying Elements – LocateCommands ___________________7Module Review _______________________________________9
Questions ________________________________________10
The User Interface ___________________________________ 11Module Overview _____________________________________11Module Prerequisites __________________________________11Module Objectives_____________________________________11Introductory Knowledge ________________________________11
Questions ________________________________________12Answers__________________________________________12
Building a User Interface ________________________________12Module Review _______________________________________15
Questions ________________________________________15
Working With Non‐Graphic Data _______________________ 17Module Overview _____________________________________17Module Prerequisites __________________________________17Module Objectives_____________________________________17Introductory Knowledge ________________________________17
iii Table of ContentsCopyright © 2009 Bentley Systems, Incorporated
Dec-09
Table of Contents
Questions ________________________________________18Answers__________________________________________18
Storing Non‐Graphic Information With Elements_____________18Tags ________________________________________________19Databases ___________________________________________21
Connecting _______________________________________21Elements with database records ______________________24
XML ________________________________________________27UserData ____________________________________________27Xdata _______________________________________________28Module Review _______________________________________32
Questions ________________________________________32
Extending Functionality ______________________________ 33Module Overview _____________________________________33Module Prerequisites __________________________________33Module Objectives_____________________________________33Introductory Knowledge ________________________________33
Questions ________________________________________33Answers__________________________________________34
CExpression Processor__________________________________34Calling C and MDL Functions _____________________________36
Using MDL functions in VBA classes ____________________38Helpful notes______________________________________52
Module Review _______________________________________55Questions ________________________________________55
Events ____________________________________________ 57Module Overview _____________________________________57Module Prerequisites __________________________________57Module Objectives_____________________________________57Introductory Knowledge ________________________________58
Questions ________________________________________58Answers__________________________________________58
Types of Events _______________________________________58File events ________________________________________59Interfaces ________________________________________59
Module Review _______________________________________62Questions ________________________________________62
Standards Checker Extensions _________________________ 63Module Overview _____________________________________63Module Prerequisites __________________________________63Module Objectives_____________________________________63Introductory Knowledge ________________________________63
iv Table of ContentsCopyright © 2009 Bentley Systems, Incorporated
Dec-09
Table of Contents
Questions ________________________________________64Answers__________________________________________64
Implementation_______________________________________64Simple – use the basic framework _____________________65Advanced – use the Fixes optional dialog________________66
Building Custom Standards Checker Applications ____________66Questions ________________________________________76
Module Review Answers _____________________________ 77Building Commands____________________________________77
Questions ________________________________________77Answers__________________________________________77
User Interface ________________________________________78Questions ________________________________________78Answers__________________________________________78
Working With Non‐Graphic Data _________________________79Questions ________________________________________79Answers__________________________________________79
Extending Functionality_________________________________79Questions ________________________________________79Answers__________________________________________80
Events ______________________________________________80Questions ________________________________________80Answers__________________________________________80
Standards Checker Extensions____________________________81Questions ________________________________________81Answers__________________________________________81
Appendix __________________________________________ 83New to MicroStation VBA? ______________________________83
The MicroStationDGN Object Model ___________________83System Overview______________________________________84
Configuration Variables for VBA_______________________84Keyins for VBA_____________________________________85
COM Client Applications ________________________________86ActiveX Controls and DLLs _______________________________87Interface Programming _________________________________88Sample Macros _______________________________________88
Scanning _________________________________________88Geometry ________________________________________89Working with other files _____________________________96Dynamic user interface______________________________97
v Table of ContentsCopyright © 2009 Bentley Systems, Incorporated
Course Overview
Course Description
This course serves as an introduction to MicroStation VBA for application developers with working knowledge of Visual Basic/VBA and as a follow‐on to the MicroStation VBA Essentials course for those with prior experience with MicroStation VBA.
Regardless of a student’s background, this course provides an essential foundation for the design, development, and deployment of MicroStation VBA macros automating fundamental operations in MicroStation.
Target Audience
The primary audience for this course is Application Developers — analysts and programmers. This course may also be valuable for the following audience(s):
• Administrators
• Consultants
• Managers
• Planners
Prerequisites
• Have either a working knowledge of Visual Basic/VBA or successfully completed the MicroStation VBA Essentials course. If the former, you should review the section “New to MicroStation VBA?” in the Appendix to this course guide before proceeding with this course.
• Familiarity with basic MicroStation concepts such as design files, models, references, and levels.
Dec-09 1 Course Overview
Copyright © 2009 Bentley Systems, Incorporated
Course Objectives
• Familiarity with popular MicroStation tools and view controls.
• Basic understanding of the MicroStation object model —in particular, the Application, Attachment, DesignFile, Element, and ModelReference objects.
Course Objectives
After completing this course, you will be able to:
• Work with MicroStation's VBA objects to automate fundamental operations in the product.
• Deploy VBA macros in MicroStation.
Modules Included
The following modules are included in this course:
• Building Commands
• User Interface
• Working With Non‐Graphic Data
• Extending Functionality
• Events
• Standards Checker Extensions
Course Overview 2 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Building Commands
Module Overview
One of the basic building blocks of applications in MicroStation is the command structure. VBA opens this up to application developers through interfaces. The two interfaces used for commands are IPrimitiveCommandEvents and ILocateCommandEvents.
The command interfaces allow macros to follow a consistent structure. The structure of the command is designed to work with the state machine format that is built into MicroStation. By developing a collection of command classes, a set of tools can be integrated in the workspace.
The basic structure for the macro is to define a subroutine in a module that will instantiate a class that implements one of the interfaces. The interface will provide the entry points for MicroStation to call, that the class will use to react to user actions. Interface programming is covered in depth in the module “Standards Checker Extensions”.
Module Prerequisites
• Familiarity with the concepts of placing, locating, manipulating and modifying elements in a model and the applicable tools.
Refer to the Course Overview for additional pre‐requisites.
Dec-09 3 Building Commands
Copyright © 2009 Bentley Systems, Incorporated
Module Objectives
Module Objectives
After completing this module, you will be able to:
• Implement commands that add elements to a model.
• Implement commands that interactively find and query or modify elements in a model.
Introductory Knowledge
Before you begin this module, let's define what you already know.
Questions
1 What does a VBA project file contain?
2 In a model scan what kind of object will return only elements that match search criteria?
3 True or False: Hardly any MicroStation menu selections have a corresponding key‐in you can use to directly invoke it.
Answers
1 Macros, including modules, classes, and forms which, in turn, contain subroutines and functions.
2 ElementScanCriteria
3 False. Most MicroStation menu selections and tools have a corresponding key‐in you can use to directly invoke it.
Adding Elements – PrimitiveCommands
The IPrimitiveCommandEvents are typically used in commands that will be adding elements to a model.
Primitive commands will use the data point and reset actions to interact with the user. The interface also defines entry points for the start of the command if key‐
Building Commands 4 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Adding Elements – PrimitiveCommands
ins are to be picked up and used, along with command termination through the cleanup event. The following is the structure of a class that implements the IPrimitiveCommandEvents interface.
Option Explicit
Implements IPrimitiveCommandEvents
Private Sub IPrimitiveCommandEvents_Cleanup()End Sub
Private Sub IPrimitiveCommandEvents_DataPoint(Point As Point3d, _ByVal View As View)
End Sub
Private Sub IPrimitiveCommandEvents_Dynamics(Point As Point3d, _ByVal View As View, ByVal DrawMode As MsdDrawingMode)
End Sub
Private Sub IPrimitiveCommandEvents_Keyin(ByVal Keyin As String)End Sub
Private Sub IPrimitiveCommandEvents_Reset()End Sub
Private Sub IPrimitiveCommandEvents_Start()
End Sub
The IPrimitiveCommandEvents_Start method is called when this command class is invoked. Typically this method will initialize any settings that the command may need to set the initial prompts for the user.
The IPrimitiveCommandEvents_Keyin method is active if the usesKeyins option is True when the command class is instantiated. This will allow the user to type into MicroStation’s key‐in line while this command is active, for the class to use.
The IPrimitiveCommandEvents_Reset method is called when the user presses the Reset button. This is useful when a command wants to step backward.
The IPrimitiveCommandEvents_Cleanup method is called when the command is terminated by another command being started.
The IPrimitiveCommandEvents_Dynamics method is active when the CommandState.startDynamics is called. This method is used to let the command show reaction to mouse movements.
The IPrimitiveCommandEvents_DataPoint method is called when the user enters a data point.
Dec-09 5 Building CommandsCopyright © 2009 Bentley Systems, Incorporated
Adding Elements – PrimitiveCommands
These methods combine to produce the standard workflow used by a command that is adding an element to a model.
In the next exercise a cell is to be built on the fly in the code.
Exercise: Implement a command that will add a cell to a model
1 Launch MicroStation and open any design file.
2 Select Utilities > Macros > Project Manager.
3 Create and load the project Exercise_1.
4 Open the Visual Basic Editor.
5 Insert a class module and rename it Exercise1.
6 Type Implements IPrimitiveCommandEvents.
7 Type the function CreateSimpleGraphic that will be used to create a simple graphic cell.
Function CreateSimpleGraphic(oPoint As Point3d) As CellElement
Dim oShape As ShapeElement
Dim oElements(0) As Element
Dim oCell As CellElement
Dim ptsCorners(3) As Point3d
ptsCorners(1).X = 10
ptsCorners(2).X = 10
ptsCorners(2).Y = 10
Building Commands 6 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Modifying Elements – LocateCommands
ptsCorners(3).Y = 10
Set oShape = CreateShapeElement1(Nothing, ptsCorners, msdFillModeFilled)
Set oElements(0) = oShape
Set oCell = CreateCellElement1("Simple", oElements, Point3dZero, False)
Set CreateSimpleGraphic = oCell
End Function
8 Add the methods, including the processing logic, for the IPrimitiveCommandEvents interface.
9 Add logic to the DataPoint method that calls CreateSimpleGraphic and then displays and adds the cell to the model.
10 Switch to Module1, which is a normal module rather than a class module, and add a subroutine named PlaceCell that will serve as an entry point for the macro.
11 In PlaceCell, create a new instance of the class Exercise1, using the line:
CommandState.startPrimitive new Exercise1
12 Run the macro.
Press <F5> in the VBA Editor and return to MicroStation.
Note: Often there is more than one way to create an element. Consider what information you will be gathering from users to create the element, then decide on the method.
Modifying Elements – LocateCommands
The ILocateCommandEvents are typically used to interactively find and query or modify elements.
Locate commands will use the “locate accept and process” algorithm for finding elements. The interface defines entry points for validating the located element and cleaning up after the command. Following is the structure of a class that implements the ILocateCommandEvents interface.
Dec-09 7 Building CommandsCopyright © 2009 Bentley Systems, Incorporated
Modifying Elements – LocateCommands
Option ExplicitImplements ILocateCommandEvents
Private Sub ILocateCommandEvents_Accept(ByVal Element As Element, _Point As Point3d, _ByVal View As View)
End Sub
Private Sub ILocateCommandEvents_Cleanup()End Sub
Private Sub ILocateCommandEvents_Dynamics(Point As Point3d, _ByVal View As View, _ByVal DrawMode As MsdDrawingMode)
End Sub
Private Sub ILocateCommandEvents_LocateFailed()End Sub
Private Sub ILocateCommandEvents_LocateFilter(ByVal Element _As Element, _Point As Point3d, _Accepted As Boolean)
End Sub
Private Sub ILocateCommandEvents_LocateReset()End Sub
Private Sub ILocateCommandEvents_Start()End Sub
The CommandState.createLocateCriteria method can be used to create the locate criteria object that will be used to filter, at a high level, the selectable elements. Once the LocateCriteria is created, use the include/exclude methods to set the features of the object. Then the CommandState.setLocateCriteria is called to put the filter in place.
In the ILocateCommandEvents_LocateFilter method, the macro has the ability to preview the element. If it fails some test, then the element can be rejected.
If the user identifies an element that does not meet the requirements of the locate criteria, the ILocateCommandEvents_LocateFailed method is called. In this method, the macro can notify the user of the error condition.
ILocateCommandEvents_LocateReset is used when an element has been selected, but the user has pressed the Reset button. In this case, many macros can release some of the resources that have accumulated during the locate process.
Building Commands 8 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Module Review
The Cleanup method is called when the command is terminated by starting another command. This can be used to release resources that have been consumed by this command.
The ILocateCommandEvents_Dynamics method can be activated by calling the CommandState.startDynamics method. Dynamics can be used to show actions like moving an element or other incremental changes that are a result of moving the mouse or pointer.
The ILocateCommandEvents_Accept method is called when the user enters a data point confirming the acceptance of the element(s) that have been selected.
In the next exercise you will implement a command that lets the user identify an element and then opens a dialog showing the type of element selected.
Exercise: Implement a locate command
1 Continuing in the project Exercise_1, insert a new class module and rename it Exercise2.
2 Type Implements ILocateCommandEvents.
3 Add the methods, including the processing logic, for the ILocateCommandEvents.
4 Add logic to the Accept method that will get the type and set a MsgBox to display this information.
5 Switch to Module1, and add a new subroutine named GetElementType that will serve as the entry point for the module.
6 In GetElementType, create a new instance of the class Exercise2, using the line:
commandState.startLocate new Exercise2
7 Run the macro.
Exercise: Implement a locate command with specified scan criteria
1 Add a LocateCriteria to the start method in Exercise2.
Module Review
Now that you have completed this module, let’s measure what you have learned.
Dec-09 9 Building CommandsCopyright © 2009 Bentley Systems, Incorporated
Module Review
Questions
1 What are events?
2 What are the events that are available to the IPrimitiveCommandEvents interface?
3 What are the events that are available to the ILocateCommandEvents interface?
4 How does one make use of an interface?
Building Commands 10 Dec-09Copyright © 2009 Bentley Systems, Incorporated
The User Interface
Module Overview
Building dialogs can begin as a simple task, but adding advanced features to enhance the user experience can take quite a bit of work.
The UserForm set that is used in VBA is limited by design. To add features such as resizing and menus, macros have to make use of the Win32 API.
Module Prerequisites
• Familiarity with the concept of locating elements in a model and the applicable tools.
• Familiarity with the native VBA tools available for building a UserForms.
Refer to the Course Overview for additional pre‐requisites.
Module Objectives
After completing this module, you will be able to:
• Build dialog boxes with functionality more advanced than that available through the native VBA UserForm controls.
Introductory Knowledge
Before you begin this module, let's define what you already know.
Dec-09 11 The User Interface
Copyright © 2009 Bentley Systems, Incorporated
Building a User Interface
Questions
1 When creating a Message Box, how do specify its buttons be labeled “Yes” and “No”?
2 Which does the ShowModal property of a UserForm determine?
3 How do you initiate the creation of a UserForm in a VBA project.
{First multiple choice/fill in the blank answer.}
{Second multiple choice/fill in the blank answer.}
{Third multiple choice/fill in the blank answer.}
Answers
1 Specify vbYesNo as the second parameter of the call to the MsgBox function.
2 The ShowModal property determines whether the form is modal.
3 In the Visual Basic Editor, select Insert > UserForm.
Building a User Interface
When building a user interface, a developer must be mindful of how the user thinks. If the user is a visual person, the user interface needs to use graphics to convey a message. Some controls that may be useful are the SpinButton, ScrollBar, and Image Control. The SpinButton and ScrollBar can be used for manipulating the image that is displayed in the Image control. The Image Control can be used to preview design graphics. To do this when working with MicroStation elements, the dialog needs to use the GetPicture method from the element class.
Dynamic contents allow the user interface to reflect the current set of information. Building a macro that has a picture of each cell in an attached cell library is an example of a macro that must create components on the fly. To do this, the macro creates a collection of buttons to add to the dialog, and the dialog must resize so they fit.
TabPages are a method to make sense of large sets of related information. To start with, TabPages are defined in a set of either TabStrip or MultiPage items. The TabStrip displays the same set of controls on multiple pages, or tabs. The
The User Interface 12 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Building a User Interface
MultiPage item displays a different set of controls on multiple pages. Tab pages can be added to the UserForm at runtime.
The information may also be generated while the macro is running. To make pages of the tab set visible, macros need to work with the tabpage.visible property. To make a form resizable the macro needs to add the following declarations.
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _(ByVal lpClassName As String, _ByVal lpWindowName As Any) As Long
Private Declare Function GetWindowLong Lib "user32" Alias _"GetWindowLongA" _(ByVal HWND As Long, _ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias _"SetWindowLongA" _(ByVal HWND As Long, _ByVal nIndex As Long, _ByVal dwNewLong As Long) As Long
Private Const WS_THICKFRAME = &H40000
Private Const GWL_STYLE = (-16)
Then when the form is activated the active method will need to call these functions to change the style of the dialog that is used:
Dim lngFrmHWND As Long
Dim lngStyle As Long
Dim lngRetVal As Long
lngFrmHWND = FindWindow("ThunderDFrame", Me.Caption)
lngStyle = GetWindowLong(lngFrmHWND, GWL_STYLE)
lngRetVal = SetWindowLong(lngFrmHWND, GWL_STYLE, _ lngStyle Or WS_THICKFRAME)
Finally, when the resize event occurs, the macro will need to take care of adjusting the contents of the dialog to the new area. An example follows.
Private Sub UserForm_Resize()
ListBox2.Left = 1
ListBox2.Width = Me.Width - 9
ListBox2.Height = Me.Height / 2 - 5
Dec-09 13 The User InterfaceCopyright © 2009 Bentley Systems, Incorporated
Building a User Interface
Label1.Top = ListBox2.Height + 2
Label1.Height = 5
Label1.Width = ListBox2.Width
ListBox1.Top = ListBox2.Height + Label1.Height + 5
ListBox1.Height = Me.Height / 2 - 10
ListBox1.Left = ListBox2.Left
ListBox1.Width = ListBox2.Width
Debug.Print "Height: " & Me.Height & " Width:" & Me.Width
Debug.Print "the upper list box is "; ListBox2.Height; " tall starts _at "; ListBox2.Top
Debug.Print "the lower list box is "; ListBox1.Height; " tall starts _at "; ListBox1.Top
End Sub
In the following exercise you will build a dialog which can be used by a locate macro to display the identified element.
Exercise: Build the dialog
1 Continuing with the project Exercise_1 from “Building Commands”, insert a userform and make it non‐modal.
2 Add an Image to the form.
3 Add a this method to the UserForm:
Sub DisplayElement(oElement As Element)
Dim stdPic As StdPicture
With PreviewImageArea
Set stdPic = oElement.GetPicture(PointsToPixelsX(.Width), _PointsToPixelsY(.Height), True)
.Picture = stdPic
End With
End Sub
The User Interface 14 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Module Review
4 In the ILocateCommandEvents_Accept method, call the UserForm1.DisplayElement method.
5 Integrate the Image Area into the locate macro by calling the Userform1.show method.
6 In the ILocateCommandEvents_Reset method, hide the form.
7 In the ILocateCommandEvents_Cleanup method, unload the form.
Module Review
Now that you have completed this module, let’s measure what you have learned.
Questions
1 Name at least five standard user interface controls available to VBA developers?
2 How can an Image Control be used in a dialog?
3 What methods must be included to make a form resizable?
Dec-09 15 The User InterfaceCopyright © 2009 Bentley Systems, Incorporated
Working With Non‐Graphic Data
Module Overview
Many applications require that a graphic element be supported by non‐graphic information. MicroStation supports many options to accomplish this and there are pros and cons for each solution. A developer must take these into account when deciding how to store information.
Module Prerequisites
• Understanding of the concept of associating non‐graphic information with graphic elements and the applicable tools.
Refer to the Course Overview for additional pre‐requisites.
Module Objectives
After completing this module, you will be able to:
• Create VBA code to attach and extract tag data.
• Create VBA code to create and maintain database linkages.
• Create VBA code to set and get Xdata.
Introductory Knowledge
Before you begin this module, let's define what you already know.
Dec-09 17 Working With Non-Graphic Data
Copyright © 2009 Bentley Systems, Incorporated
Storing Non-Graphic Information With Elements
Questions
1 What types of non‐graphic information can be associated with a graphic element?
2 What is ODBC?
3 True or False: MicroStation provides a utility to convert data records from an external database, which is linked to the active design file, into tag data.
Answers
1 Tags
Database linkages
UserData
Xdata
XML
2 ODBC is a standard generic interface that allows applications to access SQL compliant databases. MicroStation supports ODBC database linkages among others.
3 True. The utility is the Database to Tag Convertor (DBTOTAG).
Storing Non‐Graphic Information With Elements
Adding non‐graphic information allows for “smart” elements. And since graphics do not always tell the whole story, you can store extra, non‐graphic, information with elements. There are a number of methods for doing this, so the application developer will have to choose the method that works best for their needs.
• If pre‐defined tools will be used or the information is relatively simple, tags are a likely choice.
• If the information needs to be shared outside of MicroStation, then a database is the obvious choice.
• If the information needs to be protected from editing, user data should be used.
• If the information needs to be shared with an AutoCAD application, Xdata is the best choice.
Working With Non-Graphic Data 18 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Tags
Another option is XML, an emerging standard for information interchange that will see more tools and development in MicroStation.
Tags
Tags provide a method to link some fixed structure, non‐graphic, data to an element. The tag process adds an element, which can be optionally displayed, to a model. The information contained in a tag element can be edited with standard MicroStation tools.
Elements must be added to a file before a tag can be added to an element. Tags do not directly modify the element in the model. They keep a reference to the base element so it can be found. The tag itself is defined by a TagSet.
Exercise: Scanning a model for elements with tags.
1 In MicroStation, open the file Database.dgn in the folder ...\WorkSpace\Projects\Examples\General\dgn.
The model Tags contains tagged elements.
2 In the VBA Project Manager dialog, create and load the project Tags.
3 Switch to the Visual Basic Editor, insert a module, and create the following macro:
Sub TagReport()
Dim oScP As ElementScanCriteria
Dim oEnum As ElementEnumerator
Dim oTagDefs As TagDefinitions
Dim oTagSet As TagDefinition
Dim oTag As TagElement
Dim strFileName As String
Dim tmpString As String
Dim outString As String
Dim oModel As ModelReference
strFileName = ActiveDesignFile.FullName
tmpString = Replace(strFileName, ".dgn", ".txt")
Open tmpString For Output As 1
Set oScP = New ElementScanCriteria
oScP.ExcludeNonGraphical
Dec-09 19 Working With Non-Graphic DataCopyright © 2009 Bentley Systems, Incorporated
Tags
For Each oModel In ActiveDesignFile.Models
outString = "*** Scanning model " & oModel.Name & " ***"
Print #1, outString
Set oEnum = oModel.Scan(oScP)
Do While oEnum.MoveNext
Dim oEl As Element
If oEnum.Current.HasAnyTags Then
Dim oTagsArray() As TagElement
Dim i As Long
Set oEl = oEnum.Current
oTagsArray = oEl.GetTags
For i = LBound(oTagsArray) To UBound(oTagsArray)
If (i >= 1) Then
If (oTagsArray(i - 1).TagSetName <> _oTagsArray(i).TagSetName) Then
outString = _"Found Tag Set = " & _oTagsArray(i).TagSetName
Debug.Print outString
Print #1, outString
End If
Else
outString = "Found Tag Set = " & _oTagsArray(i).TagSetName
Print #1, outString
End If
outString = "--- " & _oTagsArray(i).TagDefinitionName & _" = " & oTagsArray(i).Value
Print #1, outString
Next
Print #1,
End If
Loop
Print #1, "--------------------------"
Next
Close 1
Debug.Print “TagReport processing is complete”
Working With Non-Graphic Data 20 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Databases
End Sub
4 Run the macro TagReport.
The macro creates the text file Database.txt in the same folder as the design file and writes to the text file the TagSetName and the tag data for each element.
5 Open Database.txt in a text editor such as Notepad to review the output.
Exercise: Tag an element
1 Add a tag to a selected element.
There are good examples in the MicroStation VBA help document, Objects, Tag Objects.
Databases
Databases offer a flexible storage system for non‐graphic information. The structure of the information must be determined by the database or application designer. Applications need to maintain the validity of information, since the database may not always be connected.
To work with database information, MicroStation has to connect to the data source first, then work with the information that is stored on an element. The information is kept external to MicroStation so it is available to other non‐MicroStation users through other applications.
Connecting
One of the connection types available to VBA developers is the ADO data connection. The macro needs to create an ADOConnection, then set the connection string. The connection string is the service provider type, the DSN name, and, for some types of service providers — for example, Oracle — the user name or id and the password.
Note: The provider type is the software provider for the database driver, such as Oracle, OLEDB, or MSDASQL.
To complete the next exercise, first make the GIS ODBC data source provided with MicroStation available on the system you are using, as follows:
Dec-09 21 Working With Non-Graphic DataCopyright © 2009 Bentley Systems, Incorporated
Databases
1 In the Windows Control Panel, select Administrative Tools > Data Sources (ODBC).
2 In the ODBC Data Source Administrator dialog, select the User DSN tab (or, if you have administrative rights you can use the System DSN tab) and click Add.
3 In the Create New Data Source dialog, select the Microsoft Access Driver (*.mdb) and click Finish.
4 In the ODBC Microsoft Access Setup dialog, type GIS as the data source Name and, optionally, add a description of your choice.
5 Click Select.
6 Navigate to the ...\WorkSpace\System\macros directory and select gis.mdb. You may need to copy and paste the full path to gis.mdb in the Database File field.
7 Click OK.
8 In the ODBC Microsoft Access Setup dialog, click OK.
9 In the ODBC Data Source Administrator dialog, click OK.
Exercise: Connect to the GIS ODBC data source
1 In the VBA Project Manager dialog, create and load the project ODBC_Datasource.
2 Switch to the Visual Basic Editor and insert a module that contains declarations of variables needed to connect to the data source:
'Global variables for connecting to the database
Public DNSname As String
Public UIDPWD As String
Public ADOconn As ADODB.Connection
3 Insert a UserForm, frmDBConnect, and add to it the following text field and command button objects:
txtDNSName — DSN Name
cmdDBConnect — Connect
cmdCancel — Cancel
4 Double‐click the Connect button object, and type the following code to handle a click of the button:
Private Sub cmdDBConnect_Click()
On Error GoTo RDOCC_EH
Working With Non-Graphic Data 22 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Databases
'accept values from the user
DNSname = txtDNSName.Value
If DNSname <> "" Then
UIDPWD = "Provider=MSDASQL;"
UIDPWD = UIDPWD & "DSN="
UIDPWD = UIDPWD & DNSname
Set ADOconn = New Connection
ADOconn.ConnectionString = UIDPWD
ADOconn.Open
MsgBox ("Opened connection.")
End If
Unload Me
Exit Sub
RDOCC_EH:
MsgBox ("ERROR " & Err.Number & " " & Err.Description)
End Sub
5 Type the following code to handle a click of the Cancel button:
Private Sub cmdCancel_Click()
End
End Sub
6 Select Tools > References.
7 In the References dialog, select Microsoft ActiveX Data Objects 2.5 Library and Microsoft ADO Ext. 2.8 for DDL and Security in addition to the libraries already selected, then click OK.
8 Run the form.
9 In the DSN Name field, type GIS.
10 Click Connect.
Note: In the ...\WorkSpace\System\vba\examples folder, the VBA project files DataBaseExamples.mvba and dbcheck.mvba are for use with an Oracle database. The userform for connecting to the data source is therefore augmented to require a username and password.
Dec-09 23 Working With Non-Graphic DataCopyright © 2009 Bentley Systems, Incorporated
Databases
Elements with database records
When working with graphic elements that are connected to database records, the process to extract the non‐graphic information is more complex. The element only stores a pair of numeric ids.
• The Entity Number, which is related to a database table by looking in the MSCATALOG table.
• The MSLink which is the unique value stored in a column of the table.
The macro must first query the MSCATALOG table to get the tablename from the Entity Number. Once the table name is known, the macro can then query that table based on the MSLink number to get the information from the table. To work with the data, the macro will build a RecordSet that will be used to hold the results of the queries. Some of the queries that you need will appear as follows.
'Get the Table name for a given entity.
Public Function GetTableName(MSCATstr As String, EntNum As Integer) _As String
On Error Resume Next
Dim RecordCount As Integer
Dim RecSet1 As adodb.Recordset
RecordCount = 1
'take this out for access since the USER_TABLES table does not exist!
If dbtype = "Oracle" Then
' Set RecSet1 = GetRecordSet(ADOconn, "SELECT TABLE_NAME FROM
‘ USER_TABLES WHERE TABLE_NAME = '" + MSCATstr + "'")
' RecordCount = RecSet1.RecordCount
End If
If RecordCount < 1 Then
'no entry found for given tablename
MsgBox "MSCATALOG Not Found!"
RecSet1.Close
Set RecSet1 = Nothing
GetTableName = ""
ElseIf RecordCount = 1 Then
RecordCount = 0
Dim RecSet2 As adodb.Recordset
Working With Non-Graphic Data 24 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Databases
Set RecSet2 = GetRecordSet(ADOconn, "SELECT TABLENAME FROM " + _MSCATstr + " WHERE ENTITYNUM = " & CStr(EntNum))
RecordCount = RecSet2.RecordCount
If RecordCount = 0 Then
MsgBox "No Table for Entity: " & CStr(EntNum), vbOKOnly
RecSet2.Close
Set RecSet2 = Nothing
GetTableName = ""
ElseIf RecordCount = 1 Then
GetTableName = RecSet2!TableName
RecSet2.Close
Set RecSet2 = Nothing
Else
MsgBox "Multiple Tables for Entity: " _& CStr(EntNum), vbOKOnly
RecSet2.Close
Set RecSet2 = Nothing
GetTableName = ""
End If
Else
MsgBox "MSCATALOG is not Unique!"
RecSet1.Close
Set RecSet1 = Nothing
GetTableName = ""
End If
End Function
Public Sub PrintLink(LinkIndex As Integer)
'prints database info for a given dblink
Dim mslink As Integer
Dim MSCATstr As String
Dim EntNum As Integer
Dim TableName As String
Dim RS1 As adodb.Recordset
Dim i As Integer
If oElement.HasAnyDatabaseLinks Then
Dec-09 25 Working With Non-Graphic DataCopyright © 2009 Bentley Systems, Incorporated
Databases
'check to see if element has database link
mslink = dbLinks(LinkIndex).mslink
EntNum = dbLinks(LinkIndex).EntityNumber
MSCATstr = GetMSCATALOG
TableName = GetTableName(MSCATstr, EntNum)
If TableName <> "" Then
Set RS1 = GetRecordSet(ADOconn, _"SELECT * FROM " + TableName + " _WHERE MSLINK = " & CStr(mslink))
Dim RecordCount As Integer
RecordCount = RS1.RecordCount
If RecordCount = 0 Then
' no records found with matching mslink number
Debug.Print "No Records Found!!"
ElseIf RecordCount = 1 Then
' one entry found for given mslink number
For i = 0 To RS1.Fields.count - 1
Debug.Print RS1.Fields.Item(i).Name
frmDBLinkInfo.ListBox1.List(i, 1) = "NULL"
Next
Else
' multiple entries found for mslink number
Debug.Print "MSLINK is not Unique!!"
End If
RS1.Close
Set RS1 = Nothing
End If
Else
' element does not have any database links
Debug.Print "Element has no DB Links"
End If
End Sub
Exercise: Obtain database information
1 Create a macro to query an element and get the database information that is related to the element.
Working With Non-Graphic Data 26 Dec-09Copyright © 2009 Bentley Systems, Incorporated
XML
XML
XML is a technology for creating self‐documented structured storage of information. XML can be used to create text files that can be interpreted by any macro that is capable of parsing XML data.
Note: As of this writing, the XML API in VBA is limited to using the Microsoft MSXML6 libraries. In the future, methods will be provided to add XML as additional element information.
UserData
UserData is extra information that is added to an element by a macro. The structure of the information is determined by the macro.
The macro is assigned an ID number by Bentley. The ID number is used to prevent macro conflict. To work with user data, the macro will need to first get the user data as a block of information. Then it will need to process the block of data using the CopyXXX method. The Copy methods use the copyToDataBlock parameter to determine if the data is being copied to or from the datablock.
' Do not use 22352 as your attribute ID. You must obtain a
' unique attribute ID from Bentley Systems.
Private Const attrId As Long = 22352
' AddLinkage and GetLinkage both transfer the data using TransferBlock.
' That way, it is easy to be certain that the transfer always occur in the
' same order.
Private Sub TransferBlock(dblk As DataBlock, _name As String, _value As Long, _copyToDataBlock As Boolean)
dblk.CopyString name, copyToDataBlock
dblk.CopyLong value, copyToDataBlock
End Sub
Sub AddLinkage()
Dim ele As Element
Dec-09 27 Working With Non-Graphic DataCopyright © 2009 Bentley Systems, Incorporated
Xdata
Dim id As DLong
Dim dblk As New DataBlock
id = DLongFromLong(50296)
Set ele = ActiveModelReference.GetElementByID(id)
TransferBlock dblk, "Added by User Attributes Example", 50296, True
ele.AddUserAttributeData attrId, dblk
ele.Rewrite
End Sub
Sub GetLinkage()
Dim ele As Element
Dim id As DLong
Dim dblk() As DataBlock
Dim value As Long, name As String
id = DLongFromLong(50296)
Set ele = ActiveModelReference.GetElementByID(id)
dblk = ele.GetUserAttributeData(attrId)
TransferBlock dblk(0), name, value, False
MsgBox "NAME: " & name & ", VALUE: " & value
End Sub
Note: When macros use the CopyString method, VBA will copy a Unicode String to the element. If the macro needs to be compatible with MDL the macro needs to convert the String to an Array of bytes, then use the CopyByteArray method.
Xdata
Another method that applications can use to store information is Xdata. Xdata is a format that AutoCAD uses to store extra information on elements. This has the advantage of making the information compatible with the DWG format.
Sub InterpretXData(Xdata() As XDatum)
Dim I As Long
Dim J As Long
Dim D As DLong
Dim Lev As Level
Working With Non-Graphic Data 28 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Xdata
Dim vt As Variant
' Using Debug.Print, display all of the XData to the Immediate Window of
' the Visual Basic Editor.
For I = LBound(Xdata) To UBound(Xdata)
With Xdata(I)
Debug.Print GetXDatumName(.Type) & " --- ";
If VarType(.Value) = vbEmpty Then
Debug.Print "the value is empty"
Else
Select Case .Type
Case msdXDatumTypePoint, _msdXDatumTypeWorldDirection, _msdXDatumTypeWorldSpaceDisplacement, _msdXDatumTypeWorldSpacePosition
' Value is of the type Point3d.
Debug.Print .Value.X; .Value.Y; .Value.Z
Case msdXDatumTypeDatabaseHandle
' Value is a hex string. Get the element
' ID by calling DLongFromHexString
Debug.Print "&H" & .Value
Dim eleID As DLong
eleID = DLongFromHexString(.Value)
Case msdXDatumTypeBinaryData, _msdXDatumTypeUnsupported
' Value is of the type Byte().
For J = LBound(.Value) To UBound(.Value)
Debug.Print Hex(.Value(J)); " ";
Next J
Case Else
' Value is of a type that can be printed directly.
Debug.Print .Value
End Select
End If
End With ' Xdata(I)
Next I
End Sub
Dec-09 29 Working With Non-Graphic DataCopyright © 2009 Bentley Systems, Incorporated
Xdata
Sub ShowEleXData()
' Find all of the XData in any element
Dim ee As ElementEnumerator
Set ee = ActiveModelReference.GraphicalElementCache.Scan
Do While ee.MoveNext
If ee.Current.HasAnyXData Then
Dim appNames() As String
Dim index As Long
appNames = ee.Current.GetXDataApplicationNames
For index = LBound(appNames) To UBound(appNames)
Dim aXdata() As XDatum
aXdata = ee.Current.GetXData(appNames(index))
InterpretXData aXdata
Next
End If
Loop
End Sub
Sub ShowAllModelXData()
' Find all XData contained on any model in the active design file
Dim theModel As ModelReference
Dim appNames() As String
Dim index As Long
For Each theModel In ActiveDesignFile.Models
If theModel.HasAnyXData Then
Debug.Print "-----Reporting XData for model " & _theModel.Name & " -----"
appNames = theModel.GetXDataApplicationNames
For index = LBound(appNames) To UBound(appNames)
Dim aXdata() As XDatum
Debug.Print "----- Application " & _appNames(index) & "-----"
aXdata = theModel.GetXData(appNames(index))
Working With Non-Graphic Data 30 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Xdata
InterpretXData aXdata
Next
End If
Next
End Sub
Function GetXDatumName(xdType As MsdXDatumType) As String
' Translates an XData type into a String
Select Case xdType
Case msdXDatumTypeBinaryData
GetXDatumName = "Binary Data"
Case msdXDatumTypeControlString
GetXDatumName = "Control String"
Case msdXDatumTypeDatabaseHandle
GetXDatumName = "Database Handle"
Case msdXDatumTypeDistance
GetXDatumName = "Distance"
Case msdXDatumTypeInt16
GetXDatumName = "Int16"
Case msdXDatumTypeInt32
GetXDatumName = "Int32"
Case msdXDatumTypeLevel
GetXDatumName = "Level"
Case msdXDatumTypePoint
GetXDatumName = "Point"
Case msdXDatumTypeReal
GetXDatumName = "Real"
Dec-09 31 Working With Non-Graphic DataCopyright © 2009 Bentley Systems, Incorporated
Module Review
Case msdXDatumTypeScaleFactor
GetXDatumName = "Scale Factor"
Case msdXDatumTypeString
GetXDatumName = "String"
Case msdXDatumTypeUnsupported
GetXDatumName = "Unsupported"
Case msdXDatumTypeWorldDirection
GetXDatumName = "World Direction"
Case msdXDatumTypeWorldSpaceDisplacement
GetXDatumName = "World Space Displacement"
Case msdXDatumTypeWorldSpacePosition
GetXDatumName = "World Space Position"
End Select
GetXDatumName = GetXDatumName & "(" & xdType & ")"
End Function
Module Review
Now that you have completed this module, let’s measure what you have learned.
Questions
1 What is the preferred method to work with an external database?
2 What is required to work with User Data?
3 What tools are used to work with XML data?
Working With Non-Graphic Data 32 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Extending Functionality
Module Overview
The “C” programming environment is available to applications in order to work with the data structures used in MicroStation. Applications work through the Get/SetCExpressionValue method of the Application Object to access the “C” expression evaluator that is in MicroStation’s runtime environment.
Module Prerequisites
• Familiarity with MDL as a platform for developing programmed customizations for MicroStation.
Refer to the Course Overview for additional pre‐requisites.
Module Objectives
After completing this module, you will be able to:
• Create VBA code utilizing MDL functions.
Introductory Knowledge
Before you begin this module, let's define what you already know.
Questions
1 What are the components of an ADOConnection string?
Dec-09 33 Extending Functionality
Copyright © 2009 Bentley Systems, Incorporated
CExpression Processor
2 True or False: There are no practical limitations placed on the number of MDL applications that can be simultaneously loaded.
3 What is the MDL dialog in MicroStation used for and how do you open it?
Answers
1 Service provider type, DSN name, user name or id, password
2 True
3 The MDL dialog is used to load and unload MDL applications and to view technical details and key‐ins for MDL applications. To open it, select Utilities > MDL.
CExpression Processor
An expression is a valid programming statement as it would be put into a “C” program. The GetCExpressionValue method takes in the “C” expression and, optionally, the MDL application that is required. The SetCExpressionValue method takes in the expression used to set some value in an MDL application. When an MDL function is not exposed to VBA, the Cexpression evaluator can often be used to call the function. The Cexpression processor can be used to call MDL functions from external Visual Basic programs.
retValue GetCexpressionValue(“mdlPolygon_pointInsideXY (“ & _
VarPtr(pCentroid) & “,” & _
VarPtr(boundryPoints(0)) & “,” & _
numboundryPoints & “,” _
tol & ")")
Option Explicit
Implements IPrimitiveCommandEvents
Private Sub IPrimitiveCommandEvents_Cleanup()
End Sub
Private Sub IPrimitiveCommandEvents_DataPoint(Point As Point3d, _ByVal View As View)
Dim volumeP As Double
Extending Functionality 34 Dec-09Copyright © 2009 Bentley Systems, Incorporated
CExpression Processor
Dim areaP As Double
Dim closureP As Double
Dim iXYP As Double
Dim iXZP As Double
Dim iYZP As Double
Dim tol As Double
Dim pCentroid As Point3d
Dim pMoment As Point3d
Dim pPrincipalMoments As Point3d
Dim pPrincipalDirections As Point3d
Dim retValue As Long
Dim oElement As Element
tol = 1#
Set oElement = CommandState.LocateElement(Point, View, False)
retValue = 0
If Not oElement Is Nothing Then
If oElement.IsPlanarElement Or oElement.Type = _msdElementTypeBsplineSurface Then
retValue = _ GetCExpressionValue("mdlMeasure_surfaceProperties (" & _ VarPtr(areaP) & "," & _ VarPtr(pCentroid) & "," & _ VarPtr(pMoment) & "," & _ VarPtr(iXYP) & "," & _ VarPtr(iXZP) & "," & _ VarPtr(iYZP) & "," & _ VarPtr(pPrincipalMoments) & "," & _ VarPtr(pPrincipalDirections) & "," & _ oElement.MdlElementDescrP & "," & _ tol & "," & " 0)")
Else
retValue = GetCExpressionValue("mdlMeasure_volumeProperties (" & _ VarPtr(volumeP) & "," & _
VarPtr(areaP) & "," & _ VarPtr(closureP) & "," & _ VarPtr(pCentroid) & "," & _ VarPtr(pMoment) & "," & _ VarPtr(iXYP) & "," & _ VarPtr(iXZP) & "," & _
Dec-09 35 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
VarPtr(iYZP) & "," & _ VarPtr(pPrincipalMoments) & "," & _ VarPtr(pPrincipalDirections) & "," & _ oElement.MdlElementDescrP & "," & _ tol & "," & " 0)")
End If
If retValue = 0 Then
Debug.Print "the centroid is _"; pCentroid.X; pCentroid.Y; pCentroid.Z
Else
Debug.Print "returned a "; retValue
End If
End If
End Sub
Private Sub IPrimitiveCommandEvents_Dynamics(Point As Point3d, _ByVal View As View, ByVal DrawMode As MsdDrawingMode)
End Sub
Private Sub IPrimitiveCommandEvents_Keyin(ByVal Keyin As String)
End Sub
Private Sub Class_Initialize()
End Sub
Private Sub IPrimitiveCommandEvents_Reset()
End Sub
Private Sub IPrimitiveCommandEvents_Start()
CommandState.SetLocateCursor
End Sub
Calling C and MDL Functions
One of the many features available to programmers in Visual Basic is the ability to import functions from other libraries. This can be done using the Declare
Extending Functionality 36 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
statement and providing the prototype for the function call in a Visual Basic module.
The syntax is as follows:
[Public | Private] Declare Sub subName Lib "libName.dll" [Alias "AliasName"] [ ( [ argumentList] ) ]
[Public | Private] Declare Function FuncName Lib "libName.dll" [Alias "AliasName"] [ ( [ argumentList] ) ] As Type
Public|Private defines the scope of the procedure. All declarations in a class module must be private. If neither Public or Private keyword is used, the declaration is implicitly Public.
The subName or FuncName must start with a letter, be unique within the scope, have less than 255 characters, and cannot be a VBA keyword,
The library must be the complete name including the .dll extension unless it is one of the standard Windows libraries. If the path is not specified then the search is executed using the following logic:
• The directory that the application was loaded
• The current working directory
• The Windows\System32 directory
• The Windows\System directory
• The PATH environment variable
Optionally, the “Alias” can be used to define the name as written in the original source code. That is the “subName” local to the VBA module, only the Alias is the name in the DLL.
The Argument list is the variable name and the type of information that the library needs. Some common conversions are as follows.
C DataType VBA DataType
BOOL ByVal fValue as Integer
BYTE ByVal bytValue as Byte
DWORD ByVal ingValue as Long
int ByVal intValue as Long
LPSTR ByVal strValue as String
Dec-09 37 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
Example declarations
Public Declare Function WinHelp Lib "user32" Alias "WinHelpA" _(ByVal hwnd As Long, _ByVal lpHelpFile As String, _ByVal lngCommand As Long, _dwData As Any) As Long
Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex _As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _(ByVal lpClassName As String, _ByVal lpWindowName As Any) As Long
Private Declare Function GetWindowLong Lib "user32" Alias _"GetWindowLongA" (ByVal HWND As Long, _ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias _"SetWindowLongA" (ByVal HWND As Long, _ByVal nIndex As Long, _ByVal dwNewLong As Long) As Long
Private Const WS_THICKFRAME = &H40000
Private Const GWL_STYLE = (–16)
Private Sub UserForm_Activate()
Dim lngFrmHWND As Long
Dim lngStyle As Long
Dim lngRetVal As Long
lngFrmHWND = FindWindow("ThunderDFrame", Me.Caption)
lngStyle = GetWindowLong(lngFrmHWND, GWL_STYLE)
lngRetVal = SetWindowLong(lngFrmHWND, GWL_STYLE, _lngStyle Or WS_THICKFRAME)
End Sub
Using MDL functions in VBA classes
You can use MDL functions in VBA classes by importing libraries delivered with the MicroStation SDK.
Extending Functionality 38 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
• The stdmdlbltin.dll library provides access to the core mdl functions in MicroStation.
• The stdmdlaccessor.dll library provides some useful Element/Element descriptor functions.
The MicroStation SDK is available for download athttp://www.bentley.com/en‐US/Corporate/Bentley+Partner+Program/Technology+Partners/MicroStation+SDK.htm.
Some common MDL data types are:
In Visual Basic, an Integer is 16 bits and a Long is 32 bits. Passing an argument by reference generates a pointer to the variable.
Some examples of using these are as follows.
Levels
Option Explicit
Private Declare Function mdlLevel_setActiveByName Lib "stdmdlbltin.dll" _(ByVal parentLevelIdIn As Long, _ByVal pLevelNameIn As Long) As Long
Sub SetActiveLevelFromLibraryTest()
Dim ParentID As Long
Dim oLevelName As String
Dim status As Long
Dim oPtr As Integer
' this needs to end up as 0xffffffff
ParentID = &HFFFFFFFF
oLevelName = "G-CODE"
status = mdlLevel_setActiveByName(ParentID, StrPtr(oLevelName))
Debug.Print status
End Sub
C DataType VBA DataType
Dpoint3d ByRef pt As Point3d
Transform ByRef transP As Transform3d
DgnModelRefP ByVal modelRef As Long
MSElementDescr ByRef edPP As Long
Dec-09 39 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
Multi‐line placement
Const NULLPtr As Long = 0
Declare Function mdlMline_create Lib "stdmdlbltin.dll" _(ByVal mline As Long, _ByVal seed As Long, _ByRef normal As Point3d, _ByRef points As Point3d, _ByVal nPoints As Long) As Long
Declare Function mdlElmdscr_new Lib "stdmdlbltin.dll" _(ByRef elDescrPP As Long, _ByVal elemHeader As Long, _ByVal element As Long) As Long
Function CreateMultiLine() As element
Dim bytes(0 To 2000) As Byte
Dim vertices(0 To 4) As Point3d
Dim elmDescrP As Long
vertices(0) = Point3dFromXY(0,0)
vertices(1) = Point3dFromXY(1,1)
vertices(2) = Point3dFromXY(2,0)
vertices(3) = Point3dFromXY(3,1)
vertices(4) = Point3dFromXY(4,0)
mdlMline_create VarPtr(bytes(0)), NULLPtr, Point3dFromXYZ(0,0,1), _vertices(0), 5
mdlElmdscr_new elmDescrP, NULLPtr, bytes(0)
Set CreateMultiLine = MdlCreateElementFromElementDescrP(elmDescrP)
End Function
Multi‐line information
Declare Sub mdlMline_getInfo Lib "stdmdlbltin.dll" _(ByRef nPoints As Long, _ByRef nLines As Long, _ByRef dist1 As Double, _ByRef dist2 As Double, _
Extending Functionality 40 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
ByRef zVector As Point3d, _ByVal mline As Long)
Declare Function ElmdscrAccessor_getMSElement Lib "stdmdlaccessor.dll" _(ByVal ElementDescr As Long) As Long
Sub DumpMLineInfo()
Dim ele As element
Dim pElement As Long
Dim elmDescr As Long
Dim nPoints As Long
Dim nLines As Long
Dim dDist1 As Double
Dim dDist2 As Double
Dim zVector As Point3d
Dim oScanCriteria As New ElementScanCriteria
Dim ElementEnum As ElementEnumerator
oScanCriteria.ExcludeAllTypes
oScanCriteria.IncludeType msdElementTypeMultiLine
Set ElementEnum = ActiveModelReference.Scan(oScanCriteria)
Do While ElementEnum.MoveNext
With ElementEnum.Current
elmDescr = .MdlElementDescrP
pElement = ElmdscrAccessor_getMSElement(elmDescr)
mdlMline_getInfo nPoints, nLines, dDist1, dDist2, _zVector, pElement
Debug.Print "nPoints = " & nPoints;
Debug.Print ", nLines = " & nLines;
Debug.Print ", dDist1 = " & dDist1;
Debug.Print ", dDist2 = " & dDist2
End With
Loop
End Sub
Note: In MicroStation V8 XM Edition and later, there exists a PropertyHandler object in the VBA object model that provides read‐write access to the property system used by the Element Information tool. Using PropertyHandler is
Dec-09 41 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
therefore a viable alternative not only for getting and setting element properties but also design file, model, and attachment properties. Because PropertyHandler works on a file's copy of an object, if a macro uses the object to change element properties, those changes are not automatically reflected in the Element object. The macro has to load the object again — perhaps by using the GetElementByID method — to get the changes. There are numerous examples of using PropertyHandler in the MicroStation VBA help document, Objects, PropertyHandler Object.
References
' Reference File Parameters
Const REFERENCE_DISPLAY = 1
Const REFERENCE_SNAP = 2
Const REFERENCE_LOCATE = 3
Const REFERENCE_SLOTACTIVE = 4
Const REFERENCE_SCALELINESTYLES = 5
Const REFERENCE_FILENOTFOUND = 6
Const REFERENCE_FILENAME = 7
Const REFERENCE_DESCRIPTION = 8
Const REFERENCE_LOGICAL = 9
Const REFERENCE_SCALE = 10
Const REFERENCE_ROTATION = 11
Const REFERENCE_ATTACHNAME = 13
Const REFERENCE_SCALE_MASTERUNITS = 15
Const REFERENCE_RESERVEDA = 16
Const REFERENCE_HIDDEN_LINE = 18
Const REFERENCE_DISPLAY_HIDDEN = 19
Const REFERENCE_CLIP_ROTATE = 20
Const REFERENCE_WCHAR_DESCRIPTION = 21
Const REFERENCE_WCHAR_LOGICAL = 22
Const REFERENCE_SCALE_STORED = 23
Const REFERENCE_SCALE_BY_UNITS = 24
Const REFERENCE_ANONYMOUS = 25
Const REFERENCE_OWNINGMODELREF = 26
Const REFERENCE_SOURCEMODELID = 27
Const REFERENCE_REFNUM = 28
Const REFERENCE_ELEMENTID = 29
Extending Functionality 42 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
Const REFERENCE_DISPLAYRASTERREFS = 30
Const REFERENCE_WCHAR_MODELNAME = 31
Const REFERENCE_USE_LIGHTS = 32
Const REFERENCE_DONOTNEST = 33
Const REFERENCE_CLIPBACK = 34
Const REFERENCE_CLIPFRONT = 35
Const REFERENCE_NESTDEPTH = 36
Const REFERENCE_RENDERMODE = 37
Const REFERENCE_REDUNDANT = 38
Const REFERENCE_LEVEL_OVERRIDES = 39
Const REFERENCE_DISPLAYFILENAME = 40
Const REFERENCE_DISPLAYATTACHNAME = 41
Const REFERENCE_DISPLAYMODELNAME = 42
Const REFERENCE_DWGBLOCKNAME = 43
Const REFERENCE_DONTDETACHONALL = 44
Const REFERENCE_DISPLAYFLAG = 45
Const REFERENCE_MODELNOTFOUND = 46
Const REFERENCE_CLIP_ROTMATRIX = 47
Const REFERENCE_LEVEL = 48
Const REFERENCE_DWGUNITMODE = 49
Const REFERENCE_HSVVALUEADJUST = 50
Const REFERENCE_HSVSATURATIONADJUST = 51
Const REFERENCE_BASENESTDEPTH = 52
Const REFERENCE_RIGHTNOTGRANTED = 53
Const REFERENCE_PRINTCOLORADJUST = 54
Const REFERENCE_METADATAONLY = 55
Const REFERENCE_EXTENDED = 56
Const REFERENCE_HSVHUESETTING = 57
Const REFERENCE_HSVADJUSTMENTFLAGS = 58
Const REFERENCE_DISPLAYPRIORITY = 59
Const REFERENCE_NAMEDGROUP = 60
Const REFERENCE_REVISION = 61
Const REFERENCE_TRANSPARENCY = 62
Const REFERENCE_PLOT_3D = 63
Const REFERENCE_NESTOVERRIDES = 64
Const REFERENCE_NEWLEVELDISPLAY = 65
Dec-09 43 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
Const REFERENCE_GLOBALLINESTYLESCALES = 66
Const REFERENCE_TREAT_AS_ELEMENT = 67
Const REFERENCE_PROVIDERID = 68
Const REFERENCE_RAWREVISION = 69
Const REFERENCE_REVISIONNOTFOUND = 70
Const REFERENCE_USEANNOTATIONSCALE = 71
Const REFERENCE_ATTACHMETHOD = 73
Const REFERENCE_ACTIVATESTATUS = 74
Const REFERENCE_SYNCHWITHSAVEDVIEW = 75
Const REFERENCE_USEVIEWFLAGS = 76
Const REFERENCE_SAVEDVIEWNAME = 77
Const REFERENCE_SAVEDVIEWELEMENTID = 78
Const REFERENCE_LEVELCONTROLSDISPLAY = 79
Const REFATTACH_NEST_NONE = 0
Const REFATTACH_NEST_COPY = 1
Const REFATTACH_NEST_DISPLAY = 2
Const REFCOLORTABLE_USEPREF = 0
Const REFCOLORTABLE_ALWAYS = 1
Const REFCOLORTABLE_NEVER = 2
Const REFERENCE_LOCATEON = 0
Const REFERENCE_LOCATEOFF = 1
Const REFERENCE_PARENTLOCATEOFF = 2
Const REFERENCE_NOLOCATERIGHTS = 3
Const REFERENCE_PARENTNOLOCATERIGHTS = 4
' Reference File mdl Functions
Declare Function mdlRefFile_attach Lib "stdmdlbltin.dll" _(ByRef outModelRefP As Long, _ByVal fileName As String, _ByVal logical As Long, _ByVal description As Long, _ByRef masterOrigin As Point3d, _ByRef referenceOrigin As Point3d, _ByVal scale As Double, _ByRef rotMatrix As Matrix3d, _ByVal nClipPoints As Long, _ByRef clipPoints As Point2d, _ByVal levelDisplayFlag As Long, _ByVal snapLock As Long, _ByVal locateLock As Long) As Long
Extending Functionality 44 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
Declare Function mdlRefFile_attachByView Lib "stdmdlbltin.dll" _(ByRef outModelRefP As Long, _ByVal fileName As String, _ByVal logical As Long, _ByVal description As Long, _ByRef viewName As Long, _ByVal scale As Double, _ByRef centerPoint As Point3d, _ByVal levelDisplayFlag As Long, _ByVal snapLock As Long, _ByVal locateLock As Long) As Long
Declare Function mdlRefFile_attachCoincident Lib "stdmdlbltin.dll" _(ByRef outModelRefP As Long, _ByVal fileName As String, _ByVal logical As Long, _ByVal description As Long, _ByVal levelDisplayFlag As Long, _ByVal snapLock As Long, _ByVal locateLock As Long) As Long
Declare Function mdlRefFile_attachCoincidentExtended Lib _"stdmdlbltin.dll" _(ByRef outModelRefP As Long, _ByVal fileName As String, _ByVal logical As Long, _ByVal description As Long, _ByVal levelDisplayFlag As Long, _ByVal snapLock As Long, _ByVal locateLock As Long, _ByVal callAsynchs As Long) As Long
Declare Function mdlRefFile_attachmentIdFromModelRef Lib _"stdmdlbltin.dll" _(ByVal modelRef As Long) As DLong
Declare Function mdlRefFile_beginAttachment Lib "stdmdlbltin.dll" _(ByRef outModelRefP As Long, _ByVal fileName As Long, _ByVal modelName As Long, _ByVal logical As Long, _ByVal description As Long) As Long
Declare Function mdlRefFile_completeAttachment Lib "stdmdlbltin.dll" _(ByVal modelRef As Long, _ByVal nestFlag As Long, _
Dec-09 45 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
ByVal nestDepth As Long, _ByVal initialDisplay As Long) As Long
Declare Function mdlRefFile_detach Lib "stdmdlbltin.dll" _(ByVal modelRef As Long) As Long
Declare Function mdlRefFile_getFromAttachmentID Lib "stdmdlbltin.dll" _(ByRef refFilePP As Long, _ByRef modelRefP As Long, _ByVal srcModelRef As Long, _ByVal attachmentID As DLong) As Long
Declare Function mdlRefFile_getInfo Lib "stdmdlbltin.dll" _(ByVal modelRef As Long) As Long ' Returns a pointer to a structure
Declare Function mdlRefFile_getLocateLock Lib "stdmdlbltin.dll" _(ByVal refP As Long) As Long
Declare Function mdlRefFile_getParameters Lib "stdmdlbltin.dll" _(ByVal param As Long, _ByVal paramName As Long, _ByVal modelRef As Long) As Long
Declare Function mdlRefFile_getParent Lib "stdmdlbltin.dll" _(ByVal refP As Long) As Long ' Returns a pointer to a structure
Declare Function mdlRefFile_getRefCount Lib "stdmdlbltin.dll" () As Long
Declare Function mdlRefFile_getSnapLock Lib "stdmdlbltin.dll"(ByVal refP As Long) As Long
Declare Function mdlRefFile_is3d Lib "stdmdlbltin.dll"(ByVal refP As Long) As Long
Declare Function mdlRefFile_isPrimary Lib "stdmdlbltin.dll"(ByVal refP As Long) As Long
Declare Function mdlRefFile_reattach Lib "stdmdlbltin.dll" _(ByVal modelRef As Long, _ByVal outName As String, _ByVal fileName As String, _ByVal ModelName As Integer) As Long
Extending Functionality 46 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
Declare Function mdlRefFile_reload Lib "stdmdlbltin.dll" _(ByVal modelRef As Long, _ByVal updateDisplay As Long, _ByVal forceReload As Long) As Long
Declare Function mdlRefFile_rotate Lib "stdmdlbltin.dll" _(ByVal modelRef As Long, _ByRef pivotP As Point3d, _ByVal xrotation As Double, _ByVal yrotation As Double, _ByVal zrotation As Double, _ByVal view As Long) As Long
Declare Function mdlRefFile_setClip Lib "stdmdlbltin.dll" _(ByVal modelRef As Long, _ByRef pts As Point2d, _ByVal nverts As Long) As Long
Declare Function mdlRefFile_setParameters Lib "stdmdlbltin.dll" _(ByVal param As Long, _ByVal paramName As Long, _ByVal modelRef As Long) As Long
Declare Function mdlRefFile_updateReference Lib "stdmdlbltin.dll" _(ByVal modelRef As Long, _ByVal displayMode As Long) As Long
Declare Function mdlRefFile_writeAttachment Lib "stdmdlbltin.dll" _(ByVal modelRef As Long) As Long
Declare Function mdlRefFile_writeAttachmentConditionally Lib _"stdmdlbltin.dll" _(ByVal modelRef As Long) As Long
Declare Function mdlMline_create Lib "stdmdlbltin.dll" _(ByVal mline As Long, _ByVal seed As Long, _ByRef normal As Point3d, _ByRef points As Point3d, _ByVal nPoints As Long) As Long
Dec-09 47 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
Declare Function mdlElmdscr_new Lib "stdmdlbltin.dll" _(ByRef elDescrPP As Long, ByVal elemHeader As Long, _ByVal element As Long) As Long
Const NULLPtr As Long = 0
Sub DumpRefData()
Dim activeModel As ModelReference
Dim refModel As ModelReference
Dim refModel2 As ModelReference
Dim refModels As ModelReferences
Dim attach As Attachment
Dim attchmnts As Attachments
Dim status As Long
Dim refP As Long
Dim modrefP As Long
Dim pts(0 To 4) As Point2d
Dim nverts As Long
Dim refDesc As String
Dim buffer(255) As Byte
Dim dgnfile As DesignFile
Dim param As Variant
Dim scalefactor As Double
scalefactor = ActiveModelReference.UORsPerMasterUnit
nverts = 5
pts(0).X = 0
pts(0).Y = 0
pts(1).X = 5 * scalefactor
pts(1).Y = 0
pts(2).X = 5 * scalefactor
pts(2).Y = 2 * scalefactor
pts(3).X = 0
pts(3).Y = 2 * scalefactor
pts(4).X = 0
pts(4).Y = 0
Extending Functionality 48 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
Dim ModelName As String
Dim LogicalName As String
Dim Description As String
ModelName = "Default"
LogicalName = "testing"
Description = "test file"
status = mdlRefFile_beginAttachment(modrefP, ActiveDesignFile.Name, _0, StrPtr(LogicalName), StrPtr(Description))
If status = 0 Then
status = mdlRefFile_setClip(modrefP, pts(0), nverts)
Debug.Print "Reference Clip Status = " & status
status = mdlRefFile_completeAttachment(modrefP, 2, -1, 0)
End If
End Sub
Declare Sub mdlElmdscr_setVisible Lib "stdmdlbltin.dll" _(ByVal edP As Long, _ByVal visible As Long)
Sub SetVisible()
Dim ele As element
Dim eleDescr As Long
Dim oScanCriteria As New ElementScanCriteria
Dim ElementEnum As ElementEnumerator
Set ElementEnum = ActiveModelReference.Scan(oScanCriteria)
Do While ElementEnum.MoveNext
Set ele = ElementEnum.Current
eleDescr = ele.MdlElementDescrP
mdlElmdscr_setVisible eleDescr, 0
If ele.FilePosition <> 0 Then
ele.Rewrite
End If
Dec-09 49 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
'now make it visible again
ActiveDesignFile.Views(1).Redraw
mdlElmdscr_setVisible eleDescr, 1
If ele.FilePosition <> 0 Then
ele.Rewrite
End If
ActiveDesignFile.Views(1).Redraw
Loop
End Sub
Element descriptors
Declare Function mdlACS_getCurrent Lib "stdmdlbltin.dll" _(ByRef originP As Point3d, _ByRef rotMatrixP As Matrix3d, _ByVal typeP As Long, _ByVal nameP As Long, _ByVal descriptionP As Long) As Long
Sub DoACS()
Dim ele As LineElement
Dim pntOrigin As Point3d
Dim pntStart As Point3d, pntEnd As Point3d
Dim rot As Matrix3d
Dim iType As Integer
Dim result As Long
Dim tms As Transform3d
result = mdlACS_getCurrent(pntOrigin, rot, iType, 0, 0)
rot = Matrix3dFromMatrix3dTimesMatrix3d(rot, _Matrix3dFromScale(ActiveModelReference.UORsPerMasterUnit))
rot = Matrix3dInverse(rot)
tms = Transform3dFromMatrix3dPoint3d(rot, pntOrigin)
' Create an element using this transform
pntStart = Point3dFromTransform3dTimesPoint3d(tms, Point3dFromXY(0,0))
pntEnd = Point3dFromTransform3dTimesPoint3d(tms, Point3dFromXY(3,3))
Extending Functionality 50 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
Set ele = CreateLineElement2(Nothing, pntStart, pntEnd)
ele.Redraw
ActiveModelReference.AddElement ele
End Sub
The method MdlCreateElementFromElementDescr will appear when you enable the Show hidden members option in the Object Browser.
Other methods available are:
• MdlGetModelReferenceFromModelRefP
• MdlGetDesignFileFromModelRefP
These will help bridge the gap between the two languages. In addition to stdmdlbltin.dll and stdmdlaccessor.dll, there are other libraries available that will expose most functions available to MDL in VBA. These are:
• stdbspline.dll for the Bspline curve and surface functions
• stdcons.dll for the constraint manager functions
• stdimage.dll for the image functions
• stdkisolid.dll for the kernel independent solid functions
• stdmtg.dll for the manifold topology functions
• stdraster.dll for the raster reference functions
• stdrdbms.dll for the database functions
• stdrender.dll for the rendering functions
One note about the functions that are in these libraries; since they are Pascal calling sequence functions, they do not handle variable argument lists. So, any function that uses a variable argument will not be available. The function templates are available from the MDL Function Reference help document (MDLAPIFunctionReference.chm) in the MicroStation SDK.
Excerpt from msdn.microsoft.com:
Traditionally, APIs are written for C and C++ programmers who are building Windows applications, but the functions in a DLL can be called by other programming languages, including VBA. Because most DLLs are written and documented primarily for C and C++ programmers, calling a DLL function
Dec-09 51 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
might differ somewhat from calling a VBA function. To work with an API, you must understand how to pass arguments to a DLL function.
Caution: Calling the Windows API and other DLL functions can be hazardous to the health of your application. When you call a DLL function directly from your code, you are bypassing some of the safety mechanisms that VBA usually provides for you. If you make a mistake in defining or calling a DLL function (as all programmers eventually do), you might cause an application error (also referred to as a general protection fault, or GPF) in your application. The best strategy is to save your project before you run your code, and to make sure you understand the principles behind calls to DLL functions.
Helpful notes
This process should by no means be a substitute for using the Object Model. It should be contained in modules and tested thoroughly. There are no safety checks to help you with memory allocation issues, and you have no access to the fields of a data structure.
You can use the VarPtr function to obtain a memory reference to a block of information. The drawback is that the type of data is unknown, and so, can be easily corrupted. An advantage can exist if a function can accept NULL as an input value.
To pass strings from VBA to mdl functions that require MSWChar*, you need to use the StrPtr method. This is because VBA tries to automatically convert String variables to char *.
The scope of these functions should be kept Private to the module so that the parameters can be adjusted for the specific case.
Exercise: Displaying a file selection dialog box using the function mdlDialog_fileOpen
1 If you have not already done so, download and install the MicroStation SDK fromhttp://www.bentley.com/en‐US/Corporate/Bentley+Partner+Program/Technology+Partners/MicroStation+SDK.htm
2 In the VBA Project Manager dialog, create and load the project Open_File.
3 Switch to the Visual Basic Editor, insert a module, and create the following macro, ShowOpen:
Extending Functionality 52 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
Private Declare Function mdlDialog_fileOpen Lib _"stdmdlbltin.dll" _(ByVal fileName As String, _ByVal rFileH As Long, _ByVal resourceId As Long, _ByVal suggestedFileName As String, _ByVal filterString As String, _ByVal defaultDirectory As String, _ByVal titleString As String) As Long
Private m_bCancelEnter As Boolean
Private m_flags As Long
Private m_FilterIndex As Long
Private m_Filter As String
Private m_FileName As String
Private m_iAction As Integer
Property Let Flags(lFlags As Long)
End Property
Property Get Flags() As Long
End Property
Public Property Get FilterIndex() As Long
FilterIndex = m_FilterIndex
End Property
Public Property Let FilterIndex(ByVal lFilterIndex As Long)
m_FilterIndex = lFilterIndex
End Property
Public Property Get Filter() As String
Filter = m_Filter
End Property
Public Property Let Filter(ByVal strFilter As String)
m_Filter = strFilter
End Property
Dec-09 53 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated
Calling C and MDL Functions
Public Property Get FileName() As String
FileName = m_FileName
End Property
Public Property Let FileName(ByVal strFileName As String)
TruncateAtEOS (strFileName)
m_FileName = strFileName
End Property
' Truncates a buffer to just contain the string that
' the C function returned.
Function TruncateAtEOS(str As String) As String
If str <> "" Then' This test avoids exception on zero-length string
TruncateAtEOS = Left$(str, InStr(1, str, vbNullChar) - 1)
End If
End Function
Public Sub ShowOpen()
Dim strNewFile As String
strNewFile = Space(1024)
If mdlDialog_fileOpen(strNewFile, 0, 0, "", m_Filter, _ "", "Select file") = 0 Then
TruncateAtEOS (strNewFile)
m_FileName = strNewFile
End If
End Sub
Public Property Get CancelEnter() As Boolean
CancelEnter = m_bCancelEnter
End Property
Public Property Let CancelEnter(ByVal bCancelEnter As Boolean)
m_bCancelEnter = bCancelEnter
End Property
Public Property Get Action() As Integer
Action = m_iAction
Extending Functionality 54 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Module Review
End Property
Public Property Let Action(ByVal iAction As Integer)
m_iAction = iAction
End Property
4 Run the macro.
To extend VBA to cover more possibilities, application developers need to be familiar with using other APIs that are complementary to VBA.
Module Review
Now that you have completed this module, let’s measure what you have learned.
Questions
1 What is the convention for calling C functions from VBA?
2 How can an application access MDL functions and data?
3 Where can a developer find the MDL function declarations needed for VBA?
Dec-09 55 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated
Events
Module Overview
Often applications will need to respond to some activity in the host environment. This is often the case in MicroStation, since it is an event machine. It constantly sends out messages that something is happening.
In order for a macro to take advantage of this environment, the macro needs to set up methods to be called when certain events happen.
Module Prerequisites
• Understand the concept of events and event‐driven applications.
• Ability to create macros implementing commands that add elements to a model.
• Ability to create macros implementing commands that interactively find and query or modify elements in a model.
Refer to the Course Overview for additional pre‐requisites.
Module Objectives
After completing this module, you will be able to:
• Create VBA code to listen for and act upon design file open and close operations.
• Create VBA code to listen for and act upon events exposed through the implementation of interfaces.
Dec-09 57 Events
Copyright © 2009 Bentley Systems, Incorporated
Introductory Knowledge
Introductory Knowledge
Before you begin this module, let's define what you already know.
Questions
1 What is the purpose of using the IPrimitiveCommandEvents_Dynamics method in a Primitive command?
2 Which of the following is not a method of a class that implements the ILocateCommandEvents interface?
• ILocateCommandEvents_LocateFilter
• ILocateCommandEvents_Dynamics
• ILocateCommandEvents_DataPoint
• ILocateCommandEvents_Accept
• ILocateCommandEvents_LocateFailed
• ILocateCommandEvents_Start
3 In a Locate command, what ancillary processing might the implemented interface class perform in its ILocateCommandEvents_LocateReset method?
Answers
1 IPrimitiveCommandEvents_Dynamics is used to let the command show reaction to mouse movements.
2 ILocateCommandEvents_DataPoint
3 Release some of the resources that have accumulated during the locate process.
Types of Events
There are two ways that events are caught in VBA. One is the WithEvents implementation. The other is by implementing interfaces and adding the class to the list of things to be called when an event occurs.
Events 58 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Types of Events
The two events that MicroStation supports as part of the application are OnDesignFileOpened and OnDesignFileClosed. The rest are available by adding a class that has the proper interface and method signatures to listen for the events. You will see a simple macro that will handle both situations.
File events
Macros often use MicroStation file open and close events to manage external references that are file dependent. To set a macro to respond to the OnDesignFileOpened and OnDesignFileClosed events, the class needs to declare a variable WithEvents oVariableName As Application. This will register the class that has this variable to be called when it is loaded and a file is opened or closed.
Private Sub oApplication_OnDesignFileClosed(ByVal DesignFileName _As String)
Debug.Print "application on design file close event"
End Sub
Private Sub oApplication_OnDesignFileOpened(ByVal DesignFileName _As String)
Debug.Print "application on design file opened event"
End Sub
Interfaces
To provide a framework for macros to integrate with MicroStation’s normal operations, Bentley has built a set of interfaces that classes can implement. When a class implements an interface, and so is registered with MicroStation, the methods that are defined by the interface will be called in response to certain events. This makes MicroStation extensible for most any purpose.
For the vast majority of events in MicroStation, special event handlers will need to be added to the system. To do this, the class must implement the specific interface that is designed for the task. When a class implements an interface it will provide methods of a pre‐defined signature that MicroStation can safely call.
As an example, in a class module add the line:
Implements IchangeTrackEvents
Dec-09 59 EventsCopyright © 2009 Bentley Systems, Incorporated
Types of Events
Now, in the class, add the calls that are required by the interface to be implemented:
IChangeTrackEvents_BeginUndoRedo – The undo/redo process is starting
IChangeTrackEvents_ElementChanged – an Element has changed
IChangeTrackEvents_FinishUndoRedo – The undo/redo process is completing
IChangeTrackEvents_Mark – Mark the file to group a set of changes
To establish these methods, the class needs to be listed with the system to be active. To make the system aware, there should be a method on the class that represents the system, such as addXXX. This method will take a reference to a class as a parameter. This class will then be called for the appropriate event.
It may be desirable to use the OnProjectLoad method to fire off the routine that will register the event listener. There is a parallel method for the unload event OnProjectUnload that is called when a project is removed.
Option Explicit
Private oEventHandlers As EventHandlerCls
Sub InstallEventHandlerCls()
RemoveEventHandlerCls
Set oEventHandlers = New EventHandlerCls
AddAttachmentEventsHandler oEventHandlers
AddChangeTrackEventsHandler oEventHandlers
AddLevelChangeEventsHandler oEventHandlers
AddModelActivateEventsHandler oEventHandlers
AddSaveAsEventsHandler oEventHandlers
AddViewUpdateEventsHandler oEventHandlers
End Sub
Sub RemoveEventHandlerCls()
If Not oEventHandlers Is Nothing Then
RemoveAttachmentEventsHandler oEventHandlers
RemoveChangeTrackEventsHandler oEventHandlers
RemoveLevelChangeEventsHandler oEventHandlers
RemoveModelActivateEventsHandler oEventHandlers
Events 60 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Types of Events
RemoveSaveAsEventsHandler oEventHandlers
RemoveViewUpdateEventsHandler oEventHandlers
End If
Set oEventHandlers = Nothing
End Sub
Sub OnProjectLoad()
InstallEventHandlerCls
End Sub
Sub OnProjectUnload()
RemoveEventHandlerCls
End Sub
The following interface classes are available:
• IAttachmentEvents — Lets macros monitor all of the changes to reference file attachments.
• IBatchConverterEvents — Lets macros monitor events related to MicroStation’s Batch Convertor utility.
• IChangeTrackEvents — Lets macros monitor all of the changes that happen to DesignFiles during a MicroStation session.
• IEnterIdleEvent — Lets macros receive notification that MicroStation has entered an idle state.
• ILevelChangeEvents — Defines the methods MicroStation uses to notify a macro of level‐change events.
• ILocateCommandEvents — Lets macros define element location commands. This is covered in the module “Building Commands”.
• IModalDialogEvents — Defines the methods MicroStation uses to notify a macro when the user opens and closes modal dialogs.
• IModelActivateEvents — Defines the methods MicroStation uses to notify a macro of model‐activate events.
• IModelChangeEvents — Defines the methods MicroStation uses to notify a macro of model‐change events.
• IPrimitiveCommandEvents — Lets macros define element placement commands. This is covered in the module “Building Commands”.
• IRasterEvents — Lets macros monitor all actions applied to rasters.
Dec-09 61 EventsCopyright © 2009 Bentley Systems, Incorporated
Module Review
• ISaveAsEvents — Lets macros provide methods that MicroStation calls both before and after it it performs any remapping that is required as part of a SaveAs command.
• IStandardsChecker — Lets macros extend MicroStation’s Standards Checker utility. This is covered in the module “Standards Checker Extensions”.
• IViewUpdateEvents — Defines the methods MicroStation uses to notify a macro of View update events.
Exercise: Handle an event
1 In the VBA Project Manager dialog, create and load the project Running_Element_Count.
2 Switch to the Visual Basic Editor, insert a class module, and implement a class that will update the element count when a write to file is made.
3 Insert a class module and implement a class that will reset the element count when the model changes.
Module Review
Now that you have completed this module, let’s measure what you have learned.
Questions
1 Name at least five interfaces available to a MicroStation VBA macro?
2 True or False: When a class implements an interface it defines unique calling signatures for its methods.
3 How can an application work with raster events?
Events 62 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Standards Checker Extensions
Module Overview
The Standards Checker in MicroStation is an extensible utility enabling the validation of design file data against a customizable set of standards. Implementing a simple StandardsChecker extension is a matter of implementing the IstandardsChecker interface, then having a module that will register the class as a StandardsCheckerApplication.
Module Prerequisites
• Understand the purpose of the Standards Checker utility in MicroStation.
• Familiar with the operation of the Standards Checker utility.
• Ability to create macros that listen for and act upon events.
Refer to the Course Overview for additional pre‐requisites.
Module Objectives
After completing this module, you will be able to:
• Develop custom plug‐ins to extend the functionality of the Standards Checker utility.
Introductory Knowledge
Before you begin this module, let's define what you already know.
Dec-09 63 Standards Checker Extensions
Copyright © 2009 Bentley Systems, Incorporated
Implementation
Questions
1 Which events does MicroStation support as part of the application?
2 What functionality does the AttachmentEvents interface class provide?
3 Which interface class defines the methods MicroStation uses to notify a macro of level‐change events?
Answers
1 OnDesignFileOpened and OnDesignFileClosed
2 The AttachmentEvents class lets VBA macros monitor changes to reference file attachments.
3 LevelChangeEvents
Implementation
The keys to implementing a StandardsChecker extension are to register the class only once to set up the settings and determine whether the user can interact with the check process. Standards Checker extensions can be implemented in any COM language, although only VBA macros are supported in the Power applications such as PowerDraft.
To implement a module for loading the class the code first has to make sure that there is not an already loaded class and if so unload that before loading the new one. To do this use the following structure for the module.
Public stdCheckerApp As clsStandardsCheckerImpl
Sub OnProjectLoad()
AddStdCheckerApp
End Sub
‘
' There is nothing to prevent this program from adding 2 standards
‘ checkers. To prevent that from happening, call RemoveAttachmentsChecker
‘ before adding the checker.
‘
Sub AddStdCheckerApp()
Standards Checker Extensions 64 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Implementation
RemoveStdCheckerApp
Set oSettingsCollection = New SettingCollection
oSettingsCollection.init
Set stdCheckerApp = New clsStandardsCheckerImpl
StandardsCheckerController.AddStandardsChecker stdCheckerApp, 1000
End Sub
Sub RemoveStdCheckerApp()
If Not stdCheckerApp Is Nothing Then
StandardsCheckerController.RemoveStandardsChecker stdCheckerApp
Set stdCheckerApp = Nothing
End Sub
Once this module is written, add the .mvba file to the list of Standards Checker applications by setting the configuration variable MS_STDCHECKERAPPS:
MS_STDCHECKERAPPS > mystdchecker.mvba.
Simple – use the basic framework
A class needs to implement the IStandardsChecker interface to use the basic framework. When implementing this interface, the class will only have a stub for the GetFixDetail method. The GetFixDetail method will be explored in more detail later.
The IdentityString, DialogString, VersionString and Description properties are used by the user interface and reporting systems for the Standards Checker. These will allow the macro to show its name and description and to be involved in the reporting process.
Not every checker macro will need to have some type of settings. By returning True for the HasSettings property, the settings button will be added to the standard user interface. The class needs to return True for the FoundSettings property if it is capable of operating. If the macro needs to have some settings before it can operate, then it needs to return False for FoundSettings. Any macro that returns False for the FoundSettings property is automatically disabled.
Dec-09 65 Standards Checker ExtensionsCopyright © 2009 Bentley Systems, Incorporated
Building Custom Standards Checker Applications
Advanced – use the Fixes optional dialog
The next step in the development of Standards Checker extensions is to add the capability to fix issues as they are found during the check process. To do this, the checker macro needs to call the ShowCheckerErrorWithFixOptions method which will call the GetFixDetails method.
This method requires the macro to fill a list of possible corrections for the user to pick from. These “fixes” are then presented to the user in the Standards Checker user interface. When the user has selected one of the fixes, it is sent back to the macro which then applies the fix to the element. Once the GetFixDetails method has returned the value of the ShowCheckerErrorWithFixOptions method, it is checked and one of the possible actions is taken. The results are then updated in the report using the StandardsCheckerReport class.
Building Custom Standards Checker Applications
One thing that is not apparent about the Standards Checker is the ability to extend the functionality to check more than just symbology or level definitions. It is a framework for processing design files. Application developers can use it to build custom plug‐in solutions to extend the capabilities.
An example of this functionality is the ability to read a corporate standard in a common file that is read at check time. Since symbology is more complex, only one type of information can be on one level. Custom interpretation of the information is required.
To build a custom plug‐in, start by creating a new project. In this project, add a new module. This module will be the entry point for the macro.
The next step is to insert a new class. In the new class, add the line to implement the IstandardsChecker interface.
The next step is to simply add the required method and property signatures for the interface. Now that the class to handle the standard checking is in place, add a subroutine in the module to load the class into the standard checker. An important note is that the plug‐in can be loaded multiple times. To prevent this, the macro should unload the class before trying to load the class. To move the custom plug‐in to the top of the list, use a high priority number (the second parameter).
Standards Checker Extensions 66 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Building Custom Standards Checker Applications
Option Explicit
Public oSettingsCollection As SettingCollection
Public stdCheckerApp As clsStandardsCheckerImpl
Private oSC As IstandardsChecker
Sub OnProjectLoad()
AddStdCheckerApp
End Sub
Sub AddStdCheckerApp()
‘
' There is nothing to prevent this program from adding 2 standards
‘ checkers. To prevent that from happening, call RemoveAttachmentsChecker
‘ before adding the checker.
‘
RemoveStdCheckerApp
Set oSettingsCollection = New SettingCollection
oSettingsCollection.init
Set stdCheckerApp = New clsStandardsCheckerImpl
StandardsCheckerController.AddStandardsChecker stdCheckerApp, 1000
End Sub
Sub RemoveStdCheckerApp()
If Not stdCheckerApp Is Nothing Then
StandardsCheckerController.RemoveStandardsChecker stdCheckerApp
Set stdCheckerApp = Nothing
End Sub
By implementing the interface, the class code will appear as follows.
Implements IStandardsChecker
Private Property Get IStandardsChecker_CallForEachModel() As Boolean
End Property
Private Sub IStandardsChecker_CreateSettings()
End Sub
Dec-09 67 Standards Checker ExtensionsCopyright © 2009 Bentley Systems, Incorporated
Building Custom Standards Checker Applications
Private Sub IStandardsChecker_DeleteSettings()
End Sub
Private Property Get IStandardsChecker_Description() As String
End Property
Private Property Get IStandardsChecker_DialogString() As String
End Property
Private Sub IStandardsChecker_EditSettings(ByVal IsReadOnly As Boolean)
End Sub
Private Property Get IStandardsChecker_FoundSettings() As Boolean
End Property
Private Sub IStandardsChecker_GetFixDetail(Fixes() As String, _ByVal SelectedFix As Long, _FixPropertiesLabel As String, _FixProperties() As String)
End Sub
Private Property Get IStandardsChecker_HasSettings() As Boolean
End Property
Private Property Get IStandardsChecker_IdentityString() As String
End Property
Private Sub IStandardsChecker_RunCheck _(ByVal ModelToCheck As ModelReference, _ByVal FirstModel As Boolean, _ByVal Options As Long)
End Sub
Private Property Get IStandardsChecker_VersionString() As String
End Property
Standards Checker Extensions 68 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Building Custom Standards Checker Applications
The macro control properties and methods to note here are HasSettings, EditSettings, FoundSetting, CreateSetting, and DeleteSetting. The HasSettings method is called by the interface to determine if the macro has some configurable settings. This will activate the settings button on the user interface. When the user selects the settings button, the EditSettings method in the plug‐in will be invoked. Plug‐in settings are stored as a settings element in the DGN library. If the plug‐in wants to store its settings in the design file, the CreateSettings method is invoked by the standards macro.
The DeleteSettings method is called to let the plug‐in delete the settings element. The FoundSettings property is used to tell the macro that the plug‐in has settings and is capable of running. For a macro to work properly, it must set FoundSettings to True so that it will be enabled on the user interface. Another property that a plug‐in should set is CallForEachModel. This tells the macro that the plug‐in should be called for each model in the design file.
The RunCheck method is called when the macro is invoked to check the elements. When the macro invokes the RunCheck method it will pass in the model that is being checked, some additional information such as if it is the first (or active) model, and some options that affect the operation of the plug‐in. In the RunCheck the plug‐in will do most of its work. The plug‐in will need to iterate through the elements it will check and, if necessary, invoke the user interface. To invoke the macro user interface, the plug‐in will call the StandardsCheckerController method, ShowCheckerError method, or ShowCheckerErrorWithFixOptions to interact with the user.
ShowCheckerErrorWithFixOptions requires some set up by the macro. The plug‐in will provide the options that the user can choose from if they want to fix the element. The plug‐in needs to build a set of “fixes” that will be displayed to the user in the StandardsChecker user interface. Along with this array of information, the plug‐in will provide the descriptions and labeling for the user interface.
When the user has responded to the prompting, the plug‐in needs to set the properties for the TotalProblems and FixedProblems that will be used in the user interface. If the plug‐in wants to add information to the XML report, it uses the StandardCheckerReport.AddProblem method. The last parameter for this method lets the report know if the plug‐in has fixed the element. The RunCheck is the heart of the plug‐in and, as such, will be the method that requires the most work when implementing a plug‐in.
The other methods provide some information about the macro to the standard interface. The Overstraining property lets the macro add information about the
Dec-09 69 Standards Checker ExtensionsCopyright © 2009 Bentley Systems, Incorporated
Building Custom Standards Checker Applications
plug‐ins that are used in processing the file to the report. The IdentityString property is used to provide a unique name for the plug‐in. The DialogString property is applied to the main dialog that the user will select from in the macro. The Description property is used in report generation from the macro.
To demonstrate this process, this sample macro will use the RunCheck method to process each element in the file. The settings for this plug‐in will determine the behavior of the plug‐in. The plug‐in will use a collection of rules to determine if the element is correct. The collection of rules can be thought of as the standard that the plug‐in is checking. In the plug‐in, the standard is stored in a CSV file that is opened when the Settings button is pressed on the StandardsChecker user interface. When the CSV file is opened, the plug‐in will parse the file and build its collection of rules to apply. As each model is passed into the RunCheck method, the plug‐in will iterate through the elements of that model.
Private Sub IStandardsChecker_RunCheck _(ByVal ModelToCheck As ModelReference, _ByVal FirstModel As Boolean, _ByVal Options As Long)
Dim oEnum As ElementEnumerator
Dim oCheckElement As Element
If oSettingsCollection.GetSettings.Count = 0 Then
MsgBox "Rerun and Pick Settings file first"
Exit Sub
End If
Set oEnum = ModelToCheck.Scan
Do While oEnum.MoveNext
checkSingleElement oEnum.Current
Loop
End Sub
Each element is passed into the CheckSingleElement method that is defined in the plug‐in. The CheckSingleElement method will examine the element and, in the case of a simple element, it will apply the check to the element. If the element is complex it will get the components and check them recursively. If the element passes the check, it allows the StandardsChecker to continue processing.
Standards Checker Extensions 70 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Building Custom Standards Checker Applications
Sub checkSingleElement(ocheckel As Element)
Dim oSubEnum As ElementEnumerator
Dim oSubEl As Element
Dim oSetting As Setting
If ocheckel.IsComplexElement Then
Set oSubEnum = ocheckel.AsComplexElement.GetSubElements
Do While oSubEnum.MoveNext
checkSingleElement oSubEnum.Current
Loop
Else
If ocheckel.IsGraphical And _ocheckel.Class = msdElementClassPrimary Then
Set oSetting = New Setting
oSetting.InitFromElement ocheckel
If oSettingsCollection.check(oSetting) = False Then
ReportBadElement ocheckel
End If
End If
End If
End Sub
If the element fails the check it will be passed to the ReportBadElement method that the plug‐in defines. The ReportBadElement method will invoke the StandardsCheckerShowErrorWithFixOptions method.
To call this method, the plug‐in will build the fixes array from the possible combinations that the element uses. The plug‐in uses the GetFixes method to generate information about the possible fixes.
' ShowCheckerErrorWithFixOptions calls this to get the data to display
' in the bottom section of the dialog. It also calls this everytime the
' user selects a different row in the list of fixes.
'
Private Sub IStandardsChecker_GetFixDetail _(Fixes() As String, _ByVal selectedFix As Long, _FixPropertiesLabel As String, _FixProperties() As String)
FixPropertiesLabel = "VBA Example Fix Properties"
Dec-09 71 Standards Checker ExtensionsCopyright © 2009 Bentley Systems, Incorporated
Building Custom Standards Checker Applications
Dim oSetting As Setting
Dim i As Integer
i = oSettingsCollection.GetSettings.Count
ReDim FixProperties(1 To i, 0 To 2)
For i = 1 To oSettingsCollection.GetSettings.Count
FixProperties(i, 0) = "Symbology Settings"
FixProperties(i, 1) = oSettingsCollection.GetSettings.Item(i).ToString
FixProperties(i, 2) = oSettingsCollection.GetSettings(i).description
Next i
End Sub
When the user selects from the options on the StandardsChecker macro interface, the selectedFix option is set to one of the available options. The plug‐in uses this value to determine the process path. The msdStandardsCheckerReplaceChoiceSkip is set when the plug‐in should simply skip the element.
The msdStandardsCheckerReplaceChoiceMarkIgnored option lets the plug‐in mark the element as ignored and the report can have these added so the user can revisit the element. The msdStandardsCheckerReplaceChoiceFix option tells the plug‐in that the element can be fixed according to the one of the fixes choices that the user has selected. The plug‐in can then take the corrective action and use the AddProblem method to set the element as fixed. Once the element has been processed the plug‐in will increment the TotalProblems and continue processing.
Private Sub ReportBadElement(oelm As Element)
Dim scc As StandardsCheckerController
Dim rpt As StandardsCheckerReport
Dim response As MsdStandardsCheckerReplaceChoice
Dim strDescr As String
Dim opts As MsdStandardsCheckerReplaceOptions
Dim handlerChoice As MsdStandardsCheckerReplaceChoice
Dim selFix As Long
Dim columnLabels(0 To 1) As String
Dim Fixes() As String
Dim oCurrSetting As New Setting
Dim i As Integer
Standards Checker Extensions 72 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Building Custom Standards Checker Applications
Dim scp As StandardsCheckerProblem
Set currElement = oelm
oCurrSetting.InitFromElement oelm
'StandardsCheckerController.ShowCheckerStatus "Example Status Message"
columnLabels(0) = "Symbology Options"
columnLabels(1) = ""
i = oSettingsCollection.GetSettings.Count
'set up the list of possible fixes for this element
ReDim Fixes(0 To i, 0 To 1)
Fixes(0, 0) = oCurrSetting.ToString: Fixes(0, 1) = "Current Settings"
For i = 1 To oSettingsCollection.GetSettings.Count
Fixes(i, 0) = oSettingsCollection.GetSettings.Item(i).ToString
Fixes(i, 1) = _oSettingsCollection.GetSettings.Item(i).description
Next i
strDescr = "Bad Element: " & DLongToString(oelm.ID) & _ElmToInfoString(oelm)
Set scc = StandardsCheckerController
opts = msdStandardsCheckerReplaceOptionCanFix Or _msdStandardsCheckerReplaceOptionCanIgnore
scc.ShowCheckerErrorWithFixOptions response, selFix, _strDescr, _"Fixes List Box Label", _columnLabels, Fixes, 0, _opts, _False
Set rpt = scc.Report
If response = msdStandardsCheckerReplaceChoiceSkip Then
Set scp = rpt.AddProblem(strDescr, "My Check", False)
' Record the ElementID because it is the only property of an Attachment
Dec-09 73 Standards Checker ExtensionsCopyright © 2009 Bentley Systems, Incorporated
Building Custom Standards Checker Applications
‘ that cannot change. If someone writes a program that processes
' the problem report, they can use the ElementID to be certain the
' program accesses the same attachment that the report refers to.
scp.AddElementID stdCheckerApp.currElement.ID
scp.AddStandard "My Element Checker App", m_libraryID
scp.AddVariance "My Symbology", "", oCurrSetting.ToString
End If
If response = msdStandardsCheckerReplaceChoiceFix Then
Dim aSetting As Setting
Set scp = rpt.AddProblem(strDescr, "My Check", True)
'the last parameter will put the check in the fixed column
scp.AddStandard "My Element Checker App", m_libraryID
scp.AddAction "Fixed Element Symbology"
scp.AddElementID stdCheckerApp.currElement.ID
If selFix > 0 Then
Set aSetting = oSettingsCollection.GetSettings(selFix)
Set oelm.Level = _ActiveDesignFile.Levels.Find(aSetting.Level)
oelm.Color = aSetting.Color
oelm.LineWeight = aSetting.Weight
End If
oelm.Redraw
oelm.Rewrite
scc.FixedProblems = scc.FixedProblems + 1
End If
If response = msdStandardsCheckerReplaceChoiceMarkIgnored Then
Set scp = rpt.AddIgnoredProblem("Element ", strDescr, _"My Check", False)
scp.AddAction "Fixed Element Symbology"
scp.AddElementID stdCheckerApp.currElement.ID
scp.AddStandard "My Element Checker App", m_libraryID
scp.AddVariance "My Symbology", "", oCurrSetting.ToString
scc.IgnoredProblems = scc.IgnoredProblems + 1
End If
Standards Checker Extensions 74 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Building Custom Standards Checker Applications
' If the user entered Cancel, tell RunCheck to abort
If response = msdStandardsCheckerReplaceChoiceAbort Then
stdCheckerApp.m_aborted = True
scc.TotalProblems = scc.TotalProblems + 1
End Sub
The plug‐in can use the AddedCheckerToStandardsCheckerApps method to add information to the report and call the AddLibraryToCheckerApp to add a node to the report. The report’s AddStandardToLibrary method is used to add the information to the XML data.
Private Sub IStandardsChecker_AddedCheckerToStandardsCheckerApps _(ByVal ApplicationXMLNode As Object)
Dim rpt As StandardsCheckerReport
' If the program declares these as IXMLDOMNode, then the program
' can add custom XML data to the report. If the program declares them
' as IXMLDOMNode then it must also set a reference to Microsoft XML
' v4.0.
Dim oLibraryNode As Object ' or IXMLDOMNode
Set rpt = StandardsCheckerController.Report
' m_libraryID is output from AddLibraryToCheckerApp.
' It is used later as input to AddStandard
Set oLibraryNode = rpt.AddLibraryToCheckerApp(ApplicationXMLNode, _StandardsCheckerController.SettingsFile, m_libraryID)
rpt.AddStandardToLibrary oLibraryNode, "Element Checker", "Elements"
End Sub
The framework that is provided in the IStandardsChecker interface allows the plug‐in developer to interact with the StandardsChecker user interface and build a seamless extension. The class that implements the IstandardChecker interface does not need to be developed in VBA — it can be defined in any language that supports COM.
The StandardsChecker keeps a list of COM objects that it will invoke. The configuration variable MS_STANDARDCHECKER_APP can be used to automatically load the plug‐in. If the file name ends in .mvba, it will automatically be added to
Dec-09 75 Standards Checker ExtensionsCopyright © 2009 Bentley Systems, Incorporated
Building Custom Standards Checker Applications
the list of applications that are loaded when the StandardsChecker is invoked by the user. There are many problems for plug‐ins to solve such as checking for missing references, etc.
Exercise: Implement a check
1 Implement the simple missing reference checker.
Questions
1 What are the keys to implementing a StandardsChecker extension?
2 In an implementation of the IStandardsChecker interface, what is the purpose of calling the HasSettings method?
3 True or False: A class implementing an IStandardsChecker interface need not be developed in VBA — it can be defined in any language that supports COM.
Standards Checker Extensions 76 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Module Review Answers
Building Commands
Questions
1 What are events?
2 What are the events that are available to the IPrimitiveCommandEvents interface?
3 What are the events that are available to the ILocateCommandEvents interface?
4 How does one make use of an interface?
Answers
1 Events are triggered as the user interacts with various parts of MicroStation.
2 CleanupDataPoint
Dynamics
Keyin
Reset
Start
Dec-09 77 Module Review Answers
Copyright © 2009 Bentley Systems, Incorporated
User Interface
3 Accept
Cleanup
Dynamics
LocateFailed
LocateFilter
LocateReset
Start
4 Create a class module that implements the interface. Use the Implements keyword in the class module and specify the interface object — for example, Implements IPrimitiveCommandEvents.
User Interface
Questions
1 Name at least five standard user interface controls available to VBA developers?
2 How can an Image Control be used in a dialog?
3 What methods must be included to make a form resizable?
Answers
1 CheckBox
ComboBox
CommandButton
Frame
Image
ListBox
MultiPage
OptionButton
ScrollBar
Module Review Answers 78 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Working With Non‐Graphic Data
SpinButton
ToggleButton
TabStrip
2 The SpinButton and ScrollBar can be used for manipulating the image that is displayed in the Image control.
3 FindWindow
GetWindowLong
SetWindowLong
Working With Non‐Graphic Data
Questions
1 What is the preferred method to work with an external database?
2 What is required to work with User Data?
3 What tools are used to work with XML data?
Answers
1 DSN data source
2 User attribute ID assigned by Bentley
3 MSXML
Extending Functionality
Questions
1 What is the convention for calling C functions from VBA?
2 How can an application access MDL functions and data?
3 Where can a developer find the MDL function declarations needed for VBA?
Dec-09 79 Module Review AnswersCopyright © 2009 Bentley Systems, Incorporated
Events
Answers
1 The GetCExpressionValue method takes in the “C” expression and, optionally, the MDL application that is required. The SetCExpressionValue method takes in the expression used to set some value in an MDL application.
2 Import functions from other applicable libraries provided with MicroStation by using the Declare statement and providing the prototype for the function call in a Visual Basic module.
3 MDL Function Reference help document (MDLAPIFunctionReference.chm) in the MicroStation SDK
Events
Questions
1 Name at least five interfaces available to a MicroStation VBA macro?
2 True or False: When a class implements an interface it defines unique calling signatures for its methods.
3 How can an application work with raster events?
Answers
1 IAttachmentEvents
IBatchConverterEvents
IChangeTrackEvents
IEnterIdleEvent
ILevelChangeEvents
ILocateCommandEvents
IModalDialogEvents
IModelActivateEvents
IModelChangeEvents
IPrimitiveCommandEvents
Module Review Answers 80 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Standards Checker Extensions
IRasterEvents
ISaveAsEvents
IStandardsChecker
IViewUpdateEvents
2 False. The class must provide methods of a pre‐defined signature that MicroStation can safely call.
3 Create a class module that implements an IRasterEvents interface.
Standards Checker Extensions
Questions
1 What are the keys to implementing a StandardsChecker extension?
2 In an implementation of the IStandardsChecker interface, what is the purpose of calling the HasSettings method?
3 True or False: A class implementing an IStandardsChecker interface need not be developed in VBA — it can be defined in any language that supports COM.
Answers
1 Register the class only once to set up the settings and determine whether the user can interact with the check process.
2 The HasSettings method is called to determine if the macro has some configurable settings.
3 True.
Dec-09 81 Module Review AnswersCopyright © 2009 Bentley Systems, Incorporated
Standards Checker Extensions
Module Review Answers 82 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Appendix
New to MicroStation VBA?
Visual Basic for Applications in MicroStation is Bentley’s implementation of the standard VBA SDK, with extensions that are relevant to the MicroStation environment. Visual Basic and its sibling, Visual Basic for Applications (VBA), are modern, Object‐oriented programming languages that are for use by professional and casual application developers alike. VBA in MicroStation provides an environment that has previously only been available to MDL/C programmers. VBA applications can integrate into the standard workflow of MicroStation applications.
The MicroStationDGN Object Model
In order to make the best use of the programming tools, a basic understanding of the application and its file structure is necessary.
The Application object represents the running instance of MicroStation. This means that all the features found in the product are accessible to VBA macros. A review of the Application object shows some important properties. Among these properties are the ActiveDesignFile, ActiveModelReference, and ActiveSettings. These represent the current design file, the active model, and the current settings.
Some other objects that are important to understand are Attachment, DesignFile, Element, and ModelReference.
• The Attachment object is a model attached as a reference.
• The DesignFile object is the container that holds models.
• The Element object is the base for all elements in the model, these include visible and non‐visible elements and simple and complex elements.
• The ModelReference object is the container for all the elements.
Dec-09 83 Appendix
Copyright © 2009 Bentley Systems, Incorporated
System Overview
There can be multiple models in one design file, but only one model is active at a time. The ModelReference will have Element caches for the graphical elements, and the DesignFile will have caches for the non‐model and control elements. The sub‐classes of elements match the types of elements that are in the design file.
In the Application Object there are many methods that can be useful when developing macros that are computationally complex. Point3d can be used to do vector math, and the Matrix3d and Transform groups of methods are used to scale and manipulate elements.
In the MicroStation Visual Basic for Applications help document, the Objects topic provides properties, methods and examples for many of the MicroStation objects.
System Overview
Configuration Variables for VBA
MS_VBANEWPROJECTDIRECTORY determines the default location for new Visual Basic project files.
MS_VBASEARCHDIRECTORIES determines the directories in which MicroStation will search for projects when they are loaded by name.
Appendix 84 Dec-09Copyright © 2009 Bentley Systems, Incorporated
System Overview
MS_VBAAUTOLOADPROJECTS and MS_VBAREQUIREDPROJECTS determine which projects are automatically loaded at startup time.
MS_VBASAVEONRUN determines whether modified VBA projects are automatically saved when a VBA macro is executed.
MS_VBA_OPEN_IN_MEMORY controls whether MicroStation keeps a project's data available by keeping the project's disk file open, or by copying the project into memory.
MS_VBAGUIDREFERENCES identifies DLLs and type libraries to which references will be automatically added in newly created projects.
MS_VBANAMEDREFERENCES identifies projects to which references will be automatically added in newly created projects.
MS_STANDARDSCHECKER_APPS holds a list of MDL applications. If the name of the file has .MVBA as the file extension the VBA project will be loaded.
Keyins for VBA
Following is a list of MicroStation VBA key‐ins.
• VBA CREATE <project name> — creates and loads a project with the specified name. The project file will be created with the specified project name as the root name and the extension .mvba. The file will be located in the directory specified by the configuration variable MS_VBANEWPROJECTDIRECTORY. If that configuration variable is not defined and a path is not specified in the key‐in, the file will be located in ...\Bentley\Program\MicroStation\.
• VBA EDIT — switches to the Visual Basic Editor.
• VBA EDIT <project name> — opens the specified project in the Visual Basic Editor. If the project is not currently loaded but can be found in the path(s) specified by the configration variable MS_VBASEARCHDIRECTORIES, it will be loaded automatically.
• VBA EXECUTE [project name] <VBA statement> — executes the specified VBA statement in the context of the specified project. If a project name is not specified, MicroStation chooses a project to use as the context. There is no need to specify a context if the statement uses only the MicroStationDGN object model. The statement is not case‐sensitive.
Example:VBA EXECUTE If ActiveModelReference.Is3D Then MsgBox "Active model is 3D."
Dec-09 85 AppendixCopyright © 2009 Bentley Systems, Incorporated
COM Client Applications
• VBA HIDE — exits the Visual Basic Editor.
• VBA LOAD — opens the Load Project dialog.
• VBA LOAD <project name> — loads the specified project. If the file path to the .mvba file is not specified, then MicroStation searches in the directories specified by the configuration variable MS_VBASEARCHDIRECTORIES. If a .mvba file is found with a root name that matches the specified project name, it is loaded.
• VBA PAUSE RECORDING — pauses the recording of a macro.
• VBA RESUME RECORDING — resumes recording of a previously paused macro.
• VBA RUN — opens the Macros dialog.
• VBA RUN <macro name>— runs the specified macro.
• VBA RUN [project name]<macro name> — loads the specified project if it is not already loaded and runs the specified macro. You need to include the square bracket delimiters [ ] with the project name.
• VBA SAVE <project name> — saves the specified project.
• VBA SAVEALL — saves all loaded projects.
• VBA SHOW EDITOR — switches to the Visual Basic Editor.
• VBA SHOW MACROS — opens the Macros dialog.
• VBA START RECORDING — starts the recording of a macro.
• VBA STOP RECORDING — stops the recording of a macro.
• VBA UNLOAD <project name> — unloads the specified project.
COM Client Applications
For proprietary MicroStation customizations where the code must be hidden from the user, developers can build compiled COM client applications (.exe). MicroStation exposes a type library for use by COM clients in accessing MicroStation functionality. The basis of this type library is the MicroStationDGN Object Model.
Microsoft’s Visual Basic (VB) development tools — VB6 and VB.NET — can be used to build COM client applications for MicroStation.
When building a COM client application, add a reference to the MicroStationDGN object, then set a variable to the MicroStationDGN
Appendix 86 Dec-09Copyright © 2009 Bentley Systems, Incorporated
ActiveX Controls and DLLs
Application. You can then access all the functionality of the MicroStationDGN Object Model.
At run time, a COM client application attaches to the most recently launched instance of MicroStation. If MicroStation is not running, the default MicroStation will start. This has some drawbacks if you use a special workspace or configuration.
ActiveX Controls and DLLs
ActiveX is a framework for defining reusable software components that perform a particular function or a set of functions in Microsoft Windows in a way that is independent of the programming language used to implement them.
• An ActiveX Control is a compiled .ocx file that implements user interface controls more sophisticated than those available in VBA. An example would be a TreeView control.
• An ActiveX DLL is a compiled .dll file that isolates specific logic and calculations from general application logic in a VBA macro or VB application.
Like standalone COM client applications (.exe) for MicroStation, ActiveX Controls and DLLs for MicroStation rely on the type library exposed by MicroStation. Microsoft’s Visual Basic development tools, v6.0 or greater, can be used to build ActiveX Controls and DLLs. (VB.NET does not support development of ActiveX Controls and DLLs.)
After you build an ActiveX Control or ActiveX DLL you need to register it using the Windows command regsevr32. To incorporate an ActiveX Object or DLL in a VBA macro, you need to reference the DLL — select Tools > References in the Visual Basic Editor. After you do so, you can write VBA code that accesses the ActiveX Object or DLL.
ActiveX Objects and DLLs can even include references to MicroStationDGN objects. A simple example of an ActiveX DLL that includes such a reference is as follows.
This function is in a VB project, MarksMath, that has a class, Simple, which contains this code.
Public Function GetCurrentDesignFileName() As String
Dim oMSApp As MicroStationDGN.Application
Set oMSApp = MicroStationDGN.Application
Dec-09 87 AppendixCopyright © 2009 Bentley Systems, Incorporated
Interface Programming
GetCurrentDesignFileName = oMSApp.ActiveDesignFile.Name
End Function
The VBA code looks like this:
Sub workwithmarksmath()
Dim oMath As MarksMath.Simple
Set oMath = New MarksMath.Simple
Dim oMathApp As MarksMath.Application
Set oMathApp = New MarksMath.Application
Dim oString As StringoString = oMath.GetCurrentDesignFileName
Debug.Print oString
End Sub
Interface Programming
An Interface is a group of properties, events, and methods that belong to a class. The class that is implementing the interface must provide an implementation of each of the properties, events, and methods that are specified by the Interface.
Sample Macros
Scanning
Sub SimpleScan()
Dim oScanCriteria As ElementScanCriteria
Dim oElementEnum As ElementEnumerator
Dim i As Long
i = 0
Set oScanCriteria = New ElementScanCriteria
oScanCriteria.IncludeOnlyVisible
Set oElementEnum = ActiveModelReference.Scan(oScanCriteria)
Do While oElementEnum.MoveNext
Debug.Print "Found an Element of type Number " & _oElementEnum.Current.Type
Appendix 88 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Sample Macros
i = i + 1
Loop
Debug.Print "found a total of " & i & "elements"
End Sub
Geometry
Private Function computeCenterPoint(oEl As LineElement) As Point3d
computeCenterPoint = Point3dInterpolate(oEl.Range.Low, 0.5, _oEl.Range.High)
End Function
Private Function ComputeLow(ept As Point3d, npt As Point3d) As Point3d
Dim tPt As Point3d
tPt = ept
If npt.x < ept.x Then
tPt.x = npt.x
End If
If npt.Y < ept.Y Then
tPt.Y = npt.Y
End If
If npt.Z < ept.Z Then
tPt.Z = npt.Z
End If
ComputeLow = tPt
End Function
Private Function ComputeHigh(ept As Point3d, npt As Point3d) As Point3d
Dim tPt As Point3d
tPt = ept
If npt.x > ept.x Then
tPt.x = npt.x
End If
Dec-09 89 AppendixCopyright © 2009 Bentley Systems, Incorporated
Sample Macros
If npt.Y > ept.Y Then
tPt.Y = npt.Y
End If
If npt.Z > ept.Z Then
tPt.Z = npt.Z
End If
ComputeHigh = tPt
End Function
Private Function ComputeMaxRange(pts() As Point3d) As Range3d
Dim oRange As Range3d
Dim i As Long
oRange.High = pts(0)
oRange.Low = pts(0)
For i = LBound(pts) To UBound(pts)
oRange.High = ComputeHigh(oRange.High, pts(i))
oRange.Low = ComputeLow(oRange.Low, pts(i))
Next
ComputeMaxRange = oRange
End Function
Sub CreateBoundryPoints(aPoints() As Point3d, _bPoints() As Point3d, _uOrder As Long, _vOrder As Long)
ReDim bPoints(uOrder * 2 + vOrder * 2)
Dim i As Long
For i = 0 To uOrder
bPoints(i) = Point3dInterpolate(aPoints(0), i / uOrder, _aPoints(1))
Next
Dim x As Long
x = 0
For i = uOrder To uOrder + vOrder
Appendix 90 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Sample Macros
bPoints(i) = Point3dInterpolate(aPoints(1), x / vOrder, _aPoints(2))
x = x + 1
Next
x = 0
For i = uOrder + vOrder To (uOrder * 2) + vOrder
bPoints(i) = Point3dInterpolate(aPoints(2), x / uOrder, _aPoints(3))
x = x + 1
Next
x = 0
For i = (uOrder * 2) + vOrder To (uOrder * 2) + (vOrder * 2)
bPoints(i) = Point3dInterpolate(aPoints(3), x / vOrder, _aPoints(0))
x = x + 1
Next
End Sub
Sub ConvertRangeToPoints(oRange As Range3d, pts() As Point3d)
pts(0) = oRange.Low
pts(2) = oRange.High
pts(1) = oRange.Low
pts(1).x = oRange.High.x
pts(3) = oRange.High
pts(3).x = oRange.Low.x
End Sub
Sub testDTM()
Dim oSurface As BsplineSurfaceElement, oElement As Element
Dim surface As BsplineSurface
Dim aFitPoints() As Point3d
Dim aNullWeights() As Double
Dim oScanCrit As New ElementScanCriteria
Dim oEnumerator As ElementEnumerator
Dim count As Long, i As Long
' get all text elements (assumed to be DTM atoms)
Dec-09 91 AppendixCopyright © 2009 Bentley Systems, Incorporated
Sample Macros
oScanCrit.ExcludeAllTypes
oScanCrit.IncludeType msdElementTypeLineString
Set oEnumerator = ActiveModelReference.Scan(oScanCrit)
'Count points
Do While oEnumerator.MoveNext
count = count + 1
Loop
ReDim aFitPoints(0 To count - 1)
oEnumerator.Reset
i = 0
Do While oEnumerator.MoveNext
Set oElement = oEnumerator.Current
With oElement.AsLineElement
aFitPoints(i) = computeCenterPoint(oElement)
oElement.Redraw msdDrawingModeHilite
i = i + 1
End With
Loop
Dim oRangeBlock As Range3d
oRangeBlock = ComputeMaxRange(aFitPoints)
Dim oShape As ShapeElement
Dim aVerts(3) As Point3d
ConvertRangeToPoints oRangeBlock, aVerts
Set oShape = CreateShapeElement1(Nothing, aVerts)
oShape.Redraw msdDrawingModeHilite
ActiveModelReference.AddElement oShape
Dim uOrder As Long
Dim vOrder As Long
Appendix 92 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Sample Macros
Dim boundryPoints() As Point3d
uOrder = 10
vOrder = 10
'copy the orgdata
Dim orgData() As Point3d
orgData = aFitPoints
'this function will create some boundry points to keep the surface 'from going nuts!
CreateBoundryPoints aVerts, boundryPoints, uOrder, vOrder
ReDim Preserve aFitPoints(0 To (count + (2 * uOrder) + _(2 * vOrder)) - 1)
Dim x As Long
Dim l As Long
l = 0
x = (count + (2 * uOrder) + (2 * vOrder)) - 1
For x = count To UBound(aFitPoints)
aFitPoints(x) = boundryPoints(l)
l = l + 1
Next
'Compute 10 x 10 LSQ cubic surface (5 minutes)
Set surface = New BsplineSurface
surface.FromWeightedPointsAndPlane aFitPoints, aNullWeights, _Transform3dZero, uOrder, vOrder, 4, 4
Set oSurface = CreateBsplineSurfaceElement1(Nothing, surface)
oSurface.Color = 3
ActiveModelReference.AddElement oSurface
oSurface.Redraw
'reset things?
aFitPoints = orgData
uOrder = 15
vOrder = 15
CreateBoundryPoints aVerts, boundryPoints, uOrder, vOrder
Dec-09 93 AppendixCopyright © 2009 Bentley Systems, Incorporated
Sample Macros
ReDim Preserve aFitPoints(0 To (count + (2 * uOrder) + _(2 * vOrder)) - 1)
l = 0
For x = count To UBound(aFitPoints)
aFitPoints(x) = boundryPoints(l)
l = l + 1
Next
'Compute 30 x 30 LSQ cubic surface (14 minutes)
surface.FromWeightedPointsAndPlane aFitPoints, aNullWeights, _Transform3dZero, uOrder, vOrder, 4, 4
Set oSurface = CreateBsplineSurfaceElement1(Nothing, surface)
oSurface.Color = 4
ActiveModelReference.AddElement oSurface
oSurface.Redraw
'Compute 30 x 30 LSQ quadratic surface (14 minutes)
'reset things?
aFitPoints = orgData
uOrder = 30
vOrder = 30
CreateBoundryPoints aVerts, boundryPoints, uOrder, vOrder
ReDim Preserve aFitPoints(0 To (count + (2 * uOrder) + _(2 * vOrder)) - 1)
l = 0
x = (count + (2 * uOrder) + (2 * vOrder)) - 1
For x = count To UBound(aFitPoints)
aFitPoints(x) = boundryPoints(l)
l = l + 1
Next
surface.FromWeightedPointsAndPlane aFitPoints, aNullWeights, _Transform3dZero, uOrder, vOrder, 3, 3
Appendix 94 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Sample Macros
Set oSurface = CreateBsplineSurfaceElement1(Nothing, surface)
oSurface.Color = 5
ActiveModelReference.AddElement oSurface
oSurface.Redraw
'Compute 50 x 50 LSQ quadratic surface (1 hr 45 min)
'reset things?
aFitPoints = orgData
uOrder = 50
vOrder = 50
CreateBoundryPoints aVerts, boundryPoints, uOrder, vOrder
ReDim Preserve aFitPoints(0 To (count + (2 * uOrder) + _(2 * vOrder)) - 1)
l = 0
x = (count + (2 * uOrder) + (2 * vOrder)) - 1
For x = count To UBound(aFitPoints)
aFitPoints(x) = boundryPoints(l)
l = l + 1
Next
surface.FromWeightedPointsAndPlane aFitPoints, aNullWeights, _Transform3dZero, uOrder, vOrder, 3, 3
Set oSurface = CreateBsplineSurfaceElement1(Nothing, surface)
oSurface.Color = 6
ActiveModelReference.AddElement oSurface
oSurface.Redraw
'Compute 50 x 75 LSQ quadratic surface
'reset things?
aFitPoints = orgData
uOrder = 50
vOrder = 75
CreateBoundryPoints aVerts, boundryPoints, uOrder, vOrder
Dec-09 95 AppendixCopyright © 2009 Bentley Systems, Incorporated
Sample Macros
ReDim Preserve aFitPoints(0 To (count + (2 * uOrder) + _(2 * vOrder)) - 1)
l = 0
x = (count + (2 * uOrder) + (2 * vOrder)) - 1
For x = count To UBound(aFitPoints)
aFitPoints(x) = boundryPoints(l)
l = l + 1
Next
surface.FromWeightedPointsAndPlane aFitPoints, aNullWeights, _Transform3dZero, uOrder, vOrder, 4, 4
Set oSurface = CreateBsplineSurfaceElement1(Nothing, surface)
oSurface.Color = 7
ActiveModelReference.AddElement oSurface
oSurface.Redraw
End Sub
Working with other files
Sub ScanAndCount()
Dim fileName As String
Dim oDesignFile As DesignFile
Dim oElementCache As ElementCache
Dim oEnum As ElementEnumerator
Dim oScanCriteria As ElementScanCriteria
Dim iSize As Long
Dim iCount As Long
Dim oModel As ModelReference
Dim i As Long
fileName = "d:\data\dwg\g1088-p4-2.dwg"
Set oDesignFile = OpenDesignFileForProgram(fileName, True)
For Each oModel In oDesignFile.Models
Appendix 96 Dec-09Copyright © 2009 Bentley Systems, Incorporated
Sample Macros
i = 0
Set oElementCache = oModel.GraphicalElementCache
iCount = oElementCache.Count
Set oScanCriteria = New ElementScanCriteria
Set oEnum = oElementCache.Scan(oScanCriteria)
Do While oEnum.MoveNext
Debug.Print "Found an Element of type Number " & _oEnum.Current.Type
i = i + 1
Loop
Debug.Print "Found " & i & "elements in this file " & _oModel.name
Next
oDesignFile.Close
End Sub
Dynamic user interface
Option Explicit
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _(ByVal lpClassName As String, _ByVal lpWindowName As Any) As Long
Private Declare Function GetWindowLong Lib "user32" Alias _"GetWindowLongA" _(ByVal HWND As Long, _ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias _"SetWindowLongA" _(ByVal HWND As Long, _ByVal nIndex As Long, _ByVal dwNewLong As Long) As Long
Private Const WS_THICKFRAME = &H40000
Private Const GWL_STYLE = (-16)
Private Sub UserForm_Activate()
Dim lngFrmHWND As Long
Dim lngStyle As Long
Dec-09 97 AppendixCopyright © 2009 Bentley Systems, Incorporated
Sample Macros
Dim lngRetVal As Long
lngFrmHWND = FindWindow("ThunderDFrame", Me.Caption)
lngStyle = GetWindowLong(lngFrmHWND, GWL_STYLE)
lngRetVal = SetWindowLong(lngFrmHWND, GWL_STYLE, lngStyle Or _WS_THICKFRAME)
End Sub
Private Sub UserForm_Resize()
'ListBox2.Top = Me.Top
ListBox2.Left = 1
ListBox2.Width = Me.Width - 9
ListBox2.Height = Me.Height / 2 - 5
Label1.Top = ListBox2.Height + 2
Label1.Height = 5
Label1.Width = ListBox2.Width
ListBox1.Top = ListBox2.Height + Label1.Height + 5
'ListBox1.Height = Me.Height - (ListBox2.Height + Label1.Height + 18)
ListBox1.Height = Me.Height / 2 - 10
ListBox1.Left = ListBox2.Left
ListBox1.Width = ListBox2.Width
Debug.Print "Height: " & Me.Height & " Width:" & Me.Width
Debug.Print "the upper list box is "; ListBox2.Height; _" tall starts at "; ListBox2.Top
Debug.Print "the lower list box is "; ListBox1.Height; _" tall starts at "; ListBox1.Top
End Sub
Appendix 98 Dec-09Copyright © 2009 Bentley Systems, Incorporated