Creating a .NET Visio Add-In with Custom Menus

by Aaron D-H 30. January 2009 06:23

In the course of creating a number of demonstrations for Visio 2007, I came across the need to create an add-in for Visio in .NET.

Basic .NET AddOn technology is fairly well documented in the arcticle About the Visio 2003 Managed Code Interop Tutorial [Visio 2003 SDK Documentation], but creating custom menus and caturing menu item events in the add-in is not very clear. In this posting I will show you what I discovered and how to make this all work.

The basic code from an Office AddOn begins with a .NET Class that implements the interface Extensibility.IDTExtensibility2. You can create an AddOn project in Visual Studio that does not the initial work for you by selecting File > New… > Project… and in the New Project dialog box select Visual C# > Office > Outlook Add-in (as in the figure below).

The new project template should create a file called connect.cs will look something like this:

    /// <summary>

    /// The object for implementing an Add-in.

    /// </summary>

    /// <seealso class='IDTExtensibility2' />

    [GuidAttribute("48288E48-B351-4C41-9476-F5C7FF1E67CF"), ProgId("FastRefresh.Connect")]

    public class Connect : Object, Extensibility.IDTExtensibility2

    {

        /// <summary>

        ///        Implements the constructor for the Add-in object.

        ///        Place your initialization code within this method.

        /// </summary>

        public Connect()

        {

}

/// <summary>

        /// Implements the OnConnection method of the IDTExtensibility2 interface.

        /// Receives notification that the Add-in is being loaded.

        /// </summary>

        /// <param term='application'>

        /// Root object of the host application.

        /// </param>

        /// <param term='connectMode'>

        /// Describes how the Add-in is being loaded.

        /// </param>

        /// <param term='addInInst'>

        /// Object representing this Add-in.

        /// </param>

        /// <seealso class='IDTExtensibility2' />

        public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)

        {

applicationObject = application;

addInInstance = addInInst;

     }

        /// <summary>

        /// Implements the OnDisconnection method of the IDTExtensibility2 interface.

        /// Receives notification that the Add-in is being unloaded.

        /// </summary>

        /// <param term='disconnectMode'>

        /// Describes how the Add-in is being unloaded.

        /// </param>

        /// <param term='custom'>

        /// Array of parameters that are host application specific.

        /// </param>

        /// <seealso class='IDTExtensibility2' />

public void OnDisconnection(Extensibility.ext_DisconnectMode disconnectMode, ref System.Array custom)

{

}

        /// <summary>

        /// Implements the OnAddInsUpdate method of the IDTExtensibility2 interface.

        /// Receives notification that the collection of Add-ins has changed.

        /// </summary>

        /// <param term='custom'>

        /// Array of parameters that are host application specific.

        /// </param>

        /// <seealso class='IDTExtensibility2' />

        public void OnAddInsUpdate(ref System.Array custom)

        {

}

        /// <summary>

        /// Implements the OnStartupComplete method of the IDTExtensibility2 interface.

        /// Receives notification that the host application has completed loading.

        /// </summary>

        /// <param term='custom'>

        /// Array of parameters that are host application specific.

        /// </param>

        /// <seealso class='IDTExtensibility2' />

        public void OnStartupComplete(ref System.Array custom)

        {

}

        /// <summary>

        /// Implements the OnBeginShutdown method of the IDTExtensibility2 interface.

        /// Receives notification that the host application is being unloaded.

        /// </summary>

        /// <param term='custom'>

        /// Array of parameters that are host application specific.

        /// </param>

        /// <seealso class='IDTExtensibility2' />

        public void OnBeginShutdown(ref System.Array custom)

        {

}

        

        private object applicationObject;

        private object addInInstance;

    }

In the OnConnect method we will need to add code specific to the Visio application, which captures the Visio events we will need to wire up menu item events:

      

/// <summary>

        /// Implements the OnConnection method of the IDTExtensibility2 interface.

        /// Receives notification that the Add-in is being loaded.

        /// </summary>

        /// <param term='application'>

        /// Root object of the host application.

        /// </param>

        /// <param term='connectMode'>

        /// Describes how the Add-in is being loaded.

        /// </param>

        /// <param term='addInInst'>

        /// Object representing this Add-in.

        /// </param>

        /// <seealso class='IDTExtensibility2' />

        public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)

        {

try

{

applicationObject = application;

addInInstance = addInInst;

visioApplication = application as Visio.Application;

visioApplication.DocumentOpened += new Microsoft.Office.Interop.Visio.EApplication_DocumentOpenedEventHandler(visioApplication_DocumentOpened);

visioApplication.MarkerEvent += new Microsoft.Office.Interop.Visio.EApplication_MarkerEventEventHandler(visioApplication_MarkerEvent);

}

catch (Exception exception)

{

MessageBox.Show("Error during connection in FastRefresh Visio-AddIn: " + exception.Message, title);

}

}

private Visio.Application visioApplication;

void visioApplication_MarkerEvent(Microsoft.Office.Interop.Visio.Application app, int sequenceNum, string contextString)

{

Debug.Write(contextString, "MarkerEvent");

}

void visioApplication_DocumentOpened(Microsoft.Office.Interop.Visio.Document doc)

{

}

Next we need to create out custom menu. Beginning in 2002 the Visio team included a standard add-on called QueueMarkerEvent. The Microsoft article Using the QueueMarkerEvent Add-on in Visio Solutions explains how the QueueMarkerEvent add-on can be used in menus to send events to our .NET class.

private void createCustomMenu()

{

if (this.currentDocument == null)

{

//If not, check whether there are custom menus bound to the application.

if (this.visioApplication.CustomMenus == null)

{

//If not, use the Visio built-in menus.

fastRefreshUIObject = this.visioApplication.BuiltInMenus;

}

else

{

//If there are existing Visio application-level custom menus, use them.

fastRefreshUIObject = this.visioApplication.CustomMenus;

}

}

else

{

//Check whether there are custom menus bound to the document.

if (this.currentDocument.CustomMenus == null)

{

//If not, check whether there are custom menus bound to the application.

if (this.visioApplication.CustomMenus == null)

{

//If not, use the Visio built-in menus.

fastRefreshUIObject = this.visioApplication.BuiltInMenus;

}

else

{

//If there are existing Visio application-level custom menus, use them.

fastRefreshUIObject = this.visioApplication.CustomMenus;

}

}

else

{

//Use the existing custom menus.

fastRefreshUIObject = this.currentDocument.CustomMenus;

}

}

//Get the MenuSets collection.

Visio.MenuSets visioMenuSets = fastRefreshUIObject.MenuSets;

//Get the drawing window menu set.

Visio.MenuSet visioMenuSet = visioMenuSets.get_ItemAtID((int)Visio.VisUIObjSets.visUIObjSetDrawing);

//Get the Menus collection.

Visio.Menus visioMenus = visioMenuSet.Menus;

//Add a new menu before the Window menu.

Visio.Menu visioMenu = visioMenus.AddAt(7);

visioMenu.Caption = "Fast-Refresh";

//Get the MenuItems collection.

Visio.MenuItems visioMenuItems = visioMenu.MenuItems;

//Add a menu item to the new menu.

Visio.MenuItem visioMenuItem = visioMenuItems.Add();

//Set the Caption property for the new menu item.

visioMenuItem.Caption = "&Options";

//Setup the marker event to send when the menu item is envoked

visioMenuItem.AddOnName = "QueueMarkerEvent";

visioMenuItem.AddOnArgs = "FastRefresh/Options";

visioMenuItem.ActionText = "Queue Marker Event";

visioMenuItem.Enabled = true;

}

In the highlighted statements above:

Setting the AddOnName property to "QueueMarkerEvent" means that the QueueMarkerEvent add-on is called when the user selects this menu item.
The AddOnArgs property value will be the value of the ContextString parameter when the MarkerEvent handler is called.

Next we need to add a call to our createCustomMenu method to the OnConnect function as follows:

/// <summary>

        /// Implements the OnConnection method of the IDTExtensibility2 interface.

        /// Receives notification that the Add-in is being loaded.

        /// </summary>

        /// <param term='application'>

        /// Root object of the host application.

        /// </param>

        /// <param term='connectMode'>

        /// Describes how the Add-in is being loaded.

        /// </param>

        /// <param term='addInInst'>

        /// Object representing this Add-in.

        /// </param>

        /// <seealso class='IDTExtensibility2' />

        public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)

        {

try

{

applicationObject = application;

addInInstance = addInInst;

visioApplication = application as Visio.Application;

visioApplication.DocumentOpened += new Microsoft.Office.Interop.Visio.EApplication_DocumentOpenedEventHandler(visioApplication_DocumentOpened);

visioApplication.MarkerEvent += new Microsoft.Office.Interop.Visio.EApplication_MarkerEventEventHandler(visioApplication_MarkerEvent);

createCustomMenu();

}

catch (Exception exception)

{

MessageBox.Show("Error during connection in FastRefresh Visio-AddIn: " + exception.Message, title);

}

     }

The custom menu we created above is still not visible to the Visio user. To actual see the custom menu when viewing a Visio document, we will need to add it explicity to each document as it is opened. Add following code to our DocumentOpened event handler:

void visioApplication_DocumentOpened(Microsoft.Office.Interop.Visio.Document doc)

{

if (fastRefreshUIObject != null)

{

doc.SetCustomMenus(fastRefreshUIObject);

}

}

Now to do something with the event when the user selects our new menu item, all we need to do is watch for a MarkerEvent that contain the context string we defined for our menu item. To do this, add the follow code to the MarkerEvent handler:

void visioApplication_MarkerEvent(Microsoft.Office.Interop.Visio.Application app, int sequenceNum, string contextString)

{

Debug.Write(contextString, "MarkerEvent");

if ( contextString.Equals("FastRefresh/Options") )
{

//Respond to the custom menu item event here!!!!

}

}

Tags: ,

Software Engineering | Office | SharePoint

Comments


June 17. 2009 11:49
Terry Walker
Wow, I never knew that Creating a .NET Visio Add-In with Custom Menus. That's pretty interesting...


August 15. 2009 21:57
Adee Kaye Mens Automatic
Would you like to post a guest post on my blog?

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading



About the Author

I'd like to introduce myself to you... My name is Aaron Daisley-Harrison.  I have worked in the software engineering field for a number of years, and as an  Application Architect have created solutions for many industry verticals; worked with both Sun Microsystem and Microsoft technologies; Developed SQL database engines as well as full text search systems; and Knowledge management systems.   I am currently doing contract work out of the Pacific North West and have lately been focusing on Microsoft technologies like SharePoint 2007/2010, WCF, Identity Framework, JQuery, Xml and Silverlight.
[more]


 Digg!

 

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2009 Aaron G. Daisley-Harrison - All Rights Reserved.