Integrating Menus and Toolbars in Visio 2003 Drawing Control Applications

 

Chris Castillo
Microsoft Corporation

March 2005

Applies to:
    Microsoft Office Visio 2003 Editions
    Microsoft Visual Studio .NET 2003

Summary:   You can choose from several approaches when integrating Visio menus and toolbars in your applications that host the Visio drawing control. This article walks you through a sample application demonstrating these approaches, including the use of the IOleCommandTarget interface. (13 printed pages)

Contents

Introduction to the Visio 2003 Drawing Control
Adding Your Own User Interface Controls
Creating the Sample Project
Using the IOleCommandTarget Interface
Using the Visio Object Model
Employing Other Strategies
Conclusion
Additional Resources

Introduction to the Visio 2003 Drawing Control

Microsoft Office Visio 2003 introduces a new Microsoft ActiveX component, the Visio drawing control, which allows you to embed the Visio drawing surface within the context of the user interface (UI) of your own Microsoft Windows-based application. You can offer many Visio features from your host application through the rich object model provided by Visio. Features that are commonly extended through host applications can be found in the Visio menus and toolbars.

As discussed in Programming with the Microsoft Office Visio 2003 ActiveX Control, one approach for enabling Visio menu and toolbar functionality is simply to let Visio merge its menus or toolbars with your own. This is called OLE menu merging; you can enable merging by setting the NegotiateMenus and NegotiateToolbars properties of the drawing control. To merge Visio menus or toolbars in your application, the drawing control container must support OLE menu merging. Windows Forms, provided by the Microsoft .NET Framework, does not support OLE menu merging.

In this article you learn the best practices for integrating Visio menu and toolbar functionality in your host applications. You learn, for example, how to provide the Connector tool in your Visual Basic .NET or C# Windows-based application. You also learn strategies to implement features not supported through the IOleCommandTarget interface, such as Print Preview.

To implement the sample application described in this article, ensure you have the following prerequisites installed on your computer:

If you are new to developing Microsoft Office Visio 2003 solutions, see the following articles on MSDN:

Adding Your Own User Interface Controls

When you first add a Visio drawing control to a form, the Visio drawing surface is displayed with only the right-mouse menu enabled for the page. Most of the UI components that are normally found in the Visio application are hidden. You should emulate this pattern of minimizing the Visio UI components that are displayed. Turn off any unneeded UI components, such as scroll bars or displayed windows. Enable only the UI components that are absolutely required by your application's design. Better yet, you can add other UI controls to your form to accomplish the same tasks. This technique makes the Visio drawing surface appear seamless in your custom application.

The same is true for menus and toolbars. Often, you need to display in your custom application only a handful of items that are found in the Visio menus and toolbars. Instead of employing OLE menu merging, which is not supported in Windows Forms, add your own menu items and toolbar buttons to your existing menus and toolbars. When a user clicks on the menu item or toolbar button, you can programmatically invoke its corresponding functionality through the IOleCommandTarget interface or through Visio properties and methods.

Creating the Sample Project

Let's create a sample Windows-based application that illustrates how to integrate Visio menu item and toolbar button functionality. First, start Visual Studio .NET 2003 and create a new C# project using the Windows Application template. On Form1, add the UI elements listed in Table 1.

Table 1. UI elements for the sample project

Control Name Parent Text Description
MainMenu mainMenu1     Main menu bar
MenuItem mnuFile mainMenu1 &File File menu
MenuItem mnuPrintPreview mnuFile Print Pre&view Displays Print Preview dialog box
MenuItem mnuView mainMenu1 &View View menu
MenuItem mnuPanZoom mnuView &Pan && Zoom Window Toggles the display of the Pan & Zoom window
MenuItem mnuCustProp mnuView &Custom Properties Window Toggles the display of the Custom Properties window
MenuItem mnuPageBreaks mnuView Page &Breaks Toggles the display of page breaks
MenuItem mnuFormat mainMenu1 F&ormat Format menu
MenuItem mnuText mnuFormat &Text... Displays Text Properties dialog box
MenuItem mnuLine mnuFormat &Line... Displays Line Properties dialog box
MenuItem mnuFill mnuFormat &Fill... Displays Fill Properties dialog box
ToolBar toolBar1     Main toolbar
ToolBarButton toolBarButton1 toolBar1   Selects Pointer tool. Set Pushed property to true.
ToolBarButton toolBarButton2 toolBar1   Selects Text tool
ToolBarButton toolBarButton3 toolBar1   Selects Rectangle tool
ToolBarButton toolBarButton4 toolBar1   Selects Ellipse tool
ToolBarButton toolBarButton5 toolBar1   Selects Connector tool
AxDrawingControl axDrawingControl1     Visio drawing control

After adding the toolbar and toolbar buttons, you should assign an icon to each toolbar button. To do this, you must add your own icons or bitmaps to an ImageList control and specify the correct value for the ImageIndex property of each toolbar button.

Figure 1 shows a possible UI design layout.

Sample UI design layout

Figure 1. Sample UI design layout

Using the IOleCommandTarget Interface

IOleCommandTarget is a COM interface that allows containers to send commands to child controls. You can use the IOleCommandTarget interface to invoke Visio commands such as those found in Visio menus and toolbars. For example, the toolbar buttons and Format menu items that we defined in our sample project can use the IOleCommandTarget interface of the Visio drawing control to invoke Visio functionality.

Before using the IOleCommandTarget interface, you must first declare the interface and associated structures. Add a new code file to your project and name it OleCommandTarget.cs. To declare the IOleCommandTarget interface and associated structures, add the following C# code to your new code file.

using System.Runtime.InteropServices;
using System;

namespace OleCommandTarget
{
    [StructLayout(LayoutKind.Sequential)]
    public struct OLECMDTEXT
    {
        public UInt32 cmdtextf;
        public UInt32 cwActual;
        public UInt32 cwBuf;
        public char rgwz;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct OLECMD
    {
        public UInt32 cmdID;
        public UInt64 cmdf;
    }

    [ComImport(), Guid("B722BCCB-4E68-101B-A2BC-00AA00404770"),
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IOleCommandTarget
    {
        [PreserveSig()]
        int QueryStatus( [In, MarshalAs(UnmanagedType.Struct)] ref Guid 
            pguidCmdGroup, [MarshalAs(UnmanagedType.U4)] int cCmds, 
            [In, Out] IntPtr prgCmds, [In, Out] IntPtr pCmdText);

        [PreserveSig()]
        int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdExecOpt, 
            object[] pvaIn, [In, Out, MarshalAs(UnmanagedType.LPArray)] 
            object[] pvaOut);
    }
}

The Visio drawing control provides the GetOcx method, which returns an object referencing the underlying ActiveX control. The return object of this method supports the IOleCommandTarget interface. You can send commands to Visio by calling the Exec method and passing a constant for the command. You can use most of the constants of the VisUICmds enumeration to invoke commands through the Exec method. Add the following code to the form of your sample project to send commands using the IOleCommandTarget interface.

using OleCommandTarget;

private void SendCommand(Visio.VisUICmds commandID)
{
    IOleCommandTarget commandTarget = 
        (IOleCommandTarget)axDrawingControl1.GetOcx();

    try
    {
        Guid CLSID_Application = 
            new Guid("00021A20-0000-0000-C000-000000000046");

        commandTarget.Exec(ref CLSID_Application, 
            (UInt32)commandID, 0, null, null);
    }
    catch(System.Runtime.InteropServices.COMException ex)
    {
        MessageBox.Show(ex.ToString());
    }
}

Invoking Visio Toolbar Button Behavior

In our sample project, we defined a toolbar with buttons that represent Pointer, Text, Rectangle, Ellipse, and Connector tools. When the user clicks one of these buttons, the appropriate tool should be used when manipulating the mouse on the Visio drawing surface. For example, if the user selects the Rectangle tool, he or she should be able to draw a rectangle on the Visio page. In the Click event handler for the toolbar button, you can invoke the behavior of the equivalent toolbar button in Visio by using the SendCommand function we defined earlier.

using Visio = Microsoft.Office.Interop.Visio;

private void toolBar1_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e)
{
    // Reset the buttons to be un-pushed.
    foreach (ToolBarButton button in toolBar1.Buttons)
    {
        button.Pushed = false;
    }

    // Make the clicked button appear pushed.
    e.Button.Pushed = true;

    switch (toolBar1.Buttons.IndexOf(e.Button))
    {
        // Pointer tool
        case 0:
            SendCommand(Visio.VisUICmds.visCmdDRPointerTool);
            break;

        // Text tool
        case 1:
            SendCommand(Visio.VisUICmds.visCmdDRTextTool);
            break;

        // Rectangle tool
        case 2:
            SendCommand(Visio.VisUICmds.visCmdDRRectTool);
            break;

        // Ellipse (Oval) tool
        case 3:
            SendCommand(Visio.VisUICmds.visCmdDROvalTool);
            break;

        // Connector tool
        case 4:
            SendCommand(Visio.VisUICmds.visCmdDRConnectorTool);
            break;
    }
}

The preceding code is called whenever the user clicks on a toolbar button of the sample project. First, the Pushed state for each button is cleared. Next, the Pushed state of the clicked button is set to true. Finally, the correct command, specified by the VisUICmds constant, is sent to Visio depending on the button that the user clicked.

Displaying Visio Dialog Boxes

The Visio application has an assortment of dialog boxes that provides many different features and capabilities to the user. Most of these dialog boxes are found throughout the Visio menu system. By using the IOleCommandTarget interface, you can instruct the Visio drawing control to display many of these dialog boxes programmatically.

Figure 2 shows the Format menu that we defined in our sample project. The Format menu has three menu items: Text, Line, and Fill. We can make these menu items behave like their counterparts in the Visio application.

Figure 2. Format menu in the sample project

In the event handler of each of these menu items, you simply need to call the SendCommand function that we previously coded and pass the appropriate VisUICmds constant.

private void mnuText_Click(object sender, System.EventArgs e)
{
    SendCommand(Visio.VisUICmds.visCmdFormatAllTextProps);
}

private void mnuLine_Click(object sender, System.EventArgs e)
{
    SendCommand(Visio.VisUICmds.visCmdFormatLine);
}

private void mnuFill_Click(object sender, System.EventArgs e)
{
    SendCommand(Visio.VisUICmds.visCmdFormatFill);
}

Using the Visio Object Model

In some cases you should use the Visio object model instead of the IOleCommandTarget interface. Some Visio UI commands are unavailable by design when used with the Exec method. In other cases the Visio object model simply offers a more elegant approach.

The View menu in our sample project, shown in Figure 3, demonstrates these cases. When the user toggles the Pan & Zoom Window or the Custom Properties Window menu items, their respective windows anchored on the Visio drawing surface should hide or display. Similarly, when the user toggles Page Breaks, the visibility of page breaks should be affected correctly. Because the VisUICmds constant to display page breaks (visCmdViewPageBreaks) is disabled by design through the IOleCommandTarget interface, you can use the ShowPageBreaks property instead. You can display or hide the Pan & Zoom and Custom Properties windows by using Visio UI commands. However, if you use the Visible property, synchronizing the Checked states of the menu items becomes more straightforward.

Figure 3. View menu in the sample project

When the user clicks the View menu, a check mark appears next to a menu item to indicate that the matching element is visible. The check mark states of the View menu items are handled in the Popup event. The Checked property of each menu item is synchronized with a property in the Visio object model that indicates visibility of a component.

private void mnuView_Popup(object sender, System.EventArgs e)
{
    Visio.Window winPanZoom = axDrawingControl1.Window.Windows
        .get_ItemFromID((int)Visio.VisWinTypes.visWinIDPanZoom);

    Visio.Window winCustProp = axDrawingControl1.Window.Windows
        .get_ItemFromID((int)Visio.VisWinTypes.visWinIDCustProp);
    
    // Show a check mark if the Pan & Zoom Window is visible.
    mnuPanZoom.Checked = winPanZoom.Visible;

    // Show a check mark if the Custom Properties Window is visible.
    mnuCustProp.Checked = winCustProp.Visible;

    // Show a check mark if page breaks are visible.
    mnuPageBreaks.Checked = Convert.ToBoolean(
        axDrawingControl1.Window.ShowPageBreaks);
}

When the user selects one of the View menu items, the Pan & Zoom window, Custom Properties window, or page breaks are either displayed or hidden. For the Pan & Zoom Window and Custom Properties Window menu items, the corresponding Window object is referenced and its Visible property is toggled. For the Page Breaks menu item, the ShowPageBreaks property of the drawing control window is toggled.

private void mnuPanZoom_Click(object sender, System.EventArgs e)
{
    Visio.Window winPanZoom = axDrawingControl1.Window.Windows
        .get_ItemFromID((int)Visio.VisWinTypes.visWinIDPanZoom);

    // Toggle the visibility of the Pan & Zoom Window.
    winPanZoom.Visible = !winPanZoom.Visible;
}

private void mnuCustProp_Click(object sender, System.EventArgs e)
{
    Visio.Window winCustProp = axDrawingControl1.Window.Windows
        .get_ItemFromID((int)Visio.VisWinTypes.visWinIDCustProp);

    // Toggle the visibility of the Custom Properties Window.
    winCustProp.Visible = !winCustProp.Visible;
}

private void mnuPageBreaks_Click(object sender, System.EventArgs e)
{
    // Toggle the visibility of page breaks.
    axDrawingControl1.Window.ShowPageBreaks = 
        (short)~axDrawingControl1.Window.ShowPageBreaks;
}

Employing Other Strategies

A few Visio UI commands are not supported through IOleCommandTarget and have no equivalent properties or methods in the Visio object model. In these instances you might be able to build equivalent functionality using the .NET Framework. The Print Preview dialog box found in the Visio application is one such example. The Visio 2003 SDK includes sample code that demonstrates how to implement your own Print Preview dialog box using the .NET Framework.

In our sample project, we included a Print Preview menu item under the File menu. Let's create our own Print Preview dialog box (shown in Figure 4) by using the Code Librarian sample in the Visio 2003 SDK. The sample, also named Print Preview, is found under the Publishing book in the Code Librarian tree. Because this sample defines a Windows form, the Visio 2003 SDK includes an accompanying XML Resource Template (.resx). You can find this XML Resource Template, PrintPreview.resx, in local_drive:\Program Files\Microsoft Office\Visio11\SDK\Samples\Code Librarian.

Figure 4. Print Preview dialog box

To add the Print Preview sample dialog box to your project

  1. In Visual Studio .NET 2003, click Project and then click Add New Item.
  2. Select the Code File template, name it PrintPreview.cs, and then click OK.
  3. In the Visio 2003 SDK Code Librarian Viewer, copy the entire code of the Print Preview sample to the Clipboard.
  4. In Visual Studio .NET 2003, paste the code in PrintPreview.cs.
  5. Copy the file PrintPreview.resx from local_drive:\Program Files\Microsoft Office\Visio11\SDK\Samples\Code Librarian to the solution folder of your sample project.
  6. In Visual Studio .NET 2003, click Project and then click Add Reference.
  7. On the .NET tab, select stdole. Click Select, and then click OK.
  8. Save your project.

The next step is to display the Print Preview dialog box from the Click event handler of the Print Preview menu item. To do this, simply instantiate a PrintPreview object, specify the document to be previewed, and display the form modally.

using Microsoft.Samples.Visio.CSharp;

private void mnuPrintPreview_Click(object sender, System.EventArgs e)
{
    PrintPreview printPreviewForm = new PrintPreview();
    printPreviewForm.PreviewDrawing(axDrawingControl1.Document);
    printPreviewForm.ShowDialog();
}

Conclusion

The Visio drawing control is a very flexible component that enables you to host the Visio drawing surface in your own applications. In addition, you can extend many features found throughout the Visio menu and toolbar system by using the IOleCommandTarget interface, the Visio Object Model, or other strategies that use the .NET Framework.

Additional Resources

For additional help on integrating Visio menus and toolbars and Visio solution development, see the following resources:

About the author

Chris Castillo is a developer consultant with the Microsoft Partner Services team, specializing in Visio solution development. See his blog at http://blogs.msdn.com/chcast.