MenuCommands Vs. OleMenuCommands

You can create menu commands by deriving either from MenuCommand or from OleMenuCommand object, and implementing the appropriate event handlers. In the majority of cases you can use MenuCommand, as the VSPackage project template does, but occasionally you may need to use OleMenuCommand.

The commands that a VSPackage makes available to the IDE must be visible and enabled before a user can use them. When commands are created in a .vsct file by using the Visual Studio Package project template, they are visible and enabled by default. Setting some command flags, such as DynamicItemStart, can change the default behavior. The visibility, enabled status, and other properties of a command can also be changed in code at run time by accessing the OleMenuCommand object that is associated with the command.

Prerequisites

To follow this walkthrough, you must install the Visual Studio SDK. For more information, see Visual Studio SDK.

Template Locations for the Visual Studio Package Template

You can find the Visual Studio Package template in the New Project dialog under Visual Basic / Extensibility, C# / Extensibility, or Other Project Types / Extensibility.

Creating a Command

All commands, command groups, menus, toolbars, and tool windows are defined in the .vsct file. For more information, see Visual Studio Command Table (.Vsct) Files.

If you are creating a VSPackage by using the package template, select Menu Command to create a .vsct file and define a default menu command. For more information, see Creating an Extension with a Menu Command.

To add a command to the IDE

  1. Open the .vsct file.

  2. In the Symbols section, find the GuidSymbol element that contains the groups and commands.

  3. Create an IDSymbol element for each menu, group, or command that you want to add, as shown in the following example.

    <GuidSymbol name="guidButtonGroupCmdSet" value="{f69209e9-975a-4543-821d-1f4a2c52d737}">
      <IDSymbol name="MyMenuGroup" value="0x1020" />
      <IDSymbol name="cmdidMyCommand" value="0x0100" />
    </GuidSymbol>
    

    The name attributes of the GuidSymbol and IDSymbol elements provide the GUID:ID pair for each new menu, group, or command. The guid represents a command set that is defined for your VSPackage. You can define multiple command sets. Each GUID:ID pair must be unique.

  4. In the Buttons section, create a Button element to define the command, as shown in the following example.

    <Button guid="guidButtonGroupCmdSet" id="cmdidMyCommand" priority="0x0100" type="Button">
      <Parent guid="guidButtonGroupCmdSet" id="MyMenuGroup" />
      <Icon guid="guidImages" id="bmpPic1" />
      <Strings>
        <CommandName>cmdidMyCommand</CommandName>
        <ButtonText>My Command name</ButtonText>
      </Strings>
    </Button>
    
    1. Set the guid and id fields to match the GUID:ID of the new command.

    2. Set the priority attribute.

      The priority attribute is used by the .vsct to determine the location of the button among the other objects in its parent group.

      Commands that have lower priority values are displayed above, or to the left of, commands that have higher priority values. Duplicate priority values are permitted, but the relative position of commands that have equal priority is determined by the order in which VSPackages are processed at run time, and that order cannot be predetermined.

      Omitting the priority attribute sets its value to 0.

    3. Set the type attribute. In most cases, its value will be "Button". For descriptions of other valid button types, see Button Element.

  5. In the button definition, create a Strings element that contains a ButtonText element to contain the name of the menu as it appears in the IDE, and a CommandName element to contain the name of the command that is used to access the menu in the Command window.

    If the button text string includes the '&' character, the user can open the menu by pressing ALT plus the character that immediately follows the '&'.

    Adding a Tooltip element will cause the contained text to appear when a user hovers the pointer over the button.

  6. Add an Icon element to specify the icon, if any, to be displayed with the command. Icons are required for buttons on toolbars but not for menu items. The guid and id of the Icon element must match those of a Bitmap element defined in the Bitmaps section.

  7. Add command flags, as appropriate, to change the appearance and behavior of the button. To do this, add a CommandFlag element in the menu definition.

  8. Set the parent group of the command. The parent group can be a group that you create, a group from another package, or a group from the IDE. For example, to add your command to the Visual Studio editing toolbar, next to the Comment and Remove Comment buttons, set the parent to guidStdEditor:IDG_VS_EDITTOOLBAR_COMMENT. If the parent is a user-defined group, it must be the child of a menu, toolbar, or tool window that appears in the IDE.

    You can do this in one of two ways, depending on your design:

    • In the Button element, create a Parent element and set its guid and id fields to the Guid and ID of the group that will host the command, also known as the primary parent group.

      The following example defines a command that will appear on a user-defined menu.

      <Button guid="guidTopLevelMenuCmdSet" id="cmdidTestCommand" priority="0x0100" type="Button">
        <Parent guid="guidTopLevelMenuCmdSet" id="MyMenuGroup" />
        <Icon guid="guidImages" id="bmpPic1" />
        <Strings>
          <CommandName>cmdidTestCommand</CommandName>
          <ButtonText>Test Command</ButtonText>
            </Strings>
      </Button>
      
    • You may omit the Parent element if the command is to be positioned by using command placement. Create a CommandPlacements element before the Symbols section, and add a CommandPlacement element that has the guid and id of the command, a priority, and a parent, as shown in the following example.

    <CommandPlacements>
      <CommandPlacement guid="guidButtonGroupCmdSet" id="cmdidMyCommand" priority="0x105">
        <Parent guid="guidButtonGroupCmdSet" id="MyMenuGroup" />
      </CommandPlacement>
    </CommandPlacements>
    

    Creating multiple command placements that have the same GUID:ID and have different parents causes a menu to appear in multiple locations. For more information, see CommandPlacements element.

    For more information about command groups and parenting, see Creating Reusable Groups of Buttons.

    At this point, the command will be visible in the IDE, but will have no functionality. If the command was created by the package template, by default it will have a click handler that displays a message.

Handling the New Command

Most commands in managed code can be handled by the Managed Package Framework (MPF) by associating the command with a MenuCommand object or OleMenuCommand object and implementing its event handlers.

For code that uses the IOleCommandTarget interface directly for command handling, you must implement the IOleCommandTarget interface and its methods. The two most important methods are QueryStatus and Exec.

  1. Get the OleMenuCommandService instance, as shown in the following example.

    OleMenuCommandService mcs = 
        GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
    
  2. Create a CommandID object that has as its parameters the GUID and ID of the command to handle, as shown in the following example.

    CommandID menuCommandID = 
        new CommandID(GuidList.guidButtonGroupCmdSet, 
            (int)PkgCmdIDList.cmdidMyCommand);
    

    The Visual Studio Package template provides two collections, GuidList and PkgCmdIDList, to hold the GUIDs and IDs of commands. These are populated automatically for commands that are added by the template, but for commands that you add manually, you must also add the ID entry to the PkgCmdIdList class.

    Alternatively, you can populate the CommandID object by using the raw string value of the GUID and the integer value of the ID.

  3. Instantiate either a MenuCommand or OleMenuCommand object that specifies the method that handles the command together with the CommandID, as shown in the following example.

    MenuCommand menuItem = 
        new MenuCommand(MenuItemCallback, menuCommandID);
    

    The MenuCommand is appropriate for static commands. Dynamic menu item displays require QueryStatus event handlers. The OleMenuCommand adds the BeforeQueryStatus event, which occurs when the host menu of the command is opened, and some other properties, such as Text.

    Commands created by the package template are passed by default to a OleMenuCommand object in the Initialize() method of the package class.

  4. The MenuCommand is appropriate for static commands. Dynamic menu item displays require QueryStatus event handlers. The OleMenuCommand adds the BeforeQueryStatus event, which occurs when the host menu of the command is opened, and some other properties, such as Text.

    Commands created by the package template are passed by default to a OleMenuCommand object in the Initialize() method of the package class. The Visual Studio wizard implements the Initialize method by using MenuCommand. For dynamic menu item displays, you must change this to OleMenuCommand, as is shown in the next step. Furthermore, to change the menu item text, you must add a TextChanges command flag to the menu command button in the .vsct file, as is shown in the following example

    <Button guid="guidMenuTextCmdSet" id="cmdidMyCommand" priority="0x0100" type="Button">
      <Parent guid="guidMenuTextCmdSet" id="MyMenuGroup" />
      <Icon guid="guidImages" id="bmpPic1" />
      <CommandFlag>TextChanges</CommandFlag>
      <Strings>
        <CommandName>cmdidMyCommand</CommandName>
        <ButtonText>My Command name</ButtonText>
      </Strings>
    </Button>
    
  5. Pass the new menu command to the AddCommand method in the IMenuCommandService interface. This is accomplished by default for commands created by the package template, as shown in the following example

    mcs.AddCommand( menuItem );
    
  6. Implement the method that handles the command.

To implement QueryStatus

  1. The QueryStatus event occurs before a command is displayed. This enables properties of that command to be set in the event handler before it reaches the user. Only commands that are added as OleMenuCommand objects can access this method.

    Add an EventHandler object to the BeforeQueryStatus event in the OleMenuCommand object that is created to handle the command, as shown in the following example (menuItem is the OleMenuCommand instance).

    // Create the command for the menu item.
    CommandID menuCommandID = new CommandID(GuidList.guidMenuTextCmdSet, (int)PkgCmdIDList.cmdidMyCommand);
    OleMenuCommand menuItem = new OleMenuCommand(MenuItemCallback, menuCommandID );
    menuItem.BeforeQueryStatus +=
        new EventHandler(OnBeforeQueryStatus);
    mcs.AddCommand(menuItem);
    
    Dim menuCommandID As CommandID = New CommandID(GuidList.guidMenuTextCmdSet, CInt(PkgCmdIDList.cmdidMyTextCommand))
    Dim menuItem As OleMenuCommand = New OleMenuCommand(New EventHandler(AddressOf MenuItemCallback), menuCommandID)
    AddHandler menuItem.BeforeQueryStatus, AddressOf OnBeforeQueryStatus
    mcs.AddCommand(menuItem)
    

    The EventHandler object is given the name of a method that is called when the status of the menu command is queried.

  2. Implement the query status handler method for the command. The object sender parameter can be cast to an OleMenuCommand object, which is used to set the various attributes of the menu command, including the text. The following table shows the properties on the MenuCommand class (which the MPF class OleMenuCommand derives from) that correspond to the OLECMDF flags.

    MenuCommand Property OLECMDF flag
    Checked = true OLECMDF
    Visible = false OLECMDF
    Enabled = true OLECMDF

    To change the text of a menu command, use the Text property on the OleMenuCommand object, as shown in the following example.

    private void OnBeforeQueryStatus(object sender, EventArgs e)
    {
        var myCommand = sender as OleMenuCommand;
        if (null != myCommand)
        {
            myCommand.Text = "New Text";
        }
    }
    
    Private Sub OnBeforeQueryStatus(ByVal sender As Object, ByVal e As EventArgs)
        Dim myCommand As OleMenuCommand = TryCast(sender, OleMenuCommand)
        If myCommand IsNot Nothing Then
            myCommand.Text = "New Text"
        End If
    End Sub
    

    The MPF automatically handles the case of unsupported or unknown groups. Unless a command has been added to the OleMenuCommandService by using the AddCommand method, the command is not supported.

Handling Commands By Using the IOleCommandTarget Interface

For code that uses the IOleCommandTarget interface directly, the VSPackage must implement both the QueryStatus and Exec methods of the IOleCommandTarget interface. If the VSPackage implements a project hierarchy, the QueryStatusCommand and ExecCommand methods of the IVsUIHierarchy interface should be implemented instead.

Both the QueryStatus and Exec methods are designed to receive a single command set GUID and an array of command IDs as input. We recommend that VSPackages fully support this concept of multiple IDs in one call. However, as long as a VSPackage is not called from other VSPackages, you can assume that the command array contains only one command ID because the QueryStatus and Exec methods are executed in a well-defined order. For more information about routing, see Command Routing in VSPackages.

For code that uses the IOleCommandTarget interface directly for command handling, you must implement the QueryStatus method in the VSPackage as follows to handle commands.

To implement the QueryStatus method
  1. Return S_OK for valid commands.

  2. Set the cmdf element of the prgCmds parameter.

    The value of the cmdf element is the logical union of values from the OLECMDF enumeration, combined by using the logical OR (|) operator.

    Use the appropriate enumeration, based on the status of the command:

    • If the command is supported:

      prgCmds[0].cmdf = OLECMDF_SUPPORTED;

    • If the command should be invisible at the moment:

      prgCmds[0].cmdf |= OLECMDF_INVISIBLE;

    • If the command is toggled on and appears to have been clicked:

      prgCmds[0].cmdf |= OLECMDF_LATCHED;

      In the case of processing commands that are hosted on a menu of type MenuControllerLatched, the first command that is marked by the OLECMDF_LATCHED flag is the default command that is displayed by the menu on start-up. For more information about MenuController menu types, see Menu Element.

    • If the command is currently enabled:

      prgCmds[0].cmdf |= OLECMDF_ENABLED;

    • If the command is part of a shortcut menu and is hidden by default:

      prgCmds[0] cmdf |= OLECMDF_DEFHIDEONCTXMENU

    • If the command uses the TEXTCHANGES flag, set the rgwz element of the pCmdText parameter to the new text of the command and set the cwActual element of the pCmdText parameter to the size of the command string.

      For error conditions, the QueryStatus method must handle the following error cases:

    • If the GUID is unknown or not supported, return OLECMDERR_E_UNKNOWNGROUP.

    • If the GUID is known but the command ID is unknown or not supported, return OLECMDERR_E_NOTSUPPORTED.

    The VSPackage implementation of the Exec method must also return specific error codes, depending on whether the command is supported and whether the command was handled successfully.

To implement the Exec method
  • If the command GUID is unknown, return OLECMDERR_E_UNKNOWNGROUP.

  • If the GUID is known but the command ID is unknown, return OLECMDERR_E_NOTSUPPORTED.

  • If the GUID and command ID match the GUID:ID pair that is used by the command in the .vsct file, execute the code that is associated with the command and return S_OK.

See Also

VSCT XML Schema Reference
Extending Menus and Commands