How to Run VBA Macros from a Ribbon Button in Visio 2010

A colleague, who is an experienced Visio tutor, asked me how to hook VBA macros to the Fluent UI Ribbon. He figured that it must\should be easy, but he can’t see where it is done. Well, this question has resonance because a plea was made to the Microsoft Visio team at our recent MVP Conference that this often asked for feature should be made easy for the macro developer. Now, I must admit that I have been so busy writing .Net Visio Add-Ins and following the pattern for the Fluent UI in VSTO C# code, that I had not appreciated just how convoluted the calling of VBA from a Ribbon button is. So, I thought I would offer a way of doing this in a simple (I hope!) way.

I will show how this can be done for a Visio drawing or template that contains VBA macros, and how this can be also be used for a Visio stencil that contains the code. I know that some macro developers put their code into drawings, but I would always recommend putting the code into a stencil, if the code is to be used by multiple drawings.

The Visio team’s Insight Blog has some useful background at http://blogs.msdn.com/b/visio/archive/2010/02/24/user-interface-extensibility-in-visio-2010.aspx , but it only has one fleeting mention of VBA macros:

For VBA, you simply provide the contents of a RibbonX XML as a string directly to Visio through the CustomUI property.

So, what does that mean? How do you actually do it?

Well, there is some sample code in the Visio SDK, which can be downloaded from http://msdn.microsoft.com/en-us/library/ff758690.aspx

I used this code as a starting point to develop my classes and module that I have included in the download ( see https://skydrive.live.com/redir.aspx?cid=3350d61bc93733a9&resid=3350D61BC93733A9!2388&parid=3350D61BC93733A9!197 ) for this article. The principle is that the opening of the document will load the custom UI, which will then be removed when the document is closed. I have included the public subs LoadRibbon and UnloadRibbon so that you can set the UI without closing and opening the document repeatedly.

The only change that is required to the code if it is moved into a stencil, id for the ThisDocument parameters to be replaced with Nothing. This will have the effect of enabling any code that is in the stencil whilst it is open. Of course, code in the stencil can be used for any open drawings.

There are four areas in the Fluent UI that can call VBA code:

  • The Ribbon
  • The Backstage View
  • The Context Menus
  • The Commands

I have provided an example for each. If you want to read about the principles of Fluent UI design, then you could start here http://msdn.microsoft.com/en-us/library/cc872782.aspx , but I am only showing basic buttons in this example. Feel free to expand the range of controls to suit your requirements…

Another useful resource is Office 2010 Help Files : Office Fluent User Interface Control Identifier at http://www.microsoft.com/downloads/en/details.aspx?familyid=3F2FE784-610E-4BF1-8143-41E481993AC6&displaylang=en . This is a series of Excel files that list all of the built-in control Ids for all of the Microsoft Office documents, and one of them, VisioControls.xlsx, lists those for Visio.

I decided to create some methods in the CustomUI module to return XML snippets for various parts of XML string required to build the Ribbon xml:

I included carriage return line feeds (vbCrlf) and tabs (vbTab) in the XML to make it easier to understand.

All of these XML snippets are called from the getRibbonXML method, so you only need to edit this function to modify the UI. In fact, I have included four parameters in the header of getRibbonXML to make it easier to switch on/off each of the four Fluent UI elements:

Private Function getRibbonXML(ByVal includeCommands As Boolean, _  ByVal includeRibbonTab As Boolean, ByVal includeBackstage As Boolean, _  ByVal includeContextMenus As Boolean) As String

This function is called from IRibbonExtensibility_GetCustomUI method in the Ribbon class.

The Ribbon

You can add groups to existing tabs in the ribbon, but you will most probably want to add your own tab with groups and buttons on it. In my example, I have created a new tab, labeled Custom Ribbon Tab, with a single group, labeled Custom Group, containing a large button, Macro 1; two small buttons, Macro 2 and Macro 3; and a split button, Macro 4.

In fact, the split button contains two menu items, Macro 4 and Macro 5.

All of the buttons have an icon, see the Office 2010 Add-In : Icons Gallery at http://www.microsoft.com/downloads/en/details.aspx?displaylang=en&FamilyID=2d3a18a2-2e75-4e43-8579-d543c19d0eed , and each has a supertip (a longer description) that is displayed on mouse over. Note that the FriendlyName, My custom Fluent UI, is entered in the CustomUIStart method in the CustomUI module.

The code that creates this button can be found in getRibbon method of the Ribbon class:

        'Open the Ribbon element
        strGetRibbonXML2 = getRibbonBegin
        'Open the Tab element
        strGetRibbonXML2 = strGetRibbonXML2 & getTabBegin(False, "tab1", "Custom Ribbon Tab")
        'Open the Group element
        strGetRibbonXML2 = strGetRibbonXML2 & getGroupBegin(False, "group1", "Custom Group")
        'Add custom buttons as required
        strGetRibbonXML2 = strGetRibbonXML2 & getButton( _
            "customMacro1", "Macro 1", "This is macro 1", "GroupSynchronizeWithSite", True)
        strGetRibbonXML2 = strGetRibbonXML2 & getButton( _
            "customMacro2", "Macro 2", "This is macro 2", "GroupViewsInfoPath", False)
        strGetRibbonXML2 = strGetRibbonXML2 & getButton( _
            "customMacro3", "Macro 3", "This is macro 3", "PostReply", False)
        'Open the split button element
        strGetRibbonXML2 = strGetRibbonXML2 & getSplitButtonBegin("customSplit1")
        'Open the menu element
        strGetRibbonXML2 = strGetRibbonXML2 & getMenuBegin("customMenu1")
        'Add custom buttons as required
        strGetRibbonXML2 = strGetRibbonXML2 & getButton( _
            "customMacro4", "Macro 4", "This is macro 4", "VisioDiagramGallery", False)
        strGetRibbonXML2 = strGetRibbonXML2 & getButton( _
            "customMacro5", "Macro 5", "This is macro 5", "VisioTransparency", False)
        'Close the menu element
        strGetRibbonXML2 = strGetRibbonXML2 & getMenuEnd
        'Close the split button element
        strGetRibbonXML2 = strGetRibbonXML2 & getSplitButtonEnd
        'Close the Group element
        strGetRibbonXML2 = strGetRibbonXML2 & getGroupEnd
        'Close the Tab element
        strGetRibbonXML2 = strGetRibbonXML2 & getTabEnd
        'Close the Ribbon element
        strGetRibbonXML2 = strGetRibbonXML2 & getRibbonEnd

 This produces the XML snippet:

<ribbon>  <tabs>  <tab id="tab1" label="Custom Ribbon Tab">  <group id="group1" label="Custom Group">  <button id="customMacro1" label="Macro 1" supertip="This is macro 1" size="large" imageMso="GroupSynchronizeWithSite" onAction="OnAction"/>  <button id="customMacro2" label="Macro 2" supertip="This is macro 2" imageMso="GroupViewsInfoPath" onAction="OnAction"/>  <button id="customMacro3" label="Macro 3" supertip="This is macro 3" imageMso="PostReply" onAction="OnAction"/>  <splitButton id="customSplit1">  <menu id="customMenu1">  <button id="customMacro4" label="Macro 4" supertip="This is macro 4" imageMso="VisioDiagramGallery" onAction="OnAction"/>  <button id="customMacro5" label="Macro 5" supertip="This is macro 5" imageMso="VisioTransparency" onAction="OnAction"/>  </menu>  </splitButton>  </group>  </tab>  </tabs> </ribbon> 

The Backstage View

I have given an example for two columns in the Backstage View, in a new tab labeled Custom Tab. I have included a large button, labeled BS Macro 1 and; and two normal buttons, labeled BS Macro 2 and BS Macro 3.

The code that creates this button can be found in getRibbon method of the Ribbon class:

 'Open the backstage element  strGetRibbonXML3 = getBackstageBegin  'Open the tab element  strGetRibbonXML3 = strGetRibbonXML3 & getTabBegin(False, "tab2", "Custom Tab")  'Open the column element  strGetRibbonXML3 = strGetRibbonXML3 & getBackstageColumnBegin(One)  strGetRibbonXML3 = strGetRibbonXML3 & vbTab & getGroupBegin(False, "group2", "Custom Group 2")  strGetRibbonXML3 = strGetRibbonXML3 & getBackstagePrimaryItemBegin  'Add custom buttons as required  strGetRibbonXML3 = strGetRibbonXML3 & vbTab & vbTab & getButton( _ 
 "customBMacro1", "BS Macro 1", "This is bs macro 1", "GroupSynchronizeWithSite", False)  'Close the primaryitem element 
 strGetRibbonXML3 = strGetRibbonXML3 & getBackstagePrimaryItemEnd
 'Close the group element  strGetRibbonXML3 = strGetRibbonXML3 & vbTab & getGroupEnd  'Close the column element  strGetRibbonXML3 = strGetRibbonXML3 & getBackstageColumnEnd(One)  'Open the column element  strGetRibbonXML3 = strGetRibbonXML3 & getBackstageColumnBegin(Two)  'Open the group element  strGetRibbonXML3 = strGetRibbonXML3 & vbTab & getGroupBegin(False, "group3", "Custom Group 3")  'Open the topitems element  strGetRibbonXML3 = strGetRibbonXML3 & getBackstageTopItemsBegin  'Add custom buttons as required strGetRibbonXML3 = strGetRibbonXML3 & vbTab & vbTab & getButton( "customBMacro2", "BS Macro 2", "This is bsmacro 2", "GroupViewsInfoPath", False) strGetRibbonXML3 = strGetRibbonXML3 & vbTab & vbTab & getButton( "customBMacro3", "BS Macro 3", "This is bsmacro 3", "PostReply", False) 
 'Close the topitems element  strGetRibbonXML3 = strGetRibbonXML3 & getBackstageTopItemsEnd  'Close the group element 
 strGetRibbonXML3 = strGetRibbonXML3 & vbTab & getGroupEnd  'Close the column element  strGetRibbonXML3 = strGetRibbonXML3 & getBackstageColumnEnd(Two)  'Close the tab element  strGetRibbonXML3 = strGetRibbonXML3 & getTabEnd  'Close the backstage element  strGetRibbonXML3 = strGetRibbonXML3 & getBackstageEnd 

This produces the XML snippet:

 <backstage>
 <tab id="tab2" label="Custom Tab">  <firstColumn>  <group id="group2" label="Custom Group 2">  <primaryItem>  <button id="customBMacro1" label="BS Macro 1" supertip="This is bs macro 1" imageMso="GroupSynchronizeWithSite" onAction="OnAction"/>  </primaryItem>  </group>  </firstColumn>  <secondColumn>  <group id="group3" label="Custom Group 3">  <topItems>  <button id="customBMacro2" label="BS Macro 2" supertip="This is bsmacro 2" imageMso="GroupViewsInfoPath" onAction="OnAction"/>  <button id="customBMacro3" label="BS Macro 3" supertip="This is bsmacro 3" imageMso="PostReply" onAction="OnAction"/>  </topItems>  </group>  </secondColumn>  </tab>  </backstage> 

The Context Menus

The second tab, named contextmenus, of the VisioControls.xlsx Excel workbook lists the possible contexts for the menus. In my example, I have used ContextMenuShape so that the button, labeled My Button, will be available on the Action Menu whenever a 2D shape is selected.

The code that creates this button can be found in getRibbon method of the Ribbon class:

 'Open the context menus group element  strGetRibbonXML4 = getContextMenusBegin  'Open the context menu element 
 'See the contextmenus worksheet in C:\Office 2010 Developer Resources\Documents\Office2010ControlIDs\VisioControls.xlsx 
 strGetRibbonXML4 = strGetRibbonXML4 & getContextMenuBegin("ContextMenuShape")  'Include any menu buttons required  strGetRibbonXML4 = strGetRibbonXML4 & getButton( _  "customContextMacro1", "My Button", "This is my context menu macro", _  "MindMapChangeTopic", False)  'Close the context menu element  strGetRibbonXML4 = strGetRibbonXML4 & getContextMenuEnd  'Close the context menus group element  strGetRibbonXML4 = strGetRibbonXML4 & getContextMenusEnd 

This produces the XML snippet:

 <contextMenus>  <contextMenu idMso="ContextMenuShape">  <button id="customContextMacro1" label="My Button" supertip="This is my context menu macro" imageMso="MindMapChangeTopic" onAction="OnAction"/>  </contextMenu>  </contextMenus> 

The Commands

You can disable or re-purpose built-in commands too. For example, I have provided an example of disabling the Bold button and re-purposing the Copy command …

… so that it runs some custom code:

The code that creates this button can be found in getRibbon method of the Ribbon class:

 'Open the Commands element  strGetRibbonXML1 = getCommandsBegin  'Add Command actions as required  'You can disable commands  strGetRibbonXML1 = strGetRibbonXML1 & getDisableCommand("Bold")  'You can re-purpose commands  strGetRibbonXML1 = strGetRibbonXML1 & getRedirectCommand("Copy")  'Close the Commands element  strGetRibbonXML1 = strGetRibbonXML1 & getCommandsEnd 

This produces the XML snippet:

<commands>  <command idMso="Bold" enabled="false"/>  <command idMso="Copy" onAction="CommandOnAction"/>  </commands> 

Calling Your VBA Code

You may have noticed that the onAction attributes in all of the above XML have the value OnAction or CommandOnAction. I think that centralising the calls for the buttons and the commands like this, makes it easier to follow. I show the OnAction method below, and the CommandOnAction is very similar. So, all you have to do is ensure that you have a Case option for the ID of any control (button) that you have in the Ribbon XML.

Public Sub OnAction(ByVal control As IRibbonControl)
' OnAction
'
' Abstract - This method is a callback specified in the custom UI XML file.
' It is called by Visio when the associated button defined in the XML is pressed.
'
' Parameters
' control The Ribbon UI control that was activated
 ' To execute a VBA macro, use the Document.ExecuteLine method.
 ' For example: Document.ExecuteLine("ThisDocument.HelloWorld");  Select Case control.ID Case "customMacro1"
 'Call to your code
 ThisDocument.ExecuteLine "HelloWorld" Exit Sub Case "customMacro2"
 'Call to your code Case "customMacro3"
 'Call to your code Case "customMacro4"
 'Call to your code Case "customMacro5"  'Call to your code
Case "customContextMacro1"  'Call to your code End Select 
MsgBox control.ID, vbInformation, "OnAction" End Sub

Conclusion

I hope that this provides an easy (well, not quite as easy as I would have liked) way for you to hook your VBA macros into the ribbon.

You have four different areas in the Fluent UI that you can utilise, so, have fun.

50 Responses to “How to Run VBA Macros from a Ribbon Button in Visio 2010”

  1. Adi Says:

    Thank you very much for taking the time to document this but… Oh my goodness, it’s open heart surgery! How about I just pray that they fix this in the next release! Phew…

  2. Julien Says:

    Thanks for this! It has been enormously helpful for my own Visio VBA solution.

    Do you have any experience using custom icons with the technique described above? It seems I may have uncovered a bug where none of my image callbacks (loadImage or getImage) are ever being called by Visio (even after Invalidate!), leaving me high in dry in automation land.

    Any thoughts/experience with this? Thanks!
    j.

  3. Bernie Says:

    David, great blogs, glad I stumbled across them, this one in particular.
    I’m not sure that this entry’s results are exactly what I had hoped to achieve; I would like to associate/ deliver “trigger” code (_documentOpened, _beforeDocumentClose) to insert r remove the custom ribbon within a stencil, and would hope to only have that available if the stencil item is actually added to an open diagram, making it “one of ours”. The code to invoke, I would like to deliver via a VS2010 com addin that would be installed in the client environment. That way, only those explicitly installing and enabling the addin and further, opening and pasting an item from our macro-enabling stencil to “embed/ enable” the macro code, would see the additional ribbon elements and only in those documents for which it was valid. Reopening such a document with that pasted stencil item would automatically reopen the additional Ribbon UI elements and customizations, regardless if the stencil was open at the time or not.
    Is this viable or in line with this capability?

    • davidjpp Says:

      You are mixing a COM Addin and Macros?
      Why not do a VSTO addin for the lot?
      Have you looked at Persistent Events?
      http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=12365
      http://office.microsoft.com/en-us/visio-help/HV080351058.aspx

      • Bernie Says:

        I had thought the vsto would be present and shown for any document, and the stencil macros would provide a per-document means of presenting the custom UI. These were initial ideas when attempting to address a complex problem with a new UI that I am still trying to understand with regards to integration with code (com/ vba). How to best address my solution, I am still formulating the approach, thus I am very thankful for your blog and for these additional links -I will look into each to see if part of my puzzle pops. I do have VS2010 for the vsto/ com addin development; I too would prefer a single point of delivery but my above perception had me thinking otherwise.
        Thank you again.

      • Bernie Says:

        the 1st link to the 2010 sdk -the example code I followed in there to build the visio-ribbon customization test vsto had ‘dead code’ and the intent, active UI only for a specific document, does not reflect in the result because no code is set to callback to the SDK’s sample “DemoCustomUIStart/Stop” and this SDK hadn’t been updated since march ’10.
        what in the SDK were you inkling at, is there a specific section I should look into re this approach? Thanks again

      • davidjpp Says:

        I think that you should be able to add PersistentEvents to you Stencil (I normally add it to my Template), then you can listen for the opening and closing of your stencil from your Add-in, and then modify the UI to suit.
        If you could use the PersistentEvent tool, then the Event = DocumentOpened, Target Addon = QueueMarkerEvent, Arguments = /solution=PEvtTest /cmd=1 (or similar) … you should then be able to listen for the QueueMarkerEvent

      • Bernie Says:

        excellent- I’ll give that a try and let you know of the success. Thank you for the guidance.
        How do I “add” PersistentEvents to a stencil? you add to a template, same difference? pro/con to stencil vs. template?

      • davidjpp Says:

        I just tried adding Persistent Events to a stencil, then checked them with the following VBA code:

        For Each evt In doc.EventList
        If evt.Persistent = True Then
        Debug.Print evt.Persistable, evt.Persistent, evt.Action, evt.Event, evt.Target, evt.TargetArgs, doc.Name
        End If
        Next
        Next

        Unfortunately, opening a stencil did not fire the persistent event like it does when opening a normal document does.
        …. I’m still pondering…..

      • davidjpp Says:

        Public Sub StartListening()
        Set mapp = Visio.Application
        End Sub

        Public Sub StopListening()
        Set mapp = Nothing
        End Sub

        Private Sub mapp_BeforeDocumentClose(ByVal Doc As IVDocument)
        If Doc.DocumentSheet.CellExists(“User.MySpecialStencil”, Visio.VisExistsFlags.visExistsAnywhere) Then
        Debug.Print Doc.Name, “closed”
        End If
        End Sub
        okay, my thoughts are that your addin should listen to the Application DocumentOpened and BeforeDocumentClosed events, and add a user cell into the documentsheet.
        Then you can check for the presence of this User cell when ever a document is opened or closed.
        The following is VBA, but you should use AddAvise with vb.net or just Add with C# :

        Dim WithEvents mapp As Visio.Application

        Private Sub mapp_DocumentOpened(ByVal Doc As IVDocument)
        If Doc.DocumentSheet.CellExists(“User.MySpecialStencil”, Visio.VisExistsFlags.visExistsAnywhere) Then
        Debug.Print Doc.Name, “opened”
        End If
        End Sub

      • Bernie Says:

        thanks David, I had found the SDK tool as you replied… the above code will help greatly, along the same lines I had thought, where dropping a stencil-specific item into the document would ‘validate’ it as customUI using doc (you mention user cell above),

  4. Bernie Says:

    I found in the VB macro editor, in the “this document” codepage, the attribute/ property PersistsEvents. I am guessing I set this to True. I have not found what you allude to as the “PersistentEvent tool”… still looking.

    • davidjpp Says:

      When you install the SDK, you will get an extra group SDK Tools on the Devloper tab (assuming that you have found the Run in Developer mode switch in the File \ Options \ Advanced )

      • brrrknee Says:

        David, of the 3 tools, only the “event monitor” starts, the Persistent Events and Print Shapesheet tools do nothing. macro recording doesn’t capture clicks in the ribbon or its commands either. what might be conflicting on my environment?
        I’m just trying to find a way to simply -as commandbuttons/ commandbars were -locate and ‘click’ the orgchart “relayout” and a few other org chart buttons from my VBA code. I cannot believe the dearth of info re the programmatic access to the ribbon, but there are plenty of examples to customize it. I see one example where someone is forced to use the MS IAccessible interface of oleacc.dll; why is such a simple task taken to such an excruciating level of complexity to perform in the new UI? What am I missing here?
        Thank you for the assistance.

      • davidjpp Says:

        I had a problem recently where my Persistent Events and Print SHapeSheet tools were not working either … I had installed the 64bit version of the SDK accidently, and installing the 32bit version fixed that.

        I found this blog article – http://www.wordarticles.com/Shorts/RibbonVBA/RibbonVBADemo.php which has code to give you access to the Ribbon from VBA – the only change I had to do in Visio was to insert Application. in front of CommandBars(“Ribbon”).

        As far as running the Org Chart addon from VBA … I guess you could if you knew the arguments for each command.

      • brrrknee Says:

        David, I am not “running the Org Chart addon” from VBA, but navigating the org chart Command Bar/ Buttons to apply layouts and relayout after resizing the elements. very simple with command bars and buttons, 5-10 lines, no need to know any APIs. However, now it seems to “navigate to and press” an org chart button, I have to write large amounts of code in C/C++ to use the newer MS UI Automation (or the many lines of VBA code referenced to use MSAA IAccessible). I find no VB support for this newer accessibility interface, MS stating that UIA will replace the MSAA IAccessible interface. Not having access to the accessibility interface from the Office support VBA is difficult to grasp for rationale. The example code you link to above, is a VBA use of the “decade old (MS’ description to steer folks to the UIA)” MSAA IAccessible interface, and I may have to just go with that MSAA for the near term. I had looked at that example before, but was hunting for a VBA implementation of the UIA ‘newer’ technology. Apparently MS is not looking to support its office VBA products in this fashion. Thank you for your help, I am much better informed through you!

  5. Bernie Says:

    ahhh, in the SDK… 🙂

  6. brrrknee Says:

    I’m running 64b office on a 64b system w win7 64b. therefore I loaded the 64b SDK; it isn’t working as noted above; does the SDK have a 32b limitation for those 2 tools?

  7. brrrknee Says:

    David, just noticed in the active add-on tab, the ‘working’ active add-on “monitor events” is an .exe, while the other two active ‘non responsive’ add-ons “Print Shape Sheet…” and Persistent Events…” are both .VSLs is there a setting I might have turned off disabling VSLs?

  8. Shaul Says:

    Hi David,
    Great post!

    I can’t download your example, maybe you can re-upload it?

    Thanks in advance
    Shaul

  9. musoitank Says:

    Please re-upload the .vsd example – someone?

  10. frankki Says:

    I really love this (working!) sample, but I struggle in getting the split button large.
    Could it be that you forgot a variable for that or is it me just being blind?
    Thanks for a short advise

    • davidjpp Says:

      size=”large” ?

    • brrrknee Says:

      moreover, this example is written off of stubs of the old interface, and nowhere is there a pure example written using the new test/automation UI. why cannot the MS/ MVP teams see that stating the new UI is the way of the future development would make using such old-form examples a wary step for us? We still need to access buttons and commands. HOW DO WE DO IT using purely the new test/automation approach and not have to rely upon past techniques?

      • davidjpp Says:

        MVPs are not a team … we are all independent (and unpaid) Visio enthusiasts.
        I’m not sure what you are saying?
        If you are building a system that needs to be subjected to a test harness, then you need to have separation of concerns.
        All of the UI elements should be distinct from any calls that you make, thus you should be able to test the calls directly, without simulating clicking or selecting any UI elements.
        This is one of the reasons why I like to use the MVVM model with WPF … even within Visio add-ins.
        Hooking in the Ribbon within a VIsio add-in is far easier than the VBA code that I offered, but I recognise that some people want to use the ribbon with VBA too.

      • brrrknee Says:

        I understand that the MVPs are not ‘a team’ that was my mistake in this description.
        I don’t want to have to do any of this, but since I had a simple command-bar /button access in a prior, very important, macro. now it doesn’t work in ribbon interfaces, so rather than the few lines of code that had previously worked, I am now wrestling with all of this workaround to simply “find the button and click it” from within the macro vba has become quite frustrating, and examples of UIA to do this ‘find the button and click it’ vs. the ribbon, I have yet to find. Your forum is great, I am merely frustrated with the blind MS push to the ribbon and the UIA with apparent disregard to the impact of simple business users.

      • davidjpp Says:

        I actually agree with you about that. It is really disappointing that there is not a simple way to add a button to the Ribbon to run a VBA macro in Visio. It is something that several of us MVPs have complained to Microsoft about. My code was aimed at providing a way to create/remove buttons when the document opens/closes. My fellow MVP, Chris Roth aka VisGuy, has responded to me as follows:

        Ok, I found a simpler way after hacking a bit.

        1. Create a custom tab, group, etc.
        2. Export the custom UI
        3. Edit it in notepad to call your macro

        The trick is to add or edit the onAction xml, which I learned from David’s article. Here I’ve set it to ThisDocument.DoStuff.

        You can now import this Ribbon UI. If the VBA macro is present, it will run. If not, it won’t but the button will still be there.

      • brrrknee Says:

        even simpler: take an existing, in fact pre-existing commandbar /buttons, the OrgChart Wizard. I do not want to reinvent the wheel, I just look up the commandbar that ‘is’ the wizard (almost always found at cb index 100 or 101) and select its ‘relayout’ button. no new commands or buttons. just “find org chart stuff” select and execute “relayout”. a handful of lines of code pre-ribbon; all ribbon-influenced examples, though not addressing this locate-subject but a build-it custom focus, are many many pages of code that obfuscate the simple request: locate and execute a known-to-exist command. now I am confounded in finding MSAA (the “old” UI accessibility) and/or UIA (the “new” and to replace MSAA UI accessibility) means of finding the reshuffled needles in the haystack called the ribbon. I’d also rather code to the UIA to obviate any need to rewrite after MSAA methods are tossed aside like commandbars… it happened once, it’ll happen again.
        David, I do appreciate your feedback and ideas, thank you, I hope to be able to read more of your blogs and of your partners’.

  11. brrrknee Says:

    Working through several differing samples, I’ve noticed some things that you likely can clarify:

    When I add “oleacc.dll” as a resource to my visio 2010 VBA reference list(the library that implements IAccessible according to MS, etc.), the exposed interfaces (within VBA IDE for oleacc.dll) do not include IAccessible.
    While code that uses the IAccessible such as x.accName and x.accParent seem to be working, I am hitting several cases where the IAccessible element has no name where I believe it should, (perhaps it is a IDispatch object?) but cannot find the means to view the objects.

    Also, one sample I have found helpful uses the x.accNavigate method to get to “FirstChild” of an IAccessible element. MS on 9/7/11 posted a statement (perhaps earlier) that .accNavigate is deprecated and will trigger an error. No suggestions online other than “use other methods”… it is my understanding that this call is essentially “getChildren” and then referencing the top/first of that list; am I close? If not how(which .accXxxx methods) can I achieve the “firstChild” nav utility?
    If I could ‘see’ the object via the IDE it would help me here, but as noted at top, the VBA IDE is not edit-or-view aware of IAccessible methods and properties, though the compiled code seems to be… perplexing.
    Thanks David!

  12. spols Says:

    Great article.
    However, I can’t seem to collect the sample vsd download – skydrive.live.com returns: “An error has occurred. Please try again.”

  13. Kedas Says:

    This requires ‘IRibbonControl’ as parameter -> Public Sub OnAction(ByVal control As IRibbonControl) in VBA
    Is there a way you call a function in the add-in and then this function calls a macro like Sub Refresh() ?

    This to stay compatible with existing documents (from templates) with existing macros.
    I can change the templates but not all the documents.

    Thanks

  14. Prasanna Says:

    David,
    Thanks for the help on this topic.
    I had a couple of issues; The Macro 1,2,3,4 buttons only execute the code once.
    For example, while debugging, if I stop the code, make changes to my custom macro and click again, it won’t work.
    I have to restart Visio again for that button to work.
    Also, if I only close the Visio sheet (and not Visio program), and reload the sheet, I have two ribbon tabs of the same name instead of the single “Custom Ribbon” tab.
    Any clue as why this may be happening.

    • davidjpp Says:

      Are you running the code in a drawing or a stencil ?
      You should run UnloadRibbon() before editing, then LoadRibbon() when you have finished editing your macros

  15. francisco Says:

    I’ve tried to build “two groups” usiing “begin and end group functions” but it doesn’t works. Aparentilly the xml is correct, comparing with others xml in microsoft website. Do you know the probable cause?

    • Gilles Says:

      Hi, I had exactly the same issue as Francisco:

      If includeContextMenus Then
      ‘Open the context menus group element
      strGetRibbonXML4 = getContextMenusBegin
      ‘Open the context menu element
      ‘See the contextmenus worksheet in C:\Data\Perso\Toolbox\@Visio\Macros\VisioControls.xlsx

      strGetRibbonXML4 = strGetRibbonXML4 & getContextMenuBegin(“ContextMenuShape”)
      ‘Include any menu buttons required
      strGetRibbonXML4 = strGetRibbonXML4 & getButton( _
      “SelectSimilarShapesByMaster”, “Select Shapes with same Master”, “Select all Shapes which descend from the same Master”, _
      “SourceControlShowHistory”, False)
      ‘Close the context menu element
      strGetRibbonXML4 = strGetRibbonXML4 & getContextMenuEnd

      ‘Open the context menu element
      strGetRibbonXML4 = strGetRibbonXML4 & getContextMenuBegin(“ContextMenuShape1D”)
      ‘Include any menu buttons required
      strGetRibbonXML4 = strGetRibbonXML4 & getButton( _
      “SelectSimilarShapesByMaster”, “Select Shapes with same Master”, “Select all Shapes which descend from the same Master”, _
      “SourceControlShowHistory”, False)
      ‘Close the context menu element
      strGetRibbonXML4 = strGetRibbonXML4 & getContextMenuEnd

      ‘Close the context menus group element
      strGetRibbonXML4 = strGetRibbonXML4 & getContextMenusEnd

      strGetRibbonXML = strGetRibbonXML & strGetRibbonXML4
      End If

      does not work, even if each the xml code seems to be correct.

      If I try to add the menus individually it works.
      If I create two “getContextMenusBegin / getContextMenusEnd” blocks, only the last works.

      But I do not manage to have both menus working.

      Can somebody help?

  16. Roman Says:

    francisco, did you find a solution? I am having the same trouble.

  17. Thomas Says:

    Thanks for this excellent tutorial.
    I added a checkbox to your demo and want to initialize its state with a getPressed callback.
    But this is never called.
    Same problem with getLabel while onAction and onLoad are working properly.
    Please find more information here:
    http://visguy.com/vgforum/index.php?topic=4954.0
    Any idea?

    Thanks,
    Thomas

  18. Martin Says:

    David,
    Great article, thanks a lot. I’m not sure on how I can have this ribbon extension to appear in visio on all times. I have a solution in mind like a Word Template placed in the Auto-Start folder of Word which contains the UI extension and will be loaded on every start of Word.
    Can you guid me in the right direction maybe with even a runable sample?
    Thanks so much for your help.
    Martin

  19. RichZ Says:

    So, I’m still modifying my code… but I seem to have the same problem that someone else reported. For whatever reason, the buttons on the ribbon will only run my macro once. Currently I am just testing, so I’ve placed your code within the VSD document, but my macros are stored in a VSS; the stencil of course opens upon DocumentOpen, so that should not be the issue.

    I can only imagine that there is some issue with having this code stored in the local document, but calling a macro in a stencil file?

    • RichZ Says:

      Hmm..; well I tested my previous theory and this is not the case. I placed the macros local to the VSD document, and the results are the same; the buttons I’ve added to the ribbon stop working after I click any of the buttons once. Very odd.

  20. RichZ Says:

    okay, I’ve narrowed down the issue. It seems that the macros I’ve added to the custom ribbon work until I call a macro that drops a stencil on the page that has an activex textbox inside of it. I’m not sure why this breaks ribbon. I’ve tried doing this using the code you have shared with us here, and also by using the example provided by Microsoft in the SDK. If anyone has any thoughts on this then I would greatly appreciate any advice you may have.

  21. Stephen Says:

    Can the methods developed here also be used in Excel?


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

davecra.wordpress.com/

Microsoft Office 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: