Share:

" /> Visio Guy » Connect All Shapes to Each Other
Home » Code, Development

Connect All Shapes to Each Other

Submitted by on March 17, 2008 – 4:59 pm | | 34230 views 21 Comments

Read Full ArticleIf you’re a developer, looking to create Visio-based solutions, then you’re going to need to programmatically connect Visio shapes together. Wouldn’t be nice to have a snippet or two to start from?

Today’s post shows you VB and C# code that instructs Visio to connect every shape to every other shape, and to create a beautiful mess, like that shown in the image at left.

Now, I strongly recommend the Visio 2007 SDK. It is an excellent, free download, and it has lots of snippets, utilities, sample code and sample applications, including examples of how to connect Visio shapes with code.

Nevertheless, I thought I’d address a particular newsgroup question that I encountered some time ago, which was:

“How can I automatically connect every shape on a page to every other shape?”

This seemed like an interesting example to me, and one that might appeal to folks who are creating network-related Visio-based solutions. And even if it’s not exactly the code you need, it’ll surely be a good place to start from.

In addition to the newsgroup query, I was moved to write this article by a YouTube video that a few whacky guys at Microsoft put together, called: MEDC 2007 Worst Practices, or according to Public Speaking for Geeks: The Best Bad Tech Speakers Video…Ever. Have a gander at some truly nightmarish Visio network diagrams, just after the halfway point…

..

Features

While the code isn’t complicated, it’s not super-simple and short. That’s because I added a few features to show off some of the things you can do with Visio automation. Among these are:

  • Setting Layout & Routing settings for the page
  • Creating and Undo Scope, so your users can undo all of the changes made by the code with one press of Ctrl + Z
  • Rudimentary filtering of shapes by Shape.Type
  • Connecting shapes via two methods: the newer Visio 2007 AutoConnect method, or the older GlueTo method

Hopefully, you’ll find the code well-commented, but let’s just run over each feature quickly:

Layout & Routing

Code for the Layout and Routing settings can be found in the procedure: m_setPageLayoutSettings. Here, the connector style for the page is set so that the connectors are straight, and proceed from center of one shape to the center of the other. Also, the Jump Style for intersecting lines is set to Gap, as shown here:

Connect All Shapes to Each Other - Connector Gaps

Undo Scope

The Undo Scope code is found in the main procedure. When the code is finished running, you’ll see a named operation Undo Connect All Shapes to Each Other in the Edit menu. This makes things a lot easier, and more clear for your users.

Connect All Shapes to Each Other - Undo Scope

The shape filtering in m_getShapesToConnect demonstrates the use of the Shape.Type property. With this information, we can decide which shapes don’t need connecting. In our sample code, ignored shapes include: Foreign Objects, 1D shapes, or Guides.

Shape Filtering

Ignoring Foreign Objects, allows us to place a VB Button on the page that calls the VBA code when clicked, but the button won’t end up with connections being glued to it.

1D shapes are most likely connectors, and you won’t want to connect connectors to other connectors!

Guides are just visual aids, and are so uninteresting, they don’t even print! <wink wink>

Two Methods for Connecting

m_connectShapes illustrates how to determine which version of Visio you are running, and then two different ways to connect shapes together.

Just before finishing this article, I realized that if you’re running a version earlier than Visio 2007, you might have to comment out a portion of the code, because it won’t properly compile — the Visio 12 method: AutoConnect doesn’t exist in Visio 2003. See the procedure for more notes.

VBA code

Below is the VBA code to get the job done. If you want to use VBA, you can copy the code and directly paste it into the ThisDocument module of any document’s VBA project,.

The code is easily converted to VB.NET. However, you’ll need to do the following:

  1. Add a reference to the Visio 12.0 Type Library (or Visio 11.0, or…)
  2. Add the line: Imports Visio = Microsoft.Office.Interop.Visio.

To connect the shapes, simply run the ConnectAllShapes subroutine.

Edit: 2014.02.14 I’ve added “Set” before object assignments in the code below, plus some clean-up code. The absence of “Set” statements was causing confusion. You don’t ned “Set” in VB.NET, but you do in VB6 or VBA.

Public Sub ConnectAllShapes()

 On Error GoTo Err

 '// Create an undo-scope, so that we can undo all the
 '// connections with just one Ctrl + Z:
 Dim UndoID As Long
 UndoID = Visio.Application.BeginUndoScope("Connect All Shapes to Each Other")

 '// This is where we really get the connecting done:
 '// Get a Visio Page object:
 Dim pg As Visio.Page
 Set pg = Visio.ActivePage

 '// Connect all shapes on the page:
 Call m_ConnectAllShapes(pg)
 Call Visio.Application.EndUndoScope(UndoID, True)

 Set pg = Nothing

 Exit Sub

Err:
 Call Visio.Application.EndUndoScope(UndoID, False)
 Set pg = Nothing
End Sub

Private Sub m_ConnectAllShapes(ByRef visPg As Visio.Page)

 Dim shpFrom As Visio.Shape
 Dim shpTo As Visio.Shape
 Dim shpConn As Visio.Shape
 Dim shp As Visio.Shape
 Dim collShapes As Collection
 Dim i As Integer, j As Integer

 '// Set the page-layout settings for routing-style,
 '// jump-style, etc.
 Call m_setPageLayoutSettings(visPg)

 '// Add all the non-connector shapes to a VB collection:
 Set collShapes = m_getShapesToConnect(visPg)

 '// Loop through the shapes in the shapes collection:
 For i = 1 To collShapes.Count

 Set shpFrom = collShapes.Item(i)

 '// Connect to all the other shapes:
 For j = i + 1 To collShapes.Count

 Set shpTo = collShapes.Item(j)

 Call m_connectShapes(shpFrom, shpTo)

 Next j

 Next i

 Set shpFrom = Nothing
 Set shpTo = Nothing
 Set shpConn = Nothing
 Set shp = Nothing
 Set collShapes = Nothing

End Sub

Private Sub m_connectShapes(ByRef shpFrom As Visio.Shape, ByRef shpTo As Visio.Shape)

 '// Visio 2007 introduced a new method for connection
 '// shapes. This proc looks at the Visio version and
 '// decides whether to use the old way or the new way.

 Dim pg As Visio.Page
 Set pg = shpFrom.ContainingPage

 '// Note: if you're not running Visio 2007, this might not
 '// even compile -- you'll have to comment-out the first part
 '// of the If-Then block...
 If ( pg.Application.Version = "12.0" ) Then

 Call shpFrom.AutoConnect(shpTo, visAutoConnectDirNone)

 Else

 '// Drop the built-in connector object somewhere on the page:
 Set shpConn = pg.Drop(pg.Application.ConnectorToolDataObject, 0, 0)

 '// Connect its Begin to the 'From' shape:
 Call shpConn.CellsU("BeginX").GlueTo(shpFrom.CellsU("PinX"))

 '// Connect its End to the 'To' shape:
 Call shpConn.CellsU("EndX").GlueTo(shpTo.CellsU("PinX"))

 End If

 Set pg = Nothing

End Sub

Private Function m_getShapesToConnect(ByRef visPg As Visio.Page) As Collection

 Dim shp As Visio.Shape
 Dim collShapes As Collection

 Set collShapes = New Collection

 '// For this example, we will get all shapes on the page
 '// that ARE NOT of these:
 '//
 '//  1. Connectors
 '//  2. Foreign objects (like Buttons)
 '//  3. Guides

 For Each shp In visPg.Shapes

 If (shp.OneD = False) And _
 (shp.Type <> Visio.VisShapeTypes.visTypeForeignObject) And _
 (shp.Type <> Visio.VisShapeTypes.visTypeGuide) Then

 Call collShapes.Add(shp)

 End If

 Next

 Set m_getShapesToConnect = collShapes
 Set shp = Nothing
 Set collShapes = Nothing

End Function

Private Sub m_setPageLayoutSettings(ByRef visPg As Visio.Page)

 '// We can set layout and routing options for the page by
 '// accessing the ShapeSheet for the page, and setting cells
 '// in the Page Layout section.
 '//
 '// You can see the PageSheet by deselecting all shapes on the
 '// page, and choosing Window > Show ShapeSheet.
 '// Set page routing style to center-to-center:
 visPg.PageSheet.CellsSRC(Visio.VisSectionIndices.visSectionObject, _
 Visio.VisRowIndices.visRowPageLayout, _
 visPLORouteStyle).ResultIUForce = 16

 '// Set to connector intersection to 'gap':
 visPg.PageSheet.CellsSRC(Visio.VisSectionIndices.visSectionObject, _
 Visio.VisRowIndices.visRowPageLayout, _
 Visio.VisCellIndices.visPLOJumpStyle).ResultIUForce = 2

 '// Note: another way to access the PageSheet cells is by name, ie:
 '// visPg.PageSheet.Cells("RouteStyle").ResultIU = 16
 '// visPg.PageSheet.Cells("LineJumpStyle").ResultIU = 2

End Sub

C# Code

I created a partial class for a Windows Form, so that it was easy to call the code via a button click. You might have to mess with the namespace and class name to get it to work, but it should just be a few lines.

You’ll also have add a reference to the Visio 12.0 Type Library (or Visio 11.0, or…) in order to get the “using Visio…” line to work.

To get the ball rolling, make sure you have an instance of Visio running. There should be a document open that has some shapes that you don’t really care about. Then, just call the m_connectAllShapes proc and the connections will appear!


using System.Collections.Generic;
using Visio = Microsoft.Office.Interop.Visio;

namespace ConnectAllShapes
{
  partial class Form1
  {
    private const string VisioAppID = "visio.application";

    private void m_connectAllShapes()
    {
      // First, find a running instance of Visio:
      Visio.Application visApp = m_getVisio();

      if (visApp == null)
      {
        System.Console.WriteLine(
               "Couldn't find a running instance of Visio!");
        return;
      }

       int UndoID = -1;

       try
       {
         // Create an undo-scope, so that we can undo all the
         // connections with just one Ctrl + Z:
         UndoID = visApp.BeginUndoScope("Connect All Shapes to Each Other");

         // This is where we really get the connecting done:
         // Get a Visio Page object:
         Visio.Page pg = visApp.ActivePage;

         // Connect all shapes on the page:
         m_connect(pg);

        visApp.EndUndoScope(UndoID, true);
       }
       catch (System.Exception ex)
       {
          // Try to close the undo scope, but reject the changes:
          if ( (UndoID == -1) && visApp != null)
          {
            visApp.EndUndoScope(UndoID, false);
          }
          System.Console.WriteLine(
                  "An error occurred!\n\n" + ex.Message);
       }
     }

     private void m_connect(Visio.Page visPg )
     {
       Visio.Shape shpFrom, shpTo;
       List collShapes;

       // Set the page-layout settings for routing-style,
       // jump-style, etc.
       m_setPageLayoutSettings(visPg);

       // Add all the non-connector shapes to a VB collection
       collShapes = m_getShapesToConnect(visPg);

       // Loop through the shapes in the shapes collection --
       // connect the ith shape to each jth shape, so to speak:
       for(int i = 0; i < collShapes.Count; i++)
       {
         shpFrom = collShapes[i];

         // Connect to all the other shapes:
         for (int j = i + 1; j < collShapes.Count; j++)
         {
           shpTo = collShapes[j];
           m_connectShapes(shpFrom, shpTo);
         }
       }
     }

   private void m_connectShapes(Visio.Shape shpFrom, Visio.Shape shpTo )
   {
     // Visio 2007 introduced a new method for connection
     // shapes. This proc looks at the Visio version and
     // decides whether to use the old way or the new way.

      Visio.Page pg = shpFrom.ContainingPage;

     // Note: if you're not running Visio 2007, this might not
     // even compile -- you'll have to comment-out the first part
     // of the If-Then block...
     if (string.Compare(pg.Application.Version, "12.0", true) == 0)
     {
       shpFrom.AutoConnect(shpTo,
       (short)Visio.VisAutoConnectDir.visAutoConnectDirNone, null);
     }
     else
     {
       // Drop the built-in connector object somewhere on the page:
       Visio.Shape shpConn;
       shpConn = pg.Drop(pg.Application.ConnectorToolDataObject, 0, 0);

       // Connect its Begin to the 'From' shape:
       shpConn.get_CellsU("BeginX").GlueTo(shpFrom.get_CellsU("PinX"));

       // Connect its End to the 'To' shape:
       shpConn.get_CellsU("EndX").GlueTo(shpTo.get_CellsU("PinX"));
     }
   }

 private List  m_getShapesToConnect(Visio.Page visPg )
 {
   List collShapes = new List();

   // For this example, we will get all shapes on the page
   // that ARE NOT of these:
   //
   //  1. Connectors
   //  2. Foreign objects (like Buttons)
   //  3. Guides

   foreach (Visio.Shape shp in visPg.Shapes)
   {
     if ( (shp.OneD == 0 ) &&
          (shp.Type != (short)Visio.VisShapeTypes.visTypeForeignObject) &&
          (shp.Type != (short)Visio.VisShapeTypes.visTypeGuide) )
     {
       collShapes.Add(shp);
     }
   }
   return collShapes;
 }

 private Visio.Application m_getVisio()
   {
   Visio.Application visApp;
   object objVis;

   objVis = System.Runtime.InteropServices.

   Marshal.GetActiveObject(VisioAppID);
   visApp = (Visio.Application)objVis;

   return visApp;
 }

 private void m_setPageLayoutSettings(Visio.Page visPg)
 {
   // We can set layout and routing options for the page by
   // accessing the ShapeSheet for the page, and setting cells
   // in the Page Layout section.
   //
   // You can see the PageSheet by deselecting all shapes on the
   // page, and choosing Window > Show ShapeSheet.
   // Set page routing style to center-to-center:

   visPg.PageSheet.get_CellsSRC(
           (short)Visio.VisSectionIndices.visSectionObject,
           (short)Visio.VisRowIndices.visRowPageLayout,
           (short)Visio.VisCellIndices.visPLORouteStyle).ResultIUForce = 16;

   // Set to connector intersection to 'gap':
   visPg.PageSheet.get_CellsSRC(
           (short)Visio.VisSectionIndices.visSectionObject,
           (short)Visio.VisRowIndices.visRowPageLayout,
           (short)Visio.VisCellIndices.visPLOJumpStyle).ResultIUForce = 2;

   // Note: another way to access the PageSheet cells is by name, ie:
   //visPg.PageSheet.get_Cells("RouteStyle").ResultIU = 16;
   //visPg.PageSheet.get_Cells("LineJumpStyle").ResultIU = 2;
 }
}

…More comments after the C# code…

21 Comments »

  • Art Braune says:

    Wow – this is the weirdest Visio request and solution I have seen so far…

  • Visio Guy says:

    Hi Art,

    Well storage is cheap these days…

    …and like I mentioned above, it might be a good piece of code to start with.

    A Visio developer could modify the shape-filtering in m_shapesToConnect to do something a bit more useful – like connect all Routers together or something.

    - Chris

  • gabriel says:

    Hi

    I have never used VBeditor before and I am trying to use your script. I was able to insert the script in the Visual Basic Editor through Visio 2007 but then I am not sure what to do after that. I chose the macro after this step but it didnt do anything. Can you explain to this dummy how to get it going?

    thanks!

  • Visio Guy says:

    Hi Gabriel,

    Well, that’s a big subject to cover, but I can point you to some articles that might help:

    John Goldsmith’s visLog has a nice introductory article to get you going on Visio’s ShapeSheet and VBA – Visual Basic for Applications – coding. See this article: Just for starters

    Also, a few articles on Visio Guy will be of help:
    VBA Macro Security
    Run VBA Code When Documents Open

  • gabriel says:

    hi

    i looked over the resources but still havent figured it out. I am not looking to learn vba, I just want to run this one script. Do I need to change variables in your script? Like since I’ve named my pages should I change the “pg” in “Call m_ConnectAllShapes(pg)” to the name of one of my pages?

    help?
    gabriel

  • Visio Guy says:

    Hi Gabriel

    You can get a page a number of ways:

    dim pg as Visio.Page
    Set pg = Visio.ActiveDocument.Pages.Item(3) ‘…index
    Set pg = Visio.ActiveDocument.Pages.Item(“Page-1″) ‘…name

    You can loop through all pages like this:

    For Each pg in doc.Pages

    Next pg

    - Chris

  • gabriel says:

    are there any other variables that need to be changed?

    i just want to connect my shapes to each other…

  • Thanks Chris, this did the trick.
    Used some parts of you code to connect shapes.
    Some small changes, and now it connects using the connection points instead of the shape itself.

    - Lars

  • Visio Guy says:

    Lars-Erik is BACK!

    I am glad you figured out something useful to do with this, and that you realized it was just supposed to be sample code for instructional purposes.

    Some people seemed to think the example itself was supposed to be for real-world situations! (Hehehe I’m just teasing Art Braune!)

    If people are wondering how to glue to specific connection points, then here’s some more info.

    Say we’ve got two shapes. One has a connection point named “out” and the other has a connection point named “in”. The cell names for these points would look like:

    Shape 1
    Connections.out (or “.out.x”)
    Connections.out.y

    Shape 2
    Connections.in (or “.in.x”)
    Connections.in.y

    You can glue a connector to the specific points by using these cell names:

    // Connect its Begin to the ‘From’ shape: shpConn.get_CellsU(“BeginX”).GlueTo(shpFrom.get_CellsU(“Connections.out”));

    // Connect its End to the ‘To’ shape: shpConn.get_CellsU(“EndX”).GlueTo(shpTo.get_CellsU(“Connections.in”));

  • Mia says:

    Hy!

    I am new in VISIO and in VBA in VISIO. That sounds like a solution for my problem but I need to ask you a question. Does that also work if I have an Excel Sheet behind and just want to connect the shapes with the value true from the excel sheet?

    How would that look like?

  • kevin says:

    Hiya,

    First congrats on all your stuff.

    When I copy and paste the code (using visio 14) it does nothing.

    It fails on the

    pg = Visio.ActivePage

    line with a

    Object variable or With block variable not set

    error…..

  • Visio Guy says:

    Hi Kevin,

    It needs to be:

    Set pg = Visio.ActivePage

    I’ll go and correct the code in the article.

  • Rajashekar says:

    hi…
    can anyone suggest how to add connection points to visio shapes in visio 2013 using c#;

  • Calin C. says:

    Hello,

    The VB code should work also in Visio 2010?
    I’m asking because when it runs I get an error Compile error: Argument not optional and the collShapes = under ‘// Add all the non-connector shape… gets highlighted.

    I would appreciate any hint on this.

    Thanks!

  • kevin says:

    yes i added that set command – there are many that means it doesn’t work……

  • Visio Guy says:

    I probably cut and pasted from VB.NET. If you are using VBA, then you’ll need to put “Set” in front of object assignments. For example:

    Set collShapes = m_getShapesToConnect(visPg)

  • kevin says:

    I think you need to change the text in the article then becasue its misleading…

    Below is the VBA code to get the job done. You can copy the code and directly paste it into the ThisDocument module of any document’s VBA project.

    The code is easily converted to VB.NET. However, you’ll need to do the following:

    Add a reference to the Visio 12.0 Type Library (or Visio 11.0, or…)
    Add the line: Imports Visio = Microsoft.Office.Interop.Visio.

  • Calin C. says:

    Hello all,

    I want to say a big THANK YOU! for this piece of code. The VBA code require some “set” to be added, but finally it works.

    I needed to connect 72 shapes in an any-to-any manner to reflect the functionality of a network tunnels. So, you can imagine what this would mean to do it manually.

    I did achieve using this code and another one (search for polar array vba) the following image :)

    https://drive.google.com/file/d/0BwRKmY-AxWA3QXdEZkowYXZHT28/edit?usp=sharing

  • Visio Guy says:

    Calin, thanks for sharing the image link, very cool!

  • Visio Guy says:

    Ok folks, thanks for chiming in. I’ve added the “Set” statements to the VBA code, so it will actually work in VBA. Let me know if I missed any!

  • Rod Visio says:

    So great! Ok with 2013 version.
    Thanks for codes,

Leave a comment!

Add your comment below, or trackback from your own site. You can also subscribe to these comments via RSS.

Be nice. Keep it clean. Stay on topic. No spam.

You can use these tags:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

This is a Gravatar-enabled weblog. To get your own globally-recognized-avatar, please register at Gravatar.

*