The concept of Structured Diagrams was introduced in Visio 2010 to provide core functionality for a variety of the templates in Visio, such as the Cross-Functional Flowchart, BPMN Diagram and Wireframe Diagram. This is primarily evident as Containers, Lists and Callouts and they can be customized (see Custom Containers, List and Callouts ), as shown in some of my previous articles ( see https://blog.bvisual.net/?s=container). One example of this extension can be found in all of the flowchart shapes that placed into a swimlane in a cross-functional flowchart. They have a Shape Data row, Function, that automatically inherits the text entered into the header of the swimlane. In another example, a Visio user can use Insert / Diagram Parts / Container to visually group other shapes together, and update the header text of the container. Well, I often do want my custom shapes to inherit the text of a container, so, in this article, I show how the built-in containers can be enhanced to provide this ability.
You should be aware that I often exploit the fact that Visio creates a local copy of each master shape that it uses within a document. This can be modified and set to match master by name on drop, to ensure that it is the one to use, even if the original Microsoft provided version is used. In this case, I use this trick to create hidden, enhanced versions of the containers that are available in the gallery of containers. Microsoft also do this with the Dynamic connector shape in a number of their own templates.
If you examine the Function Shape Data row of any of the flowchart shapes, then you will find the formula:
This formula will get the value of the User.visHeadingText cell in the first container, that it is within, with the category “Swimlane“.
This is possible because there is a User-defined Cell row, with the reserved name msvShapeCategories, in the Swimlane shape. This cell can store a semi-colon separated list of categories. In fact, the formula for the User.msvShapeCategories cell in the Swimlane shapes is:
This means that it belongs to two categories, Swimlane and DoNotContain.
The reason why the Function Shape Data row references the User.visHeadingText cell is because the Swimlane is a group shape, and the text is actually in one if the sub-shapes, with the value in User.msvStructureType = “Heading”. Therefore, the reference is to another cell, User.visHeadingText that gets the text of a sub-shape with the following formula (where n is the ID of the sub-shape):
This means that the formula in any other shape just needs to get a reference to the known User.visHeadingText cell. Well, all of the built-in containers are a very similar structure to the Swimlane shape, but the User.visHeadingText cell is not always present. Therefore, my bit of VBA code below simply enhances any containers with a Heading sub-shape, so that the User.visHeadingText cell is present, and it returns the text that it finds.
It is not essential that the containers are filtered by a category, so the following formula is also valid, and would return the value in the first container, if it has a User.visHeadingText cell (casing is not important):
Now, all of the containers in the ribbon have the following formula in the User.msvShapeCategories cell:
This can be edited to store any other custom category too. So the following formula would make the container have two categories, either of which could be used as a filter:
It is easy to add all of the built-in containers quickly because the ribbon gallery will replace the currently selected container shape with the gallery item. The container masters will be added to the document stencil, as in the following example.
Then the EnhanceContainers macro will update all of these copies in the document stencil, and hide them from the normal stencil view too.
All that remains is to prepare the Shape Data row of any master that needs to inherit the value of the container text with the following formula:
For example, I added the above formula into the Owner Shape Data row in the Process master shape.
The VBA Code
Insert one or more containers from Insert / Diagram Parts / Container into your document, delete them (not using Undo), then run the following macro. It will enhance them all.
Public Sub EnhanceContainers() 'Author : David Parker, Jul 2018, No rights reserved 'Purpose: To enhance any container masters in the active document ' to regularise the reference to the heading text 'Usage : Add the following ShapeSheet function to Shape Data rows ' =IFERROR(CONTAINERSHEETREF(1,opt_category)!User.VISHEADINGTEXT,"") ' where opt_category is an optional category, ' listed in the User.msvShapeCategories cell of the container On Error GoTo errHandler Dim mst As Visio.Master Dim mstEdit As Visio.Master Dim shp As Visio.Shape Dim subShp As Visio.Shape Dim strType As String Dim strSubType As String Dim headingSheet As String Dim categories As String Dim aryCats() As String Dim newCategories As String Dim i As Integer Dim catExists As Boolean Const ROWTYPE As String = "User.msvStructureType" Const CELHDR As String = "visHeadingText" Const ROWCAT As String = "User.msvShapeCategories" Const CONTAINERCAT As String = "My Container" 'Optional category Const UDCSECT As Integer = Visio.VisSectionIndices.visSectionUser Const FLGEX As Integer = Visio.VisExistsFlags.visExistsAnywhere For Each mst In ActiveDocument.Masters If mst.Shapes(1).CellExists(ROWTYPE, FLGEX) <> 0 Then headingSheet = "" strType = mst.Shapes(1).Cells(ROWTYPE).ResultStr("") If strType = "Container" Then 'Open the master to edit it Set mstEdit = mst.Open Set shp = mstEdit.Shapes(1) For Each subShp In shp.Shapes If subShp.CellExists(ROWTYPE, FLGEX) <> 0 Then strSubType = subShp.Cells(ROWTYPE).ResultStr("") If strSubType = "Heading" Then 'Get the NameID of the Heading sub-shape headingSheet = subShp.nameId Exit For End If End If Next subShp 'If necessary, update the container categories categories = shp.Cells(ROWCAT).ResultStr("") If Len(CONTAINERCAT) > 0 Then If Len(categories) > 0 Then aryCats = Split(categories, ";") For i = 0 To UBound(aryCats) If aryCats(i) = CONTAINERCAT Then catExists = True Exit For End If Next I If catExists = False Then newCategories = categories & ";" & CONTAINERCAT End If Else newCategories = CONTAINERCAT End If End If shp.Cells(ROWCAT).FormulaForceU = "=""" & newCategories & """" If Len(headingSheet) > 0 Then 'Create the visHeadingText cell If shp.CellExists("User." & CELHDR, FLGEX) = 0 Then shp.AddNamedRow UDCSECT, CELHDR, 0 End If 'Set the fomula for the visHeadingText cell shp.Cells("User." & CELHDR).FormulaForceU = _ "=SHAPETEXT(" & headingSheet & "!TheText)" End If 'Close the master edit object mstEdit.Close mst.matchByName = True mst.Hidden = 1 End If End If Next mst exitHere: Exit Sub errHandler: MsgBox Err.description, vbExclamation, "EnhanceContainers" Resume exitHere End Sub
BTW, I always put this sort of code in a macro-enabled stencil that I sve into the My Shapes folder.
Caution! Do not specify “Swimlane” as a category for your containers because the Microsoft BASFLO add-on behaves badly, and won’t let you edit the text in the header!
A macro-enabled stencil with the above code can be downloaded from here
An enhanced sample diagram can be downloaded here