MicroStationV8iSs1VBAforAdvancedUsers TRN014750 1 0001
March 9, 2017 | Author: vthiyagain | Category: N/A
Short Description
Download MicroStationV8iSs1VBAforAdvancedUsers TRN014750 1 0001...
Description
MicroStation VBA for Advanced Users MicroStation V8i (SELECTseries 1) Bentley Institute Course Guide
TRN014750-1/0001
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
Table of Contents Course Overview ____________________________________ 1 Course Description ____________________________________ 1 Target Audience_______________________________________ 1 Prerequisites _________________________________________ 1 Course Objectives _____________________________________ 2 Modules Included _____________________________________ 2
Building Commands _________________________________ 3 Module Overview _____________________________________ 3 Module Prerequisites __________________________________ 3 Module Objectives_____________________________________ 4 Introductory Knowledge ________________________________ 4 Questions ________________________________________ 4 Answers__________________________________________ 4 Adding Elements – PrimitiveCommands ____________________ 4 Modifying Elements – LocateCommands ___________________ 7 Module Review _______________________________________ 9 Questions ________________________________________ 10
The User Interface ___________________________________ 11 Module Overview _____________________________________ 11 Module Prerequisites __________________________________ 11 Module Objectives_____________________________________ 11 Introductory Knowledge ________________________________ 11 Questions ________________________________________ 12 Answers__________________________________________ 12 Building a User Interface ________________________________ 12 Module Review _______________________________________ 15 Questions ________________________________________ 15
Working With Non‐Graphic Data _______________________ 17 Module Overview _____________________________________ 17 Module Prerequisites __________________________________ 17 Module Objectives_____________________________________ 17 Introductory Knowledge ________________________________ 17
Dec-09
iii
Copyright © 2009 Bentley Systems, Incorporated
Table of Contents
Table of Contents
Questions ________________________________________ 18 Answers__________________________________________ 18 Storing Non‐Graphic Information With Elements_____________ 18 Tags ________________________________________________ 19 Databases ___________________________________________ 21 Connecting _______________________________________ 21 Elements with database records ______________________ 24 XML ________________________________________________ 27 UserData ____________________________________________ 27 Xdata _______________________________________________ 28 Module Review _______________________________________ 32 Questions ________________________________________ 32
Extending Functionality ______________________________ 33 Module Overview _____________________________________ 33 Module Prerequisites __________________________________ 33 Module Objectives_____________________________________ 33 Introductory Knowledge ________________________________ 33 Questions ________________________________________ 33 Answers__________________________________________ 34 CExpression Processor__________________________________ 34 Calling C and MDL Functions _____________________________ 36 Using MDL functions in VBA classes ____________________ 38 Helpful notes______________________________________ 52 Module Review _______________________________________ 55 Questions ________________________________________ 55
Events ____________________________________________ 57 Module Overview _____________________________________ 57 Module Prerequisites __________________________________ 57 Module Objectives_____________________________________ 57 Introductory Knowledge ________________________________ 58 Questions ________________________________________ 58 Answers__________________________________________ 58 Types of Events _______________________________________ 58 File events ________________________________________ 59 Interfaces ________________________________________ 59 Module Review _______________________________________ 62 Questions ________________________________________ 62
Standards Checker Extensions _________________________ 63 Module Overview _____________________________________ 63 Module Prerequisites __________________________________ 63 Module Objectives_____________________________________ 63 Introductory Knowledge ________________________________ 63
Dec-09
iv
Copyright © 2009 Bentley Systems, Incorporated
Table of Contents
Table of Contents
Questions ________________________________________ 64 Answers__________________________________________ 64 Implementation _______________________________________ 64 Simple – use the basic framework _____________________ 65 Advanced – use the Fixes optional dialog________________ 66 Building Custom Standards Checker Applications ____________ 66 Questions ________________________________________ 76
Module Review Answers _____________________________ 77 Building Commands____________________________________ 77 Questions ________________________________________ 77 Answers__________________________________________ 77 User Interface ________________________________________ 78 Questions ________________________________________ 78 Answers__________________________________________ 78 Working With Non‐Graphic Data _________________________ 79 Questions ________________________________________ 79 Answers__________________________________________ 79 Extending Functionality _________________________________ 79 Questions ________________________________________ 79 Answers__________________________________________ 80 Events ______________________________________________ 80 Questions ________________________________________ 80 Answers__________________________________________ 80 Standards Checker Extensions____________________________ 81 Questions ________________________________________ 81 Answers__________________________________________ 81
Appendix __________________________________________ 83 New to MicroStation VBA? ______________________________ 83 The MicroStationDGN Object Model ___________________ 83 System Overview ______________________________________ 84 Configuration Variables for VBA _______________________ 84 Keyins for VBA_____________________________________ 85 COM Client Applications ________________________________ 86 ActiveX Controls and DLLs _______________________________ 87 Interface Programming _________________________________ 88 Sample Macros _______________________________________ 88 Scanning _________________________________________ 88 Geometry ________________________________________ 89 Working with other files _____________________________ 96 Dynamic user interface ______________________________ 97
Dec-09
v
Copyright © 2009 Bentley Systems, Incorporated
Table of Contents
Table of Contents
Table of Contents
vi
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Dec-09
•
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.
1 Copyright © 2009 Bentley Systems, Incorporated
Course Overview
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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 Copyright © 2009 Bentley Systems, Incorporated
Building Commands
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Building Commands
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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 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
Copyright © 2009 Bentley Systems, Incorporated
Building Commands
Modifying Elements – LocateCommands
Option Explicit Implements 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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Building Commands
Module Review
Questions
Building Commands
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?
10
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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 Copyright © 2009 Bentley Systems, Incorporated
The User Interface
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
The User Interface
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; " at "; ListBox2.Top
tall starts _
Debug.Print "the lower list box is "; ListBox1.Height; " at "; ListBox1.Top
tall starts _
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Dec-09
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?
15
Copyright © 2009 Bentley Systems, Incorporated
The User Interface
Module Review
The User Interface
16
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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 Copyright © 2009 Bentley Systems, Incorporated
Working With Non-Graphic Data
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Working With Non-Graphic Data
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Working With Non-Graphic Data
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Working With Non-Graphic Data
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Working With Non-Graphic Data
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Working With Non-Graphic Data
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Working With Non-Graphic Data
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Working With Non-Graphic Data
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Dec-09
What are the components of an ADOConnection string?
33 Copyright © 2009 Bentley Systems, Incorporated
Extending Functionality
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Extending Functionality
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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.
Dec-09
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
37
Copyright © 2009 Bentley Systems, Incorporated
Extending Functionality
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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 at http://www.bentley.com/en‐US/Corporate/Bentley+Partner+Program/ Technology+Partners/MicroStation+SDK.htm. Some common MDL data types are: C DataType
VBA DataType
Dpoint3d
ByRef pt As Point3d
Transform
ByRef transP As Transform3d
DgnModelRefP
ByVal modelRef As Long
MSElementDescr ByRef edPP As Long
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
Dec-09
39
Copyright © 2009 Bentley Systems, Incorporated
Extending Functionality
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Extending Functionality
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Extending Functionality
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Extending Functionality
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Extending Functionality
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Extending Functionality
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Extending Functionality
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 from http://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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Extending Functionality
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Dec-09
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?
55
Copyright © 2009 Bentley Systems, Incorporated
Extending Functionality
Module Review
Extending Functionality
56
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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:
Dec-09
•
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.
57 Copyright © 2009 Bentley Systems, Incorporated
Events
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?
3
•
ILocateCommandEvents_LocateFilter
•
ILocateCommandEvents_Dynamics
•
ILocateCommandEvents_DataPoint
•
ILocateCommandEvents_Accept
•
ILocateCommandEvents_LocateFailed
•
ILocateCommandEvents_Start
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Events
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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”. •
Dec-09
IRasterEvents — Lets macros monitor all actions applied to rasters.
61
Copyright © 2009 Bentley Systems, Incorporated
Events
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
Events
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?
62
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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 Copyright © 2009 Bentley Systems, Incorporated
Standards Checker Extensions
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Standards Checker Extensions
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Standards Checker Extensions
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Standards Checker Extensions
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Standards Checker Extensions
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Standards Checker Extensions
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Standards Checker Extensions
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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 Copyright © 2009 Bentley Systems, Incorporated
Module Review Answers
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Dec-09
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?
79
Copyright © 2009 Bentley Systems, Incorporated
Module Review Answers
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Dec-09
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.
81
Copyright © 2009 Bentley Systems, Incorporated
Module Review Answers
Standards Checker Extensions
Module Review Answers
82
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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.
Dec-09
•
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.
83 Copyright © 2009 Bentley Systems, Incorporated
Appendix
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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 — 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 — 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] — 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
Copyright © 2009 Bentley Systems, Incorporated
Appendix
COM Client Applications
•
VBA HIDE — exits the Visual Basic Editor.
•
VBA LOAD — opens the Load Project dialog.
•
VBA LOAD — 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 — runs the specified macro.
•
VBA RUN [project 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 — 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 — 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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Appendix
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 String oString = 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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Appendix
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Appendix
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Appendix
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Appendix
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
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
Copyright © 2009 Bentley Systems, Incorporated
Appendix
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
Copyright © 2009 Bentley Systems, Incorporated
Dec-09
View more...
Comments