{"id":1551,"date":"2011-07-13T01:20:46","date_gmt":"2011-07-13T08:20:46","guid":{"rendered":"https:\/\/developer.microsoft.com\/en-us\/office\/blogs\/?p=1551"},"modified":"2021-11-17T12:16:25","modified_gmt":"2021-11-17T20:16:25","slug":"working-with-connected-diagrams-programmatically-part-1","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/working-with-connected-diagrams-programmatically-part-1\/","title":{"rendered":"Working with Connected Diagrams Programmatically, part 1"},"content":{"rendered":"<header class=\"entry-header single\"><\/header>\n<p>In a\u00a0previous blog post, we told you about the new Connectivity APIs in Visio 2010 that make it easier for developers to create and to move across a connected diagram. We\u2019ve shown you how to use some of the new APIs to create new connected shapes; now we\u2019ll examine techniques for traversing connected diagrams. One of the most useful APIs for analyzing a connected diagram is the\u00a0<a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ff767122.aspx\"><b>Shape.ConnectedShapes<\/b><\/a>\u00a0method, which allows you to get a reference to the shapes connected to a shape.<\/p>\n<p>Let\u2019s take a look at a specific scenario to demonstrate how this method can be used to walk through a connected diagram. Imagine that you have a simple project plan, created using the Basic Flowchart template:<\/p>\n<p><a href=\"https:\/\/msdnshared.blob.core.windows.net\/media\/MSDNBlogsFS\/prod.evol.blogs.msdn.com\/CommunityServer.Blogs.Components.WeblogFiles\/00\/00\/00\/64\/06\/metablogapi\/5468.clip_image002_027DA685.jpg\"><img decoding=\"async\" title=\"clip_image002\" src=\"https:\/\/msdnshared.blob.core.windows.net\/media\/MSDNBlogsFS\/prod.evol.blogs.msdn.com\/CommunityServer.Blogs.Components.WeblogFiles\/00\/00\/00\/64\/06\/metablogapi\/6557.clip_image002_thumb_743F2D94.jpg\" alt=\"clip_image002\" width=\"377\" height=\"484\" border=\"0\" \/><\/a><\/p>\n<p>In the shape data, each task in the flowchart includes a start date and an end date by which the task must be completed (shown in the chart below).<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"133\"><b>Shape name<\/b><\/td>\n<td valign=\"top\" width=\"192\"><b>Shape text<\/b><\/td>\n<td valign=\"top\" width=\"132\"><b>Start date<\/b><\/td>\n<td valign=\"top\" width=\"174\"><b>End date<\/b><\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"133\"><b>Start\/End<\/b><\/td>\n<td valign=\"top\" width=\"192\">\u201cBegin\u201d<\/td>\n<td valign=\"top\" width=\"132\">6\/30\/2011<\/td>\n<td valign=\"top\" width=\"174\">6\/30\/2011<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"133\"><b>Process<\/b><\/td>\n<td valign=\"top\" width=\"192\">\u201cAssess needs for project\u201d<\/td>\n<td valign=\"top\" width=\"132\">7\/5\/2011<\/td>\n<td valign=\"top\" width=\"174\">7\/11\/2011<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"133\"><b>Decision<\/b><\/td>\n<td valign=\"top\" width=\"192\">\u201cIs project well-defined?\u201d<\/td>\n<td valign=\"top\" width=\"132\">7\/8\/2011<\/td>\n<td valign=\"top\" width=\"174\">7\/8\/2011<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"133\"><b>Process.6<\/b><\/td>\n<td valign=\"top\" width=\"192\">\u201cRe-evaluate needs\u201d<\/td>\n<td valign=\"top\" width=\"132\">7\/7\/2011<\/td>\n<td valign=\"top\" width=\"174\">7\/11\/2011<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"133\"><b>Process.4<\/b><\/td>\n<td valign=\"top\" width=\"192\">\u201cBuild a prototype\u201d<\/td>\n<td valign=\"top\" width=\"132\">7\/11\/2011<\/td>\n<td valign=\"top\" width=\"174\">7\/18\/2011<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"133\"><b>Start\/End.11<\/b><\/td>\n<td valign=\"top\" width=\"192\">\u201cPrototype complete\u201d<\/td>\n<td valign=\"top\" width=\"132\">7\/29\/2011<\/td>\n<td valign=\"top\" width=\"174\">7\/29\/2011<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Ideally, no task in this project should start before the previous task has been completed. You could select each shape and compare the start and end dates in the\u00a0<b>Shape Data<\/b>\u00a0window, but the process would be very manual and tiresome. You could even use\u00a0<a href=\"http:\/\/office.microsoft.com\/en-us\/visio-help\/enhance-your-data-with-data-graphics-HA010379394.aspx\">Data Graphics<\/a>\u00a0to display the start and end dates next to each shape, but it would still require visually inspecting each pair of connected shapes in the drawing.<\/p>\n<p>With the\u00a0<b>ConnectedShapes<\/b>\u00a0method, however, you can write code that walks through each connected shape in your drawing and examine the shapes connected to it. The method returns an array of the shape IDs of all the shapes connected to a shape, filtered by the type of relationship between the shapes (e.g. is the connector \u201cgoing out\u201d to the other shape or \u201ccoming in\u201d from the other shape?). You can also narrow down the returned IDs further by specifying a shape category as an additional filter.<\/p>\n<pre>Here\u2019s some simple VBA that will iterate through each connected shape in a diagram and change the fill color of a connected shape if there is unwanted overlap between its start date and the task \u201cbefore\u201d it.\n\n<code><span style=\"font-family: Consolas;font-size: small\">Dim vsoPage As Visio.Page\u00a0\nDim shapeList\n\nSub TraverseFlowchart()\n\n\u00a0\u00a0\u00a0 Dim vsoShape1 As Visio.Shape\u00a0\n\u00a0\u00a0\u00a0 Dim shapeIDArray() As Integer\u00a0\n\u00a0\u00a0\u00a0 Dim searching As Boolean\n\n\u00a0\u00a0\u00a0 Set vsoPage = Application.ActivePage\u00a0\n\u00a0\u00a0\u00a0 Set vsoShape1 = vsoPage.Shapes(\"Start\/End\")\u00a0\n\u00a0\u00a0\u00a0 Set shapeList = CreateObject(\u201cScripting.Dictionary\u201d)\u00a0\n\u00a0\u00a0\u00a0 shapeList.Add vsoShape1.Name, 1\n\n\u00a0\u00a0\u00a0 GetConnectedShapes vsoShape1\n\nEnd Sub\n\nSub GetConnectedShapes(shape As Visio.shape)\n\n\u00a0\u00a0\u00a0 Dim outgoingShape As Visio.shape\u00a0\n\u00a0\u00a0\u00a0 Dim shapeIDArray As Variant\u00a0\n\u00a0\u00a0\u00a0 Dim shapeIDArrayNext As Variant\u00a0\n\u00a0\u00a0\u00a0 Dim outgoingNodes As Integer\u00a0\n\u00a0\u00a0\u00a0 Dim node As Integer\u00a0\n\u00a0\u00a0\u00a0 Dim prevTaskEnd As Date\u00a0\n\u00a0\u00a0\u00a0 Dim nextTaskStart As Date\u00a0\n\u00a0\u00a0\u00a0 Dim beenChecked As Boolean\n\n\u00a0\u00a0\u00a0 prevTaskEnd = shape.Cells(\"Prop.EndDate\").result(visDate)\u00a0\n\u00a0\u00a0\u00a0 shapeIDArray = shape.ConnectedShapes(visConnectedShapesOutgoingNodes, \"\")\u00a0\n\u00a0\u00a0\u00a0\u00a0\n\u00a0\u00a0\u00a0 If (UBound(shapeIDArray) &gt;= 0) Then\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 outgoingNodes = UBound(shapeIDArray)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 For node = 0 To outgoingNodes\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Set outgoingShape = vsoPage.Shapes(shapeIDArray(node))\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 nextTaskStart = outgoingShape.Cells(\"Prop.StartDate\").result(visDate)\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Debug.Print shape.Name &amp; \" end: \" &amp; prevTaskEnd &amp; \"; \" &amp; _\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 outgoingShape.Name &amp; \" start: \" &amp; nextTaskStart\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 If (nextTaskStart &lt; prevTaskEnd) Then\u00a0\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 outgoingShape.Cells(\"FillForegnd\").Formula = \"RGB(255, 0, 0)\"\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 End If\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 shapeIDArrayNext = outgoingShape.ConnectedShapes(visConnectedShapesOutgoingNodes, \"\")\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 beenChecked = shapeList.Exists(outgoingShape.Name)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 If ((UBound(shapeIDArrayNext) &gt;= 0) And Not beenChecked) Then\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 shapeList.Add outgoingShape.Name, 1\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 GetConnectedShapes outgoingShape\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 End If\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Next node\n\n\u00a0\u00a0\u00a0 End If\n\nEnd Sub<\/span><\/code><\/pre>\n<p>If we run this code on the project plan shown above, we get a result as shown below. Notice that the two shapes that have start dates that are earlier than the previous tasks\u2019 end dates have a red fill applied to them.<\/p>\n<p><a href=\"https:\/\/msdnshared.blob.core.windows.net\/media\/MSDNBlogsFS\/prod.evol.blogs.msdn.com\/CommunityServer.Blogs.Components.WeblogFiles\/00\/00\/00\/64\/06\/metablogapi\/2350.clip_image004_48FA968D.jpg\"><img decoding=\"async\" title=\"clip_image004\" src=\"https:\/\/msdnshared.blob.core.windows.net\/media\/MSDNBlogsFS\/prod.evol.blogs.msdn.com\/CommunityServer.Blogs.Components.WeblogFiles\/00\/00\/00\/64\/06\/metablogapi\/6545.clip_image004_thumb_5AD72A5A.jpg\" alt=\"clip_image004\" width=\"376\" height=\"484\" border=\"0\" \/><\/a><\/p>\n<p>In the code sample, the\u00a0<i>GetConnectedShapes<\/i>\u00a0subroutine uses the\u00a0<b>ConnectedShapes<\/b>\u00a0method on the shape passed in as an argument to get an array of the shape IDs (integers) of the shapes that it connects to. (That is, the shape contains the beginning connection of a connector that points to the shapes referenced by the IDs in the array.) Then the subroutine iterates through each shape referenced by the IDs in the array. Next, the value of the \u201cProp.StartDate\u201d cell of each attached shape is compared to the \u201cProp.EndDate\u201d cell of the first shape. If the values overlap, the value of \u201cFillForegnd\u201d cell is changed to red.<\/p>\n<p>This code sample also uses the\u00a0<b>ConnectedShapes<\/b>\u00a0method to traverse the connected diagram in a manner similar to \u201cwalking\u201d a tree data structure, starting from a specific shape (the first Start\/End shape). After it analyzes a shape and one of its attached shapes, the\u00a0<i>GetConnectedShapes<\/i>\u00a0subroutine checks whether the attached shape has any shapes that it connects out to (making another call to\u00a0<b>ConnectedShapes<\/b>). If so, the subroutine makes a recursive call to itself (and thereby halting the execution of the original call), passing in the attached shape as an argument. In practice, it is likely that you will need to use this programming technique in conjunction with the\u00a0<b>ConnectedShapes<\/b>\u00a0method in order to work all the way through each shape in a large connected diagram.<\/p>\n<p>(Also notice that the code sample keeps track of each shape whose outgoing nodes have been checked by adding the shape to a dictionary object before the GetConnectedShapes subroutine is called. This will prevent the code from becoming stuck in an infinite loop if the diagram includes a circular reference.)<\/p>\n<p>Even though we\u2019ve used a project plan scenario as the example in this code sample, there are many other practical applications for this technique. In a related but larger sense, you could incorporate this technique within a custom validation checker to determine whether a connected diagram has been constructed correctly. Also similar to the code sample above, you could use this technique to extract and store data from each shape in a connected diagram. (Extending the project plan example, you could get the duration from each task in the workflow and then create a total of all the resource hours needed for the project.)<\/p>\n<p>In the next blog post, we\u2019ll look at another way that you can use this technique and the\u00a0<a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ff767928.aspx\"><b>Page.SplitConnector<\/b><\/a>\u00a0method to add new shapes to a connected diagram.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In a\u00a0previous blog post, we told you about the new Connectivity APIs in Visio 2010 that make it easier for developers to create and to move across a connected diagram. We\u2019ve shown you how to use some of the new APIs to create new connected shapes; this blog will examine techniques for traversing connected diagrams. One of the most useful APIs for analyzing a connected diagram is the\u00a0Shape.ConnectedShapes\u00a0method, which allows you to get a reference to the shapes connected to a shape.<\/p>\n","protected":false},"author":69200,"featured_media":25159,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[11],"tags":[77],"class_list":["post-1551","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-office-add-ins","tag-visio"],"acf":[],"blog_post_summary":"<p>In a\u00a0previous blog post, we told you about the new Connectivity APIs in Visio 2010 that make it easier for developers to create and to move across a connected diagram. We\u2019ve shown you how to use some of the new APIs to create new connected shapes; this blog will examine techniques for traversing connected diagrams. One of the most useful APIs for analyzing a connected diagram is the\u00a0Shape.ConnectedShapes\u00a0method, which allows you to get a reference to the shapes connected to a shape.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/posts\/1551","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/users\/69200"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/comments?post=1551"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/posts\/1551\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/media\/25159"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/media?parent=1551"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/categories?post=1551"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/tags?post=1551"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}