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.
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.
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;
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;
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 –
&
#160; 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;
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);
}
}
&
#160; }
}
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.
[…] 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 […]