PowerShell And Visio Part 5 – Connections (updated)

It’s been a while since the last post.  I decided that if I had to chose between writing PowerShell and writing about PowerShell, I should favor the former.

In this episode, I’ll talk about how to create connections between objects in a Visio diagram.  Turns out it’s not really that hard (just like most things with Visio so far), but there some interesting things we can do with the connections once they exist.

So, to start with, let’s get a some shapes on a page.  If this doesn’t look familiar, refer back to the earlier posts in this series here:

 

$visio=New-Object -ComObject Visio.Application
$Document=$Visio.Documents.Add('')
$rect1=$Visio.ActivePage.DrawRectangle(1,1,2,2)
$rect2=$Visio.ActivePage.DrawRectangle(4,4,5,5)

Here’s what that got us:
2Rectangles

Now, we have 2 shapes ($rect1 and $rect2). To connect them, we’ll use the AutoConnect() method on one of the 2 shapes. Since Visio connectors are directional (that is, they have a start and an end), it matters which one we use.

$rect1.AutoConnect($rect2,0)

The AutoConnect() method takes three arguments:

  1. The shape you want to connect to
  2. The direction (relative to the first shape) you want to move the connected shape to (0 means don’t move the second shape)
  3. The shape you want to use as a connector

In this example, we chose to not move the second shape, and used the default connector.

The result looks like this:
ConnectedRectangles

There are several interesting things about this connector:

  1. It doesn’t indicate direction (you can’t tell which way the connector goes)
  2. It’s dynamic (it will move/route as you move the two shapes)
  3. We didn’t get a reference to it as a result of the AutoConnect() method

We’ll start by addressing the last point. Since we didn’t get a reference to the connector, how can we find it? It turns out that just about everything in Visio is a shape, so we can look at the Shapes collection on the page:

$Visio.ActivePage.Shapes | Select-Object Name

In the output, you will see (at the end) a shape called “Dynamic connector”. That’s our new connector (duh).

$connector = $Visio.ActivePage.Shapes['Dynamic connector'] | select -Last 1

I picked the last one in the collection in case there were others. Visio adds to this collection as shapes are added to the list, so this should be safe.

Now that we’ve got a reference to the connector, how can we designate which end is which? In the Visio GUI, we would click the Line drop-down on the ribbon and select the arrows (or dots) that we wanted. In PowerShell, it’s a different story altogether.

First of all, there’s not an Arrows property that we can use. That would be too easy. It turns out that in Visio, everything has what’s called a ShapeSheet, which is kind of like a bunch of little spreadsheets that control every aspect of how the object behaves in Visio. Accessing cells in the ShapeSheet can be done several ways, but typically you either use the Section, Row, Column reference (SRC) or a special Named Cell. These references look something like this:

$Shape.CellsSRC($section,$row,$column) 

and

$Shape.Cells('CellName')

To set the ending arrow for a connector, there’s a “named cell” called EndArrow which tells Visio what symbol to draw at the end of the connector. We’re going to set the cell to 4. The number can apparently be anywhere from 1-45 to display different arrows.

$connector.Cells('EndArrow')=4

And the resulting arrow (zoomed in so you can see it):
Arrow

If we wanted the arrow to be bi-directional, we could also set the “BeginArrow” cell.

$connector.Cells('BeginArrow')=4

BeginArrow

Ok. Now for #2. Can we change how the connector routes? I often like to use straight lines because they’re easier to follow. Change the connector to be straight, we have to resort to the ShapeSheet again. However, there’s no named cell this time (see P.S…I found names). Now, we have to know the SRC to where the cell is. In fact, for this there are 2 cells that we have to set, so we need to know both SRCs.

 $connector.CellsSRC(1,23,10) = 16
 $connector.CellsSRC(1,23,19) = 1 

That’s pretty cryptic, right? Absolutely. It turns out with Visio, there are about 40 bajillion different constants to use. This link will help you with some, but they aren’t organized much. This page is organized a little better, but it’s still hard to know where to look. The constants in this example are:

  • visSectionObject = 1
  • visRowShapeLayout = 23
  • visSLORouteStyle = 10
  • visLORouteCenterToCenter = 16
  • visSLOLineRouteExt = 19
  • visLORouteExtStraight = 1

So that’s a mess (at least to me). How in the world do we figure these out? The easiest way is to record a macro and do what you want to find out about, then look at the VBA. The VBA code for setting a connector to a straight line looks like this:

Application.ActiveWindow.Page.Shapes.ItemFromID(3).CellsSRC(visSectionObject, visRowShapeLayout, visSLOLineRouteExt).FormulaU = "1"
Application.ActiveWindow.Page.Shapes.ItemFromID(3).CellsSRC(visSectionObject, visRowShapeLayout, visSLORouteStyle).FormulaU = "16"

That, with some googling, can get you what you need. I’m working on a better solution, but it’s not ready quite yet. 🙂

That’s enough about connectors.

Let me know what you think in the comments (or on Twitter)

Mike
@MikeShepard70

P.S. Found the “named cells” for making straight lines: ShapeRouteStyle and ConLineRouteExt.

10 Comments

  1. Hi all

    I’m trying to change the page layout to radial and I have checked the VBA code in the macro

    Dim DiagramServices As Integer
    DiagramServices = ActiveDocument.DiagramServicesEnabled
    ActiveDocument.DiagramServicesEnabled = visServiceVersion140 + visServiceVersion150

    Dim UndoScopeID1 As Long
    UndoScopeID1 = Application.BeginUndoScope(“Lay Out Shapes”)
    Application.ActiveWindow.Page.PageSheet.CellsSRC(visSectionObject, visRowPageLayout, visPLOPlaceStyle).FormulaForceU = “6”
    Application.ActiveWindow.Page.PageSheet.CellsSRC(visSectionObject, visRowPageLayout, visPLORouteStyle).FormulaForceU = “16”
    Application.ActiveWindow.Page.Layout
    Application.EndUndoScope UndoScopeID1, True

    ‘Restore diagram services
    ActiveDocument.DiagramServicesEnabled = DiagramServices

    I must admit I am completely baffled as to how to enable this form powershell. I would really appreciate any advice that you guys have on this.

  2. So first of all, “Application” is the reference to the Visio instance, which we’ve been calling $Visio. ActiveDocument is a property of $Visio, so that should be easy. The rest of the fun is decoding the “vis” constants.

    The DiagramServicesEnabled constants can be found here: https://msdn.microsoft.com/en-us/library/office/ff765437.aspx

    It looks like the first CellsSRC call can be replaced with the named cell “PlaceStyle”. 6 seems to be “circular” here: https://msdn.microsoft.com/en-us/library/office/aa221274(v=office.11).aspx

    The second CellsSRC call is for the named cell “RouteStyle”, and 16 means “center-to-center” – https://msdn.microsoft.com/en-us/library/office/ff765968.aspx

    That’s most of the fun. If you need more help, let me know.

  3. hi mike

    thanks for your prompt reply. I trolled through the reference sheets and managed to also determine that I need to set routestyle and placestyle to 6 and 16. my problem is I am unsure how to do so, i keep getting errors. I have tried

    #$pages.CellSRC(1,24,8)=6
    #$pages.pagelayout.Cells(‘RouteStyle’)=16

    should it be $Visio.CellsSRC(1,24,8)=16 or
    or $Visio.Cells(‘RouteStyle’)=16 and similar for the placestyle

    thanks for a great blog. you are a lifesaver !

  4. Try something like this:

    $page=$Visio.ActivePage
    $page.Cells(‘RouteStyle’)=16

    I’m thinking you’re looking for $Page.Layout() as well.

    • i have tried this but I seem to always get the error or simialr below

      You cannot call a method on a null-valued expression.
      At C:\bv5.ps1:209 char:1
      + $mypage.Cells(‘RouteStyle’)=16
      + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      + CategoryInfo : InvalidOperation: (:) [], RuntimeException
      + FullyQualifiedErrorId : InvokeMethodOnNull

  5. That’s what I get for typing stuff in without trying.

    Cells() is a method on shapes. Pages aren’t (quite) shapes. They have a Shapes collection, and in the Shapes collection there’s a “meta-shape” called “ThePage” which is the shapesheet for the page.

    So this code actually executes (assuming you’ve got $Visio set up and there’s an active document):

    $page=$visio.ActivePage
    $page.Shapes(‘ThePage’).Cells(‘RouteStyle’)=16
    $page.Shapes(‘ThePage’).Cells(‘PlaceStyle’)=6

      • Thanks for the help mike. How to manipulate visio in this manner via powershell was always a gray area to me. Your blog really helped improve my understanding. You rock

  6. think i spoke too soon. now i have this to deal with. I wonder if it might be a visio version issue ?

    Method invocation failed because [Microsoft.Office.Interop.Visio.PageClass] does not contain a method named ‘Shapes’.
    At C:\Users\leon.moodley\bv5.ps1:209 char:1
    + $pages.Shapes(‘ThePage’).Cells(‘RouteStyle’)=16
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

    • It’s hard to know what’s going on without seeing the whole bit of code.

      Here’s a snippet that works (in terms of not generating errors) from start to finish:
      $Visio=new-object -com Visio.Application
      $Visio.Documents.Add(”)
      $Visio.ActivePage.Shapes(‘ThePage’).Cells(‘RouteStyle’)=16

      for what it’s worth, there’s another way to get to the page’s shape sheet:
      $Visio.ActivePage.PageSheet.Cells(‘RouteStyle’)=16

      Hope that helps.

      If it doesn’t, just email me ([email protected]). No point carrying on this extended conversation in comments.

Comments are closed.