Using Custom Context Menus with VSTO

Context Menu, Right Mouse Action, call them what you want.  When you right click on a Visio shape you are given options that will specifically target that shape.  From a developers point of view, yes you can customize this menu…in a number of different ways.  For this article I want to focus on

Defining Actions in the ShapeSheet

The ShapeSheet provides a developer with a powerful tools set for developing shape behavior.  One of these behaviors is the ability to define Actions, which are your custom menu items that show up when you right click on a shape.

Defining Actions can trigger the execution of formulas built into a shape to get the shape to do something, like in this simple example where we toggle the display of a value.

Right click on the shape, choose our ‘Set to 20’ action and the formula that we created changes the value.

image

Right click and set it back to 10.

image

We did this with two simple formulas in the ShapeSheet by adding an Action row and defining the Action formula which does the work, and the Menu formula which ensures the user is seeing the correct phrase in the menu when they right click.

image

This is good but what if we want to have this Action call into our code so that we can do some more complex things…no problem.

We need two things to make this happen.  We need to setup our Action in the ShapeSheet to trigger a custom event and we need to setup a listener in our code to listen for this custom event.

The Action

For the Action row in the ShapeSheet we define the Menu formula and the Action formula just as we did before.  In this case the Menu formula is just simple text which the user will see as a menu item when they click on our shape.  The Action formula is a little more complex as we need Visio to inform our code that a user click this Action.  RUNADDONWARGS is a special ShapeSheet function that allows us to call an add-on and pass it arguments.  What you need to know here is that there is a built in add-on called QueueMarkerEvent which triggers a MarkerEvent to fire within the Application instance.  If we combine the two, we can trigger this MarkerEvent to fire when the user clicks on our Action, passing us arguments that we may need to determine what to do within our code.

image

The formula that you see above looks like this:

RUNADDONWARGS("QueueMarkerEvent","/app=RMASample /args=DoSomething")

The first parameter is the name of the add-on that we need to execute.  The second parameter allows us to send along additional information with the event.  This is helpful because when the event fires our code can get this string, parse it, and do some action based on it.  Notice how I have two arguments in this string:

/app=RMASample

This is helpful because this allows my code identify this action as an action that has come from my shapes.  Remember this is a generic mechanism and your application might not be the only one firing these events.

/args=DoSomething

Now that I have identified this action as mine, I want to know exactly what to do.  All I have to do is parse this out of the event string and then my code knows that to do next.

The Event Handler

Now that our ShapeSheet Action is configured, we need to setup the event handler in our code.

The most common configuration is to connect to the MarkerEvent from the Application object.  As you can see here in this most basic example, I have connected to the MarkerEvent as soon as my VSTO add-in is invoked.  The event handler simply displays the ContextString argument in a messagebox.

image

This ContextString argument contains the argument string from the Action in the ShapeSheet…but it also contains a few other helpful bits of information.

Here is what the value of my ContextString looks like at runtime:

/doc=1 /page=1 /shape=Sheet.1 /shapeu=Sheet.1 /app=RMASample /args=DoSomething

As you can see I have my /app= and /args= values but I also have some additional values.

/doc= tells you the index of the document from the Application.Documents collection.

/page= tells you the index of the page from the Document.Pages collection.

/shape= tells you the name of the shape from the Page.Shapes collection.

Parsing this string to pull out all these values gives your code everything you need to know about the shape that triggered the Action.

RMASample.zip