David's profilebVisualPhotosBlogListsMore Tools Help

Blog


    November 21

    Microsoft Visio 2010 Beta now available to the public

    One of the greatest benefits of being an MVP is our chance to participate in private beta tests of the forthcoming versions of our chosen application.  Well, I have been testing the new version of Visio for a while now, and blogging about parts that we have been able to talk publicly about.  I think Visio 2010 is going to be a tremendous step forward for enterprises, so it feels good to announce that anyone can download the beta of Visio 2010 Premium edition now … just follow the link on this button:

    clip_image001

    For me, the highlights are:

    • Structured Diagramming – Containers, Lists, Connectivity API
    • Rules Validation – ability to use and create diagramming rules, like for BPMN and SharePoint Workflow
    • Visio Services – server side refreshing of data linked diagrams, SharePoint integration

    There are many improvements behind the scenes to support these new features.  Try it out for yourself!

    September 21

    Visio 2010 Validation Rules (part 2)

    In my previous post, I began to explore the new Validation objects in the Visio 2010 Technical Preview, an I used some VBA code to do this.  Well, I have now uploaded the code to SkyDrive ( http://cid-3350d61bc93733a9.skydrive.live.com/self.aspx/Blogs/bVisualValidationExplorer.vss ), so that you can use it too!  So, this post is about using this code …

    First, download the stencil and save it to you My Shapes folder (if you trust me).  This will make it available to you to use in any Visio 2010 document.

    Then, whenever you want to use the code, simply open the stencil from the More Shapes / My Shapes category….

    imageThe stencil will be docked with the other shape stencils, but, of course, this stencil does not have any shapes … only VBA code.

    To keep it simple, I have exposed just one public macro, ThisDocument.Show Validation, which you can run by selecting the bVisualValidationExplorer.vss in the list of Macros dialogue.

    image

    Note, for those of you ( like me ) who had trouble finding out where to switch on the Developer ribbon, look under the Options / Advanced settings.  There you will find the Run in Developer Mode check box.:

    image

    So, if you have a BPMN diagram, for example, then you are able to validate it against a selected rule set:

    image The bVisual Validation Explorer lists the Issues too, but it additionally displays the Name of the transgressed Rule, along with the Page and , if relevant, the Shape Name.

    image

    In addition, it focuses the rule that has been transgressed on the Rules tab :

    image

    The Test and Filter Expressions of some rules are longer that the singe line that I have allowed for in the main dialogue, so I have provided a builder button to the right of each one enables you to open the expression in a large multi-line box:

    image

    All of the editable properties are editable, and I have included buttons to :

    • Validate or Clear Issues in a document
    • Delete an Issue
    • Add, Delete, or Copy Rulesets, , and to Paste a copied Ruleset
    • Add and Delete Rules, or even to Add Issue of the selected rule to a nominated Shape:

    image

    This is based on the Microsoft Visio 2010 Technical Preview so it may not work with the full release … but I will update it if that is the case.  All the rules developer needs to know now is how to construct the Test and Filter Expressions! Hmmmm ……

    September 16

    Visio 2010 Validation Rules (part 1)

    The Microsoft Visio team recently blogged about Creating custom validation rules for Visio 2010 (see http://blogs.msdn.com/visio/archive/2009/09/10/creating-custom-validation-rules-for-visio-2010.aspx), in which it was suggested that we can use Visual Basic for Applications to interact with the new validation API.  I thought, there’s a challenge … so I will develop a VBA project to do just that.  I will upload the code soon, but, as it is only half developed today, I will demonstrate how to understand why a rule has been broken.

    In the following BPMN diagram, I have failed to connect a Task to the End Event.  I have made it obvious here but it could be easy to overlook in a larger diagram.  In fact, I know of one company who were advised by a “consultant” that there must be something wrong when the end of a connector goes red therefore you should move it away from the shape until it goes green!  The result was that the company had over 300 flow diagrams without a single valid connection!  That company is no more!

    image

    The new UI in Visio 2010 has a new Process tab, which has a button to Check Diagram and a tick box display the Issues Window.  This shows that two rules have been broken, but it does not give you any more than the rule Description and Category.  Now, you may assume that Microsoft have the BPMN rules modelled correctly, but if you are creating your own rule sets, then you need to understand exactly why they have been broken, therefore I have constructed a basic VBA dialog that displays the Issues and Rules in a selected document.

    Issues

    So, when an Issue is read from the new document.Validation.Issues collection

    Issue 1 has the rule with the description An End Event must have incoming Sequence Flow. has the name NoSequenceFlowToEndEvent , and the category of End Events .

    Issue 2 has the rule with the description The flow must have a source and target. has a name NoTargetOrSource, and a category list of Sequence Flow, Message Flow .

     

    image

    Rules

    The Name, Description and Category of the Rule, cannot be sufficient to define what the rule is, so we must explore the Rules of the active RuleSet in the document.  In this case it is the BPMN RuleSet, which contains many Rules, and using my dialogue, I can find the Rule with the name NoTargetOrSource to see what it’s definition is.

    image

    Now we can begin to understand how a rule is defined by inspecting the Test Expression and Filter Expression values.

    TestExpression=AND(AGGCOUNT(GLUEDSHAPES(4))=1,AGGCOUNT(GLUEDSHAPES(5))=1)

    FilterExpression=OR(Actions.MessageFlow.Checked, Actions.SequenceFlow.Checked)

    These expressions look very similar to ShapeSheet formulae, but they are not actual ShapeSheet formulae.  However, some ShapeSheet functions can be used in these expressions, but Microsoft have had to create some extra quasi-ShapeSheet functions that are needed in order to create a rule definition.

    Closer inspection of the FilterExpression shows that this rule is only to be applied to shapes which have either an Actions.MessageFlow or Actions.SequenceFlow row checked.  The Dynamic Connector in the BPMN template has been modified to have such rows in the Actions section of the spreadsheet.

    image

    If you now look at the TestExpression reveals that the new GluedShapes method, as seen in my previous post Listing Connections in Visio 2010 ( http://bvisual.spaces.live.com/blog/cns!3350D61BC93733A9!1820.entry ), returns an array of glued shapes .  The arguments 4 and 5 are the actual values of the constants visGluedShapesIncoming2D and visGluedShapesOutgoing2D respectively.  The function AGGCOUNT() obviously means the count the number of items in the retuned array, so the whole expression simply says that there must be a connected 2D shape at either end of the connector.

    The other broken rule, NoSequenceFlowToEndEvent, has :

    TestExpression=AGGCOUNT(FILTERSET(GLUEDSHAPES(1),"Actions.SequenceFlow.Checked"))>0

    FilterExpression=AND(HASCATEGORY("Event"),Actions.End.Checked)

    Inspection of these expressions reveal that it applies to and shape that has the category “Event”, and has the row Actions.End checked, and it must have a glued connector which has the row Actions.SequenceFlow checked.  I discussed the new ShapeSheet function HASCATEGORY() in my earlier blog Visio 2010 : Containment and Cross-Functional Flowcharts ( http://bvisual.spaces.live.com/blog/cns!3350D61BC93733A9!1811.entry ), but the other new function is FILTERSET() obviously means that the returned array of GluedShapes is filtered to include only those that satisfy the second argument, in this case, Actions.SequenceFlow.Checked.

    Once you start examining the rules, you can begin to understand how you can create your own ones.  Getting interested?

    Listing Connections in Visio 2010

    One of the best bits of Visio is the connections between shapes, but it has always been difficult to understand these connections in code.  There are connections to and from shapes, which require you to understand where you are and which way to look … at the begining or the end of a 1D connector. Visio 2010 has added some useful extra methods to the shape object which makes understanding connections much easier.  This post will show you how you can use the new GluedShapes() and ConnectedShapes() methods.

    Take, for example, a network diagram where there are connections between servers and routers:

    image

    You may wish to list the connections at the start and end of each cable.  Previously, this would have meant inspecting the connected cell to see if was at the beginning or end of the line, but now you can use the GluedShapes() method of a shape to retrieve an array of the 2D shapes connected at one end or another with the the relevant arguments, visGluedShapesIncoming2D or visGluedShapesOutgoing2D.  The ListGluedConnections macro below displays the following in the immediate window:

    Connector     Dynamic connector
                  >             Router.45     router-02
                  <             Server        server-01
    Connector     Dynamic connector.107
                  >             Router.45     router-02
                  <             Server.30     server-02
    Connector     Dynamic connector.108
                  >             Router        router-01
                  <             Server.75     server-03
    Connector     Dynamic connector.109
                  >             Router.91     router-03
                  <             Server.30     server-02
    Connector     Dynamic connector.110
                  >             Router.91     router-03
                  <             Server        server-01

    Public Sub ListGluedConnections()
    Dim shp As Visio.Shape
    Dim connectorShape As Visio.Shape
    Dim sourceShape As Visio.Shape
    Dim targetShape As Visio.Shape
    Dim aryTargetIDs() As Long
    Dim arySourceIDs() As Long
    Dim targetID As Long
    Dim sourceID As Long
    Dim i As Integer

    For Each shp In Visio.ActivePage.Shapes
        If shp.OneD Then
            Debug.Print "Connector", shp.Name
            arySourceIDs = shp.GluedShapes(visGluedShapesIncoming2D, "")
            For i = 0 To UBound(arySourceIDs)
                Set sourceShape = Visio.ActivePage.Shapes.ItemFromID(arySourceIDs(i))
                If sourceShape.CellExists("Prop.NetworkName", Visio.visExistsAnywhere) Then
                    Debug.Print , ">", sourceShape.Name, sourceShape.Cells("Prop.NetworkName").ResultStr("")
                End If
            Next
            aryTargetIDs = shp.GluedShapes(visGluedShapesOutgoing2D, "")
            For i = 0 To UBound(aryTargetIDs)
                Set targetShape = Visio.ActivePage.Shapes.ItemFromID(aryTargetIDs(i))
                If targetShape.CellExists("Prop.NetworkName", Visio.visExistsAnywhere) Then
                    Debug.Print , "<", targetShape.Name, targetShape.Cells("Prop.NetworkName").ResultStr("")
                End If
            Next
        End If
    Next

    End Sub

    Similarly, you may want to simply list the next connected 2D shape, effectively ignoring the cable.  In this case, you can use the new ConnectedShapes() method, with the relevant arguments visGluedShapesIncoming2D or visGluedShapesOutgoing2D, to produce an output like the following ( using the ListNextConnections macro below):

    Shape         Server        server-01
                  >             Router.45     router-02
                  >             Router.91     router-03
    Shape         Router        router-01
                  <             Server.75     server-03
    Shape         Server.30     server-02
                  >             Router.45     router-02
                  >             Router.91     router-03
    Shape         Router.45     router-02
                  <             Server        server-01
                  <             Server.30     server-02
    Shape         Server.75     server-03
                  >             Router        router-01
    Shape         Router.91     router-03
                  <             Server        server-01
                  <             Server.30     server-02

    Public Sub ListNextConnections()
    Dim shp As Visio.Shape
    Dim connectorShape As Visio.Shape
    Dim sourceShape As Visio.Shape
    Dim targetShape As Visio.Shape
    Dim aryTargetIDs() As Long
    Dim arySourceIDs() As Long
    Dim targetID As Long
    Dim sourceID As Long
    Dim i As Integer

    For Each shp In Visio.ActivePage.Shapes
        If Not shp.OneD Then
            If shp.CellExists("Prop.NetworkName", Visio.visExistsAnywhere) Then
                Debug.Print "Shape", shp.Name, shp.Cells("Prop.NetworkName").ResultStr("")
                arySourceIDs = shp.ConnectedShapes(visConnectedShapesOutgoingNodes, "")
                For i = 0 To UBound(arySourceIDs)
                    Set sourceShape = Visio.ActivePage.Shapes.ItemFromID(arySourceIDs(i))
                    If sourceShape.CellExists("Prop.NetworkName", Visio.visExistsAnywhere) Then
                        Debug.Print , "<", sourceShape.Name, sourceShape.Cells("Prop.NetworkName").ResultStr("")
                    End If
                Next
                aryTargetIDs = shp.ConnectedShapes(visConnectedShapesIncomingNodes, "")
                For i = 0 To UBound(aryTargetIDs)
                    Set targetShape = Visio.ActivePage.Shapes.ItemFromID(aryTargetIDs(i))
                    If targetShape.CellExists("Prop.NetworkName", Visio.visExistsAnywhere) Then
                        Debug.Print , ">", targetShape.Name, targetShape.Cells("Prop.NetworkName").ResultStr("")
                    End If
                Next
            End If
        End If
    Next

    End Sub

    I think this makes interrogation of connected diagrams, of all flavours, much simpler, though I would love to have similar ShapeSheet functions too!

    September 08

    Visio 2010 : Containment and Cross-Functional Flowcharts

    One of the templates to get a revision in Visio 2010 is the Cross Functional Flowchart template because of the new list and containment functionality that has been added into the core application.  I had to write a small bit of code in earlier versions of Visio for each flowchart shape to automatically understand which swimlane and phase it belongs to, but now there are ShapeSheet functions available, so a slight modification of a flowchart master enables it to inherit values from the swimlane that it is in.  This article demonstrates who you can do this to, for example, synchronize the fill color of each flowchart shape to that of the swimlane that it belongs to.

    image

    The Cross Functional Flowchart template now includes its own add-in ribbon:

    image

    Each Swimlane shape is now a container shape, as is the CFF Container and Phase shapes.   Notice that the Process shape has a Shape Data row, named Function, which automatically updates itself with the text entered into the header text of the Swimlane that it is within.  Unfortunately, it is not the same for the Phase that it is within … perhaps Microsoft will correct this for the official release of Visio 2010, but in the meantime I will propose a workaround later in this post.  Additionally, the Function shape data row is displayed, regardless of whether it is within a Swimlane or not, therefore I will show how the visibility can be controlled to automatically hide if it is not within a Swimlane.

    Container Categories

    The new container masters have lots of new User-defined cells to define how they are to be handled, amongst which are the following two:

    • User.msvStructureType = ”Container”
    • User.msvShapeCategories = “Phase”

    The second value will be CFF Container, CFF List, Swimlane or Phase, depending on which Master it is.

    In the above example, the My Process shape is actually within three containers, a CFF Container, Swimlane and Phase (note that the new Phase shape actually extends from the Swimlane headers to the right hand line that has the Phase X label).

    Listing the Containers that a Shape is Within

    There are times when you will need to determine what containers a shape is within, and to be able to recognise what type of container it is.  The ListSelectedShapeContainers macro listed below will produce output in the Immediate Window like httis (if the My Process shape is selected first):

    Process       My Process
                  ID            CFF Container CFF List      Swimlane      Phase         Name
                   20           False         False         False         True          Phase (vertical).20
                   4            False         True          False         False         Swimlane List
                   11           False         False         True          False         Swimlane.11

    Public Sub ListSelectedShapeContainers()
    Dim shp As Visio.Shape
        If Visio.ActiveWindow.Selection.PrimaryItem Is Nothing Then
            Exit Sub
        Else
            Set shp = Visio.ActiveWindow.Selection.PrimaryItem
        End If

    Dim aryContainerIDs() As Long
    Dim containerID As Long
    Dim iContainer As Integer
    Dim containerShp As Visio.Shape

        Debug.Print shp.Name, shp.Text
        aryContainerIDs = shp.MemberOfContainers()
        Debug.Print , "ID", "CFF Container", "CFF List", "Swimlane", "Phase", "Name"
        For iContainer = 0 To UBound(aryContainerIDs)
            Set containerShp = Visio.ActivePage.Shapes.ItemFromID(aryContainerIDs(iContainer))
            Debug.Print , aryContainerIDs(iContainer), _
                containerShp.HasCategory("CFF Container"), _
                containerShp.HasCategory("CFF List"), _
                containerShp.HasCategory("Swimlane"), _
                containerShp.HasCategory("Phase"), _
                containerShp.Name
        Next iContainer
    End Sub

    Notice the new shape method MemberOfContainers() which returns an array of container shape IDs, which are then used to get the actual container shapes in order to discover what type of container they are using the new HasCategory() method.

    Opening the ShapeSheet of the My Process shape reveals how the containing swimlane shape text is displayed in the Prop.Function row:

    image

    The new ShapeSheet function, CONTAINERSHEET() requires the index position in the list of container shapes, and the second argument enables the list of returned containers to be filtered by the category.  The Scratch.A1 cell formula is =User.visHeadingText, which has the formula =SHAPETEXT(Sheet.13!TheText)

    There is also a new function, CONTAINERCOUNT(), which returns the number of containers that the shapes is within.

    Listing the Containers and Members on a Page

    It is also possible to list containers on a page, and to list the shapes contained within them.  The following Immediate Window output was produced by the ListPageContainers() macro:

    ID            CFF Container CFF List      Swimlane      Phase         Name
    1            True          False         False         False         CFF Container
                   4            Swimlane List
                   6            Phase List
    4            False         True          False         False         Swimlane List
                   8            Swimlane
                   11           Swimlane.11
                   17           Process
                   18           Process.18
    6            False         True          False         False         Phase List
                   14           Phase (vertical)
                   20           Phase (vertical).20
    8            False         False         True          False         Swimlane
                   18           Process.18
    11           False         False         True          False         Swimlane.11
                   17           Process
    14           False         False         False         True          Phase (vertical)
                   18           Process.18
    20           False         False         False         True          Phase (vertical).20
                   17           Process

    Notice that I did not filter out the contained containers, but I did filter out the connectors (1D) shapes.

    Public Sub ListPageContainers()
    Dim aryContainerIDs() As Long
        aryContainerIDs = Visio.ActivePage.GetContainers(visContainerIncludeNested)
    Dim containerID As Long
    Dim iContainer As Integer
    Dim containerShp As Visio.Shape

    Dim aryMemberIDs() As Long
    Dim memberID As Long
    Dim iMember As Integer
    Dim memberShp As Visio.Shape

        Debug.Print "ID", "CFF Container", "CFF List", "Swimlane", "Phase", "Name"
        For iContainer = 0 To UBound(aryContainerIDs)
            Set containerShp = Visio.ActivePage.Shapes.ItemFromID(aryContainerIDs(iContainer))
            Debug.Print aryContainerIDs(iContainer), _
                containerShp.HasCategory("CFF Container"), _
                containerShp.HasCategory("CFF List"), _
                containerShp.HasCategory("Swimlane"), _
                containerShp.HasCategory("Phase"), _
                containerShp.Name
            aryMemberIDs = containerShp.ContainerProperties.GetMemberShapes(Visio.VisContainerFlags.visContainerFlagsDefault)
            For iMember = 0 To UBound(aryMemberIDs)
                Set memberShp = Visio.ActivePage.Shapes.ItemFromID(aryMemberIDs(iMember))
                If memberShp.OneD = False Then
                    Debug.Print , aryMemberIDs(iMember), memberShp.Name
                End If
            Next iMember
        Next iContainer
    End Sub

    Notice the new page method GetContainers() which returns an array of container shape IDs.  These container shape IDs are then used to retrieve the actual shapes, and the new shape collection ContainerProperties has a method GetMemberShapes() which will retrieve the desired member shapes.

    There is also a new ShapeSheet function, =CONTAINERMEMBERCOUNT(), which returns the number of shapes contained within it.

    Displaying the Header Text

    As stated earlier, the Swimlane master has a user-defined cell, visHeadingText, which contains a reference to the text of a sub-shape (Sheet.7 in the Master).  However, the Phase master does not, therefore I added the visHeadingText user-defined cell to the Phase master in the local stencil, with the same formula, because the text edit target shape in that master is also Sheet.7.

    User.visHeadingText = SHAPETEXT(Sheet.7!TheText)

    This means that you can then edit the flowchart masters, for example the Process master, to display the enclosing Phase shape in a new Shape Data row, in the same manner as the Function Shape Data row.

    imageLike before, these edits were done to the master on the local document stencil.

    A useful consequence of having the Phase in each shape is that these values will now be exposed to the Visio reporting tool.

    Controlling Visibility of the Shape Data Rows

    To avoid the needless display of Shape Data rows when they are not relevant, I decided to add formlae to the Function and Phase Shape Data rows.

     image

    Note that the values display #REF! in the master ShapeSheet, which makes it reasonable to assume that we can use the ISERROR() function to determine whether or not there is an enclosing swimlane:

    Prop.Function.Invisible = ISERROR(CONTAINERSHEETREF(1,"Swimlane")!NAME())

    A similar formula was added to Prop.Phase.Invisible: = ISERROR(CONTAINERSHEETREF(1,"Phase")!NAME())

    Synchronizing the Flowchart Shape Fill Color with the Swimlane

    Finally, I get to set the fill format ell values so that the Process master is synchronized with the enclosing swimlane.

    image

    The Fill Format section of the Process master has several modified formulae in order to synchronize the fill color wit the enclosing swimlane.  Fortunately, I have already setup a cell which tells me if the shape is inside a swimlane or not, so it is a simple matter to refer to that value to evaluate whether to apply the fill properties of the enclosing swimlane, or just to use the default values from a theme.

    image

    So, there we go, we can now enhance the Cross Functional Flowchart template with just ShapeSheet functions … the new containment features of Visio 2010 are really useful additions to the rich developer interface … enjoy!