3 4
Determining where to place shapes can be one of the more challenging tasks for a program that creates Microsoft Visio drawings, especially in connected diagrams or other kinds of drawings with complex relationships between shapes. The ultimate goal is the same: You'll need to calculate a pair of page coordinates for each shape you place on the drawing page. The approach you take will depend on the kind of drawing you're trying to create and the data on which the drawing is based.
In addition, Visio provides automatic layout capabilities that control how the shapes and connectors between shapes in your drawing interact. You can view the default layout settings in the Page Layout and Shape Layout sections in a ShapeSheet window. You can customize these settings in your program by working with the formulas in these sections. For details about shapes and automatic layout, see Designing Shapes for Automatic Layout in Chapter 11, Arranging Shapes in Drawings. For details about the Page Layout and Shape Layout sections, see the Microsoft Visio Developer Reference (on the Help menu, click Developer Reference).
The following CreateDrawing procedure provides an example of placing shapes in a simple network diagram. The example reads data from a two-dimensional array—the first element in the array describes the name of a master in the Basic Network Shapes 3D stencil, and the second element of the array is a shape label. CreateDrawing places a hub in the middle of the page, and then places the components in a circle around the hub.
This example demonstrates several techniques for placing shapes, including the following:
Public Sub CreateDrawing(arrNetData As String) Dim shpObjHUB As Visio.Shape Dim shpObjNodes As Visio.Shape Dim mstObj As Visio.Master Dim stnObj As Visio.Document Dim dblX, dblY As Double Dim dblDegreeInc As Double Dim dblRad As Double Dim dblPageWidth, dblPageHeight As Double Dim i As Integer Const PI = 3.1415 Const CircleRadius = 2 'Divide the circle by the number of objects in the array so they 'are spaced evenly dblDegreeInc = 360 / UBound(arrNetData) 'Read the PageWidth and PageHeight properties dblPageWidth = ActivePage.PageSheet.Cells("PageWidth").ResultIU dblPageHeight = ActivePage.PageSheet.Cells("PageHeight").ResultIU 'Open the Basic Network Shapes 3D stencil Set stnObj = Application.Documents.OpenEx("Basic Network Shapes _ 3D.vss", visOpenDocked) 'Process the hub shape Set mstObj = stnObj.Masters(arrNetData(0, 0)) Set shpObjHUB = ActivePage.Drop(mstObj, dblPageWidth / 2, _ dblPageHeight / 2) 'Set the text of the hub shape shpObjHUB.Text = arrNetData(0, 1) 'Process the nodes For i = 1 To UBound(arrNetData) Set mstObj = stnObj.Masters(arrNetData(i, 0)) 'Determine X, Y location for placement (in circle _ around hub) dblRad = (dblDegreeInc * i) * PI / 180 dblX = CircleRadius * Cos(dblRad) + (dblPageWidth / 2) dblY = CircleRadius * Sin(dblRad) + (dblPageHeight / 2) 'Add shape to drawing in proper location Set shpobj = ActivePage.Drop(mstObj, dblX, dblY) 'Set shape text shpobj.Text = arrNetData(i, 1) Next End Sub
Notice the use of page sheet properties to get information about positioning on the page. Shapes are spaced evenly around the hub by using the dblDegreeInc variable to calculate the x- and y- coordinates based on the number of elements in the array.
Note
If you want to find shapes based on whether they are located on or near other shapes, or modify a shape's behavior relative to another shape, you can use these properties: SpatialRelation, SpatialNeighbors, SpatialSearch, DistanceFrom, DistanceFromPoint, and HitTest.
You can use these properties to create shapes that change their settings based on the other shapes they are dropped on or near. For example, in an office layout solution, you could create smart furniture shapes such as a cabinet shape that attaches itself to a wall when dropped next to it, and takes on the style of a cabinet that is located nearby. Or, you can prevent a user from placing a shape near or on certain other shapes, such as not allowing a user to drop a fire extinguisher near any type of heat source in a building layout.
Properties or methods that determine spatial relationships between shapes
Property or method | Description |
SpatialRelation property | Returns an integer that represents the spatial relationship of one shape to another shape. Both shapes must be on the same page or in the same master. Use this property to determine whether one shape contains, is contained by, overlaps, or touches another shape. |
SpatialNeighbors property | Returns a Selection object that represents the shapes that meet certain criteria in relation to a specified shape. Use this property to find a set of shapes that contain, are contained by, overlap, or touch a specific shape. |
SpatialSearch property | Returns a Selection object whose shapes meet certain relationship criteria in relation to a point that is expressed in the coordinate space of a page, master, or group. Use this property to find a set of shapes that contain, are contained by, overlap, or touch a specific coordinate space. |
DistanceFrom property | Returns the distance from one shape to another. Both shapes must be on the same page or in the same master. Use this property to determine if one shape is within a required distance of another shape. |
DistanceFromPoint property | Returns the distance from a shape to a point. Use this property to determine if a coordinate on a shape is within a required distance of a point. |
HitTest property | Determines if a given x,y position hits outside, inside, or on the boundary of a shape. Use this property to check one shape against criteria for a limited set of other shapes. |
The SpatialRelation property of a Shape object returns the relationship between the calling shape and another shape (passed as an argument with the property).
The Microsoft Visual Basic for Applications (VBA) code in the following example assumes the user can drop any shape from the document stencil onto a page with existing shapes. The text of each shape on the page changes to display the relationship between the dropped shape and each shape in the drawing.
sPrivate Sub Document_ShapeAdded(ByVal Shape As IVShape) Dim ShapeOnPage As Shape Dim dblTolerance As Integer Dim iSpatialRelation As VisSpatialRelationCodes Dim strSpatialRelation As String On Error GoTo errHandler 'Try setting different tolerance values dblTolerance = 0.25 For Each ShapeOnPage In ActivePage.Shapes If Shape = ShapeOnPage Then 'The shape being tested is the shape that was added 'Display the shape name Shape.Text = Shape.Name ElseIf ShapeOnPage.Name <> "Abstract" Then 'Get the relation between the added shape, and the 'iterated shape on the page iSpatialRelation = Shape.SpatialRelation(ShapeOnPage, _ dblTolerance, 0) 'Convert return code to text Select Case iSpatialRelation Case VisSpatialRelationCodes.visSpatialContain _ strSpatialRelation = "Contains" Case VisSpatialRelationCodes.visSpatialContainedIn _ strSpatialRelation = "is Contained in" Case VisSpatialRelationCodes.visSpatialOverlap _ strSpatialRelation = "overlaps" Case VisSpatialRelationCodes.visSpatialTouching _ strSpatialRelation = "is touching" Case Else strSpatialRelation = "has no relation with" End Select 'Put relation on shape ShapeOnPage.Text = Shape.Name & " " & _ strSpatialRelation & _ " " & ShapeOnPage.Name End If Next errHandler: End Sub
You can use the SpatialNeighbors property of a Shape object in your program to determine the relationship between the shape of interest and the rest of the shapes on the page. The property returns the set of shapes (as a Selection object) that meet the criteria set by the arguments passed with the property. For details about using the Selection object, see Working with Selected Shapes later in this chapter.
The VBA code in this example assumes the user will drop a shape within the boundaries of a square, circle, or both, already provided on the drawing page. The text of the dropped shape displays the results of the property being called.
Private Sub Document_ShapeAdded(ByVal Shape As IVShape) Dim ShapeOnPage As Shape Dim dblTolerance As Integer Dim ReturnedSelection As Selection Dim strSpatialRelation As String Dim iSpatialRelation As VisSpatialRelationCodes On Error GoTo errHandler strSpatialRelation = "" 'Try setting different tolerance values dblTolerance = 0# 'Try setting different spatial relationships iSpatialRelation = visSpatialContainedIn Const ISRELATED = " is contained by " Const ISNOTRELATED = " is not contained." 'Get the set of spatially related shapes 'that meet the criteria set by the parameters Set ReturnedSelection = _ Shape.SpatialNeighbors(iSpatialRelation, dblTolerance, 0) 'Evaluate the results If ReturnedSelection.Count = 0 Then 'No shapes met the criteria set by the 'parameters of the method strSpatialRelation = Shape.Name & ISNOTRELATED Else 'Build the positive result string For Each ShapeOnPage In ReturnedSelection strSpatialRelation = strSpatialRelation & Shape.Name _ & ISRELATED & ShapeOnPage.Name & Chr$(10) Next End If 'Put the results on the added shape Shape.Text = strSpatialRelation errHandler: End Sub
The VBA code in this example demonstrates the DistanceFrom and DistanceFromPoint properties of a Shape object. These properties are used to determine the distance from the calling shape, and a shape passed as an argument with the property. Use DistanceFromPoint to determine the distance from a specific point on the shape.
The VBA code in this example expects the user to drop various shapes onto the page at various distances from a base shape. A message box appears to inform the user if the dropped shapes are too close to the base shape. The minimum acceptable distance is defined by dblMinimumDistance.
To test this example, paste the following code in the ThisDocument code window of a new drawing, save the drawing and close it, reopen the drawing, and then drop shapes onto the page.
Public objBaseShape As Visio.Shape Public Function MeetsClearanceRequirements(BaseShape As Shape, _ ShapeToCheck As Shape, dblMinimumDistance As Double) As Boolean Dim bRetVal As Boolean On Error GoTo errHandler 'Check distance from specific point 'Use the DistanceFromPoint property dblDistance = ShapeToCheck.DistanceFromPoint(BaseShape.Cells("PinX"), _ BaseShape.Cells("PinY"), 0) 'To check distance from closest points use DistanceFrom property: 'dblDistance = ShapeToCheck.DistanceFrom(BaseShape,0) If dblDistance < dblMinimumDistance Then bRetVal = False Else bRetVal = True End If MeetsClearanceRequirements = bRetVal Exit Function errHandler: bRetVal = False End Function Private Sub Document_DocumentOpened(ByVal doc As IVDocument) 'Draw a rectangle on the page; this will be the 'shape that all added shapes are compared to Set objBaseShape = ActivePage.DrawRectangle(3, 7, 4, 6) End Sub Private Sub Document_ShapeAdded(ByVal Shape As IVShape) Dim strMsg As String Dim dblMinimumDistance As Double dblMinimumDistance = 2 'Because the objBaseShape is set when the document is opened, 'attempt to compare shapes on different pages If objBaseShape.Parent = Shape.Parent And (objBaseShape <> _ Shape) Then If MeetsClearanceRequirements(objBaseShape, _ Shape, dblMinimumDistance) Then _ strMsg = "Distance requirements met" Else strMsg = "Too close" End If 'Display message MsgBox strMsg Else 'Do nothing, shapes are on a different page, or 'BaseShape = Added Shape End If End Sub