Working with connected diagrams programmatically

(This blog post is the second part in a series of two blog posts. The first blog post was published to the Visio Insights blog Working with Connected Diagrams Programmatically, part 1.)

Visio 2010 provides a wealth of features for working with connected diagrams (that is, a diagram with shapes that are connected by connector shapes).

In addition, Visio 2010 includes several APIs for moving through a connected diagram programmatically. The Visio 2010 API contains new methods that let developers manipulate and traverse connected diagrams or graphs at a higher level of abstraction. In particular, these APIs make it much easier for Visio developers to crawl across a drawing to find shapes or alter the structure of the diagram.

If you want to see all of the new connectivity APIs, see this post on the Visio Insights blog.

There are two APIs in particular - the Shape.ConnectedShapes method and the Page.SplitConnector method - that allow you to navigate across a connected diagram and add content to the diagram without changing the underlying structure. The first method, Shape.ConnectedShapes, returns an array of IDs of the shapes that are connected to the shape in the diagram. By using this API and a "walking the tree" design pattern, you can traverse an entire connected diagram programmatically. (See the first post in this series on the Visio Insights blog if you'd like to read more about this method.)

With the Page.SplitConnector API, you can add shapes to the diagram while leaving the diagram connected. The method allows you to drop a new shape between two connected shapes, where the connector between the two original shapes is split into two.

For example, say you have two shapes connected like so in your diagram:

Imagine that you want to add some step between the Decision shape and the Process shape, like an auditing step. With the Page.SplitConnector method, you can add a shape between the two connected shapes:

With one line of code - Page.SplitConnector(ConnectorToSplit As Visio.Shape, Shape As Visio.Shape) - you can insert additional shapes into the drawing without having to break apart connectors and reconnect them again afterward.

Here's some code that will crawl over a connected diagram, examine each shape, and for each process shape, add a new "auditing" process shape right after it. If you paste the VBA into the Visual Basic Editor, you'll want to call the TraverseFlowchart macro to begin execution.

Also note that the code specifies a shape named "Start/End" as the starting shape and uses the "Process" shape from the "Basic Flowchart Shapes (US)" stencil as the shape to insert into the diagram. You can easily alter this code to specify the starting shape and insertion shape that you want.

VBA code | Change to C# code (Must have JavaScript enabled in your browser.)

Dim vsoStencil As Visio.Document
Dim vsoPage As Visio.Page
Dim vsoMaster As Visio.Master
Dim shapeList

Sub TraverseFlowchart()
    Dim vsoShape As Visio.shape
    Dim startShapeName As String
    startShapeName = "Start/End"
    Set vsoPage = Application.ActivePage
    ' Open the stencil with the master shape that will be used
    ' to split the connectors.
    Set vsoStencil = Application.Documents("BASFLO_U.VSS")
    Set vsoMaster = vsoStencil.Masters("Process")
    Set vsoShape = vsoPage.Shapes(startShapeName)

    ' Create a dictionary to keep track of the shapes that we've
    ' already crawled.
    Set shapeList = CreateObject("Scripting.Dictionary")
    shapeList.Add vsoShape.ID, vsoShape.Name

    GetConnectedShapes vsoShape

    ' Close the stencil when we're done with it.
End Sub

Sub GetConnectedShapes(shape As Visio.shape)
    Dim vsoShape As Visio.shape
    Dim shapeIDArray As Variant
    Dim shapeIDArrayNext As Variant
    Dim outgoingNodes As Integer
    Dim node As Integer
    ' Get the IDs of all of the shapes that this shape is connected to.
    shapeIDArray = shape.ConnectedShapes(visConnectedShapesOutgoingNodes, "")
    If (UBound(shapeIDArray) >= 0) Then
        outgoingNodes = UBound(shapeIDArray)
        For node = 0 To outgoingNodes

            Set vsoShape = vsoPage.Shapes.ItemFromID(shapeIDArray(node))

            ' If the shape object passed into the sub is a process shape, then
            ' we want to split each connector leaving it.
            If (InStr(1, shape.Name, "Process") > 0) Then
                SplitConnectorWithShape shape
            End If
            ' We need to see if the vsoShape that this shape is connected to has any
            ' out-going nodes, so we'll get the shapes connected to it.
            shapeIDArrayNext = vsoShape.ConnectedShapes(visConnectedShapesOutgoingNodes, "")
            Dim beenChecked As Boolean
            beenChecked = shapeList.Exists(vsoShape.ID)
            If ((UBound(shapeIDArrayNext) >= 0) And Not beenChecked) Then
                shapeList.Add vsoShape.ID, vsoShape.Name
                GetConnectedShapes vsoShape

            End If
        Next node
    End If
End Sub

Sub SplitConnectorWithShape(shape As Visio.shape)
    Dim vsoShape As Visio.shape
    Dim vsoConnectorResult As Visio.shape
    Dim vsoConnectorOutgoing As Visio.shape
    Dim shapeIDs As Variant
    Dim shapeID As Integer
    ' We add a new shape to the drawing using the global vsoMaster object.
    ' It doesn't matter where it is dropped because we will re-layout the page
    ' at the end of this sub-routine.
    Set vsoShape = vsoPage.Drop(vsoMaster, 1, 1)
    vsoShape.Text = "Audit process: '" & shape.Text & "'"
    ' Add this new shape to the shapeList so it doesn't get crawled.
    shapeList.Add vsoShape.ID, vsoShape.Name

    ' Need to get all of the out-going connector shapes from this shape
    ' so that we can split each one. We also need to make sure that it hasn't
    ' already been split.
    shapeIDs = shape.GluedShapes(visGluedShapesOutgoing1D, "")    
    Dim count As Integer
    For count = 0 To UBound(shapeIDs)
        If (Not shapeList.Exists(shapeIDs(count))) Then
            shapeID = shapeIDs(count)
            Exit For
        End If
    Next count
    ' We split the connector and then keep track of the resulting
    ' connector created by the split.
    Set vsoConnectorOutgoing = vsoPage.Shapes.ItemFromID(shapeID)
    Set vsoConnectorResult = vsoPage.SplitConnector( _
        vsoPage.Shapes.ItemFromID(shapeID), _
    ' We don't want to get trapped in a loop re-splitting these connectors,
    ' so we add them both to the shapeList dictionary.
    shapeList.Add vsoConnectorOutgoing.ID, vsoConnectorOutgoing.Name
    shapeList.Add vsoConnectorResult.ID, vsoConnectorResult.Name

End Sub

Skip to main content