Questions similar to this appear on the Bentley Discussion Groups. These problems appeared in the VBA discussion group.
Q How do I extract the boundary of a TextElement ? How do I extract the boundary when the TextElement is rotated?
A
A TextElement 's Boundary is a UDT with Low and High components, each a Point3d.
If the TextElement is not rotated, then these points can be used to construct a rectangle that represents the
TextElement 's boundary.
When a TextElement is rotated the Boundary.Low and Boundary.High points
are modified so that they continue to provide the lowest and highest coordinates that bound the element.
If you attempt to construct a rectangle from the boundary points of a rotated TextElement , you end up with
something that appears to have little to do with the TextElement you started with.
The solution is to unrotate the TextElement before extracting its Boundary.Low and Boundary.High points.
Then derive a rectangle from those points, and rotate the rectangle to the TextElement 's original rotation.
The VBA Project TextAnalysis is available in ZIP archive TextAnalysis.zip. The project illustrates how to extract a valid text boundary from rotated or unrotated TextElement s. The code implements a model scanner that extracts boundary data from each TextElement , then creates a ShapeElement corresponding to that boundary.
The key procedure in the project is the function ExtractBoundary. The essential code of
ExtractBoundary is shown below, less error statements.
Function ExtractBoundary(ByRef points() As Point3d, ByVal oText As TextElement) As Boolean
ExtractBoundary = False
Dim oRotation As Matrix3d
Dim oTransform As Transform3d
' Save the Text Element's rotation
oRotation = oText.Rotation
' Create a transformation from the inverse of the rotation about the text origin
oTransform = Transform3dFromMatrix3dAndFixedPoint3d(Matrix3dInverse(oRotation), oText.origin)
' Unrotate the Text Element (but don't rewrite it)
oText.Transform oTransform
' Create a rectangle's vertices from the unrotated Text Element's boundary
Dim i As Integer
For i = 0 To 4
points(i) = oText.Boundary.Low
Next i
points(2) = oText.Boundary.High
points(1).X = points(2).X
points(3).Y = points(2).Y
' Create a transformation to rotate the points to match the original Text Element
oTransform = Transform3dFromMatrix3dAndFixedPoint3d(oRotation, oText.origin)
' Transform the boundary rectangle to the original rotation
For i = 0 To 4
points(i) = Point3dFromTransform3dTimesPoint3d(oTransform, points(i))
Next i
ExtractBoundary = True
End Function
You can download the VBA TextAnalysis Project as a ZIP archive.
Unpack the ZIP archive and extract TextAnalysis.mvba to a suitable location, such as
C:\Program Files\Bentley\Workspace\Standards\MVBA.
Q How do I increment the value of a TextElement ? How do I change 500 TextElements automatically?
A The best by far method is to purchase a license for FlexiTable™. FlexiTable provides ways to review, create and modify text. Text modification methods work using formulæ similar to those you find in Excel™. However, since you're reading this you must be a VBA programmer, so skip this flagrant self-promotion and get into the nitty-gritty!
You change lots of elements at once by scanning a model. Use an ElementScanCriteria class
to filter TextElements, then use the ActiveModelReference.Scan method to obtain an
ElementEnumerator. The enumerator is, put simply, a traversable list of elements that pass the
scan criteria test.
Once you have each element, in this case a TextElement, you can do with it what you want. In this example, what we want to do is add a number to TextElements that have a numeric content. In other words, we want to find TextElements that contain a number, add (or subtract) an amount to that number, then update the TextElement …
You can download the VBA Change Numeric Text Project as a ZIP archive.
Unpack the ZIP archive and extract ChangeNumericText.mvba to a suitable location, such as
C:\Program Files\Bentley\Workspace\Standards\MVBA.
To run this utility keyin …
vba run [ChangeNumericText]modMain.Main <delta>
where <delta> is the positive or negative number to apply
Q How do I change the TextStyle of a TextElement ? How do I change the font of a TextElement ?
A Changing a TextElement's font and style can seem a littly tricky. You can't simply do the obvious, such as …
Dim oText As TextElement
... assign oText from somewhere
oText.TextStyle.BackgroundFillColor = 111
The reason you can't do that has to do with the way in which VBA handles the members of classes that contain sub-classes. It's explained in a remote corner of the MicroStation VBA documentation. Rather than perform the above sort of direct manipulation, obvious though it may seem, you have take some indirect steps to achieve your goal …
TextStyle variable to your TextElement's TextStyle
TextStyle that you want to change
TextStyle to your TextStyle variable
A similar sequence of steps is needed to change a TextElement's font. A line of code is worth 1,000 words: here's an example procedure …
' ---------------------------------------------------------------------
' SetTextStyle
' Attempt to set a text element's style and font
' Example call syntax:
' If (SetTextStyle (oText, "250Leroy")) Then ... End If
' Returns: True on success
' ---------------------------------------------------------------------
Function SetTextStyle(ByRef oText As TextElement, ByVal fontName) As Boolean
SetTextStyle = False
On Error GoTo err_SetTextStyle
Dim oFont As Font
Set oFont = ActiveDesignFile.Fonts.Find(msdFontTypeWindowsTrueType, fontName, Nothing)
If oFont Is Nothing Then
Dim warning As String
warning = "Font '" & fontName & "' not in active DGN file"
ShowMessage msg, msg, msdMessageCenterPriorityWarning, True
Else
Debug.Print "Found font '" & oFont.Name & "' in active DGN file"
Dim oStyle As TextStyle
Set oStyle = oText.TextStyle
Set oStyle.Font = oFont
oStyle.BackgroundFillColor = 253
oStyle.BorderColor = 253
oStyle.BorderAndBackgroundVisible = True
Set oText.TextStyle = oStyle
oText.Redraw msdDrawingModeNormal
oText.Rewrite
SetTextStyle = True
End If
Exit Function
err_SetTextStyle:
MsgBox Err.Description, vbOKOnly Or vbCritical, "Error in SetTextStyle"
End Function
Q How do I change the TextStyle of a TextElement by its level ?
A The question arose because the VBA macro recorder doesn't see the settings changed when you use the Text Style dialog. This example illustrates how to set the text style by queuing a command …
Option Explicit' --------------------------------------------------------------------- ' Change Text Style by Level ' --------------------------------------------------------------------- ' Notice: ' Example MicroStation VBA code supplied by LA Solutions Ltd. ' You are free to use this code in your own work provided that ' this notice is retained in full. Use at your own risk. ' Visit our website ' http://www.la-solutions.co.uk ' End of notice ' --------------------------------------------------------------------- ' ChangeTextStyleByLevelTest ' Tests the ChangeTextStyleByLevel subroutine ' ---------------------------------------------------------------------Sub ChangeTextStyleByLevelTest() Debug.Print "ChangeTextStyleByLevel Test" Dim strTextStyle As String, _ strLevelName As String strTextStyle = "Arial Blue"' This is an example: change to your text style namestrLevelName = "Words"' This is an example: change to your level nameChangeTextStyleByLevel strTextStyle, strLevelName strTextStyle = "Lucida Green"' This is an example: change to your text style namestrLevelName = "More Words"' This is an example: change to your level nameChangeTextStyleByLevel strTextStyle, strLevelName CommandState.StartDefaultCommand End Sub' --------------------------------------------------------------------- ' ChangeTextStyleByLevel ' Change text elements on a specified level by applying a Text Style ' ---------------------------------------------------------------------Sub ChangeTextStyleByLevel(ByVal strTextStyle As String, ByVal strLevelName As String) Debug.Print "ChangeTextStyleByLevel Text Style " & SingleQuote(strTextStyle) & " Level " & SingleQuote(strLevelName); "" Dim confirm As Point3d' Arbitrary coordinates to confirm commands that require a datapointconfirm = Point3dFromXY(1, 1)' Local reference to active text styleDim oTextStyle As TextStyle Set oTextStyle = ActiveSettings.TextStyle' Send a keyin that selects all elements on the specified levelCadInputQueue.SendKeyin "level element select " & DoubleQuoteIfSpace(strLevelName)' Start the Change Text Attributes commandCadInputQueue.SendCommand "DMSG ACTIVATETOOLBYPATH \Drawing\Text\Change Text Attributes"' Change the active text styleCadInputQueue.SendCommand "textstyle active " & strTextStyle Debug.Print "Active text style is " & oTextStyle.Name' Apply the new style to selected textCadInputQueue.SendCommand "MODIFY TEXT"' Confirm the current commandCadInputQueue.SendDataPoint confirm, 1' Send a reset to the current commandCadInputQueue.SendReset' Drop selectionCadInputQueue.SendCommand "CHOOSE NONE" End Sub' --------------------------------------------------------------------- ' Double Quote If Space wraps a string in double-quote marks if the string contains a space ' ---------------------------------------------------------------------Function DoubleQuoteIfSpace(ByVal s As String) As String If (HasSpace(s)) Then DoubleQuoteIfSpace = DoubleQuote(s) Else DoubleQuoteIfSpace = s End If End Function' --------------------------------------------------------------------- ' Double Quote wraps a string in double-quote marks ' ---------------------------------------------------------------------Function DoubleQuote(ByVal s As String) As String Const Quote As String = """" DoubleQuote = Quote & s & Quote End Function' --------------------------------------------------------------------- ' Single Quote If Space wraps a string in double-quote marks if the string contains a space ' ---------------------------------------------------------------------Function SingleQuoteIfSpace(ByVal s As String) As String If (HasSpace(s)) Then SingleQuoteIfSpace = SingleQuote(s) Else SingleQuoteIfSpace = s End If End Function' --------------------------------------------------------------------- ' Singe Quote wraps a string in single-quote marks ' ---------------------------------------------------------------------Function SingleQuote(ByVal s As String) As String Const Quote As String = "'" SingleQuote = Quote & s & Quote End Function' --------------------------------------------------------------------- ' HasSpace returns True if string contains a space ' ---------------------------------------------------------------------Function HasSpace(ByVal s As String) As Boolean HasSpace = False Const Space As String = " " If (0 < InStr(1, s, Space)) Then HasSpace = True End If End Function
Q How do I set the active text settings and place text?
A First of all, consider using a text style rather than manipulating the active settings. Like paragraph styles in a text processor, such as Microsoft Word™, text styles allow you to define consistent settings. You can apply the same text style to a set of text elements, and then, if you need the text to look different, simply change the text style.
However, you want to know how to modify settings using VBA, rather than read my rants about super-duper features of MicroStation. The answer is very simple. So simple, in fact, that the code speaks for itself …
' --------------------------------------------------------------------- ' Set Text Size ' Set the current text height & width ' ---------------------------------------------------------------------Sub SetTextSize(ByVal size As Double) If (0 < size) Then ActiveSettings.TextStyle.Height = size ActiveSettings.TextStyle.Width = size End If End Sub' --------------------------------------------------------------------- ' Place custom text: text size 10, place 'X' ' ---------------------------------------------------------------------Sub PlaceCustomText() SetTextSize 10 Const command As String = "PLACE TEXT ICON;" Dim text As String' Change this text to whatever you wanttext = "X" Debug.Print "This is the command sent to MicroStation: " & command & text CadInputQueue.SendCommand command & text, True End Sub