Understanding Visio Connections

A reader recently asked if I could explain how to programmatically get the shapes connected to a shape in Visio. So, I thought I would have a go, because there are alternatives, depending upon which functions are used, and what parameters are passed to them. The following animated gif is rotating around the different types of selections that can be made from the lower Decision shape. Normally, two 2D shapes are connected together using a 1D shape. The 1D shape has a direction because it starts from “BeginX” and finishes at “EndX”. This is irrespective of an arrowheads that the user may have chosen to adorn the 1D connector with at either end.

multiSelect Children and Parents

The above image shows my multiSelect tool in use, which is available free from http://bvisual.net/Products/multiSelect.aspx.

The following image shows the same diagram, but with the ID of each shape shown. The Decision shape with an ID=65 is selected. It has one inward connector, labelled Maybe, and two outwards connectors labelled No and Yes.

 

image

So, the request is how to get the connected shapes … Well, the connectors are glued to the Decision shape, and the opposite end of each connector is glued to another shape.

Originally, Visio developers had to follow the Connects and FromConnects collection of the Shape object, and the direction of the 1D connector shapes can be derived from knowing if the connected cell is named BeginX or EndX.

Then Microsoft introduced a GluedShapes(…) method and a ConnectedShapes(…) method which return an array of shape IDs, and have arguments to filter the direction and category of the target shapes.

 

GetGluedShapes

The following C# code (written in LinqPad) gets the glued outgoing 1D shapes and selects them (in addition to the originally shape(s)  – the Decision shape).

var vApp = MyExtensions.GetRunningVisio();

Visio.Window win = vApp.ActiveWindow;

Visio.Selection sel = vApp.ActivePage.CreateSelection(Visio.VisSelectionTypes.visSelTypeEmpty);
foreach (Visio.Shape shp in win.Selection)
{
    sel.Select(shp, (short) Visio.VisSelectArgs.visSelect);    //If source shape still required
    GetGlued(shp,Visio.VisGluedShapesFlags.visGluedShapesOutgoing1D, "", sel, true);
}

vApp.ActiveWindow.Selection = sel;

 

image

 

It also outputs the following rows:

Decision.65 is glued to

Dynamic connector.70

Decision.65 is glued to

Dynamic connector.71

The GetGlued(…) function

void GetGlued(Visio.Shape shp,
    Visio.VisGluedShapesFlags whichGluedShapes, string category,
    Visio.Selection selection, bool addToSelection)
{
    Array aryTargetIDs;
    aryTargetIDs = shp.GluedShapes(whichGluedShapes, category);
    for (int i = aryTargetIDs.GetLowerBound(0); i <= aryTargetIDs.GetUpperBound(0); i++)
    {
        Visio.Shape gluedShape = shp.ContainingPage.Shapes.ItemFromID[(int) aryTargetIDs.GetValue(i)];
        //LinqPad only –
        gluedShape.Name.Dump(shp.Name + " is glued to");
        if (addToSelection)
        {
            selection.Select(gluedShape, (short)Visio.VisSelectArgs.visSelect);
        }
    }
}

 

ConnectedShapes

The following C# code (written in LinqPad) gets the connected outgoing 2D shapes and selects them (in addition to the originally shape(s)  – the Decision shape).

var vApp = MyExtensions.GetRunningVisio();

Visio.Window win = vApp.ActiveWindow;

Visio.Selection sel = vApp.ActivePage.CreateSelection(Visio.VisSelectionTypes.visSelTypeEmpty);
foreach (Visio.Shape shp in win.Selection)
{
    sel.Select(shp, (short) Visio.VisSelectArgs.visSelect);    //If source shape still required
    GetConnected(shp, Visio.VisConnectedShapesFlags.visConnectedShapesOutgoingNodes, "",sel, true);
}

vApp.ActiveWindow.Selection = sel;

image

It also outputs the following rows:

Decision.65 is connected to

Process.54

Decision.65 is connected to

Process.67

The GetConnected(…) function

void GetConnected(Visio.Shape shp,
    Visio.VisConnectedShapesFlags whichConnectedShapes, string category,
    Visio.Selection selection, bool addToSelection)
{
    Array aryTargetIDs;
    aryTargetIDs = shp.ConnectedShapes(whichConnectedShapes, category);
    for (int i = aryTargetIDs.GetLowerBound(0); i <= aryTargetIDs.GetUpperBound(0); i++)
    {
        Visio.Shape connectedShape = shp.ContainingPage.Shapes.ItemFromID[(int)aryTargetIDs.GetValue(i)];
        //LinqPad only –
        connectedShape.Name.Dump(shp.Name + " is connected to");
        if (addToSelection)
        {
            selection.Select(connectedShape, (short) Visio.VisSelectArgs.visSelect);
        }
    }
}

 

GetShapeConnects

The following C# code (written in LinqPad) gets the connected outgoing 1D and 2D shapes and selects them (in addition to the originally shape(s)  – the Decision shape).

var vApp = MyExtensions.GetRunningVisio();
if (vApp.ActiveWindow == null) return;
Visio.Window win = vApp.ActiveWindow;

Visio.Selection sel = vApp.ActivePage.CreateSelection(Visio.VisSelectionTypes.visSelTypeEmpty);
foreach (Visio.Shape shp in win.Selection)
{
    sel.Select(shp, (short) Visio.VisSelectArgs.visSelect);    //If source shape still required
    GetShapeConnects(shp, true ,sel, true, true);
}

vApp.ActiveWindow.Selection = sel;

image

It also outputs the following rows:

Shape

65 : Decision.65

FromConnect

65 : Decision.65 – 101 – Connections.X2 < 71 : Dynamic connector.71 – 9 – BeginX

Shape

71 : Dynamic connector.71

Connect

71 : Dynamic connector.71 – 12 – EndX > 54 : Process.54 – 3 – PinX

FromConnect

65 : Decision.65 – 103 – Connections.X4 < 70 : Dynamic connector.70 – 9 – BeginX

Shape

70 : Dynamic connector.70

Connect

70 : Dynamic connector.70 – 12 – EndX > 67 : Process.67 – 3 – PinX

 

The GetShapeConnects(…) function calls itself, traversing the 1D connectors

void GetShapeConnects(Visio.Shape shp, bool? outwardOnly,
    Visio.Selection selection, bool add1DToSelection, bool add2DToSelection)
{
    //LinqPad only –
    (shp.ID.ToString() + " : " + shp.Name).Dump("Shape");

    //Used when the incoming shape is 1D
    foreach (Visio.Connect cnxn in shp.Connects)
    {
        if ((!outwardOnly.HasValue) ||
            outwardOnly.HasValue && outwardOnly.Value.Equals(true) && cnxn.FromCell.Name.Equals("EndX") ||
            outwardOnly.HasValue && outwardOnly.Value.Equals(false) && cnxn.FromCell.Name.Equals("BeginX"))
        {
            //LinqPad only –
            (cnxn.FromSheet.ID + " : " + cnxn.FromSheet.Name + " – " + cnxn.FromPart.ToString() + " – " + cnxn.FromCell.Name +
            " > " + cnxn.ToSheet.ID + " : " + cnxn.ToSheet.Name + " – " + cnxn.ToPart.ToString() + " – " + cnxn.ToCell.Name)
            .Dump("Connect");

            if (add2DToSelection)
            {
                selection.Select(cnxn.ToSheet, (short) Visio.VisSelectArgs.visSelect);
            }
        }
    }
    //Used when the incoming shape is 2D
    foreach (Visio.Connect cnxn in shp.FromConnects)
    {
        if ( (!outwardOnly.HasValue) ||
            outwardOnly.HasValue && outwardOnly.Value.Equals(true) && cnxn.FromCell.Name.Equals("BeginX") ||
            outwardOnly.HasValue && outwardOnly.Value.Equals(false) && cnxn.FromCell.Name.Equals("EndX"))
        {
            //LinqPad only –
            (cnxn.ToSheet.ID + " : " + cnxn.ToSheet.Name + " – " + cnxn.ToPart.ToString() + " – " +    cnxn.ToCell.Name +
            " < " + cnxn.FromSheet.ID + " : " + cnxn.FromSheet.Name + " – " + cnxn.FromPart.ToString() + " – " + cnxn.FromCell.Name)
            .Dump("FromConnect");

            if (cnxn.FromCell.Name.Equals("BeginX"))
            {
                GetShapeConnects(cnxn.FromSheet, true, selection, add1DToSelection, add2DToSelection);
            }
            else if (cnxn.FromCell.Name.Equals("EndX"))
            {
                GetShapeConnects(cnxn.FromSheet, false, selection, add1DToSelection, add2DToSelection);
            }
            if (add1DToSelection)
            {
                selection.Select(cnxn.FromSheet, (short)Visio.VisSelectArgs.visSelect);
            }
        }
    }
}

 

NB. All of the Connect objects are actually stored in the Page.Connects collection, so this could be used as an alternative. The Visio type library re-purposes them as Connects and FromConnects collections on each shape. If you examine the native vsdx XML, you will only find Page.Connects elements.

Posted in Visio. Tags: , . 1 Comment »

One Response to “Understanding Visio Connections”

  1. Update to multiSelect – tracing sub-shape connections in Visio | bVisual - for people interested in Microsoft Visio Says:

    […] between shapes, and my previous post about connections used a flowchart as an example (see https://blog.bvisual.net/2016/08/09/understanding-visio-connections/ ). However, it has come to my attention that some Visio developers add connection points to […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Chris Webb's BI Blog

Microsoft Analysis Services, MDX, DAX, Power Pivot, Power Query and Power BI

davecra.wordpress.com/

Microsoft Office 365 Development, and more...

PowerShell.Amsterdam

Automate, Accelerate, Accurate

johnvisiomvp

Life with Visio and other Microsoft Toys!

Title (Required)

Windows Server Essentials Tips & Tricks

Nilsandrey's Weblog

Just another WordPress.com weblog

Things that Should be Easy

Every so often (too often in the IT industry) I encounter things that should have been very easy to do but turned out to be far too complicated. My favorite topics include SharePoint, .Net development, and software architecture, especially distributed systems.

Visio Guy

Shapes, Stencils, Drawings Templates, Tutorials, Tips & Developer Info for Microsoft Visio

Hannes's Virtual Earth

Tips & Tricks around Mapping and Cloud Computing

Pluralsight blog

be smart, be clear, be visual ...

%d bloggers like this: