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
Open the .vsct file.
In the
Symbols
section, find the GuidSymbol element that contains the groups and commands.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 theGuidSymbol
andIDSymbol
elements provide the GUID:ID pair for each new menu, group, or command. Theguid
represents a command set that is defined for your VSPackage. You can define multiple command sets. Each GUID:ID pair must be unique.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>
Set the
guid
andid
fields to match the GUID:ID of the new command.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.Set the
type
attribute. In most cases, its value will be"Button"
. For descriptions of other valid button types, see Button Element.
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.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
andid
of theIcon
element must match those of a Bitmap element defined in theBitmaps
section.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.
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 itsguid
andid
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 theSymbols
section, and add a CommandPlacement element that has theguid
andid
of the command, apriority
, 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.
Get the OleMenuCommandService instance, as shown in the following example.
OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
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
andPkgCmdIDList
, 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 thePkgCmdIdList
class.Alternatively, you can populate the CommandID object by using the raw string value of the GUID and the integer value of the ID.
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.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 theInitialize
method by usingMenuCommand
. For dynamic menu item displays, you must change this toOleMenuCommand
, 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>
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 );
Implement the method that handles the command.
To implement QueryStatus
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.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
Return S_OK for valid commands.
Set the
cmdf
element of theprgCmds
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 theOLECMDF_LATCHED
flag is the default command that is displayed by the menu on start-up. For more information aboutMenuController
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 thergwz
element of thepCmdText
parameter to the new text of the command and set thecwActual
element of thepCmdText
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, returnOLECMDERR_E_UNKNOWNGROUP
.If the
GUID
is known but the command ID is unknown, returnOLECMDERR_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.