Command Handling

A language service can control what context menus are shown in the editor, by intercepting the SHOWCONTEXTMENU enumeration. Alternately, you can control the context menu on a per-marker basis. For more information see Key Commands for Language Service Filters.

注意

Beginning with Visual Studio 2008 SDK, use XML Command Table (.vsct) files instead of command table configuration (.ctc) files to define how menus and commands appear in your VSPackages. For more information, see XML-Based Command Table Configuration (.vsct) Files.

Adding Commands to the Editor Context Menu

To add a command to the context menu, you must first define a set of menu commands in a .ctc-file belonging to a specific group. These commands must be invisible and disabled by default. The following code is from the ctc-file generated as a part of the walkthrough, Walkthrough: Adding a Command to an Editor Generated by the Package Wizard:

NEWGROUPS_BEGIN
// NewGroup    Parent Group   Priority
guidTestEditorCmdSet:MyMenuGroup,
guidSHLMainMenu:IDM_VS_MENU_EDIT,
0x0600;
NEWGROUPS_END

BUTTONS_BEGIN
// Command      Parent Group    Priority   Image   Type    Visibility
guidTestEditorCmdSet:cmdidColorFont,
guidTestEditorCmdSet:MyMenuGroup,
0x0100,
guidTestEditorCmdSet:bmpPicSmile, 
BUTTON,
DYNAMICVISIBILITY | DEFAULTINVISIBLE | DEFAULTDISABLED,
"Color Text";
guidTestEditorCmdSet:cmdidMyCommand,
guidSHLMainMenu:IDM_VS_MENU_TOOLS,
0x0100,
guidTestEditorCmdSet:bmpPic1,
BUTTON,    ,
"My Command Name";

The above ctc-file code adds two menu buttons, one to the Edit menu and one to the Tools menu. The command Color Text is added to the Edit menu and the command My Command Name is added to the Tools menu.

The Parent Group setting determines the primary group the command belongs to. Every menu button must belong to a primary parent group.

The Image setting adds an optional icon that appears next to the menu button in the menu. For example, the Color TextImage parameter is set to guidTestEditorCmdSet:bmpPic1 to include a smiley-face icon. The bmpPic1 is a bitmap provided by the template generated by the Visual Studio Integration Package wizard.

The Priority setting determines the order in which the command buttons appear in the given menu where a smaller number indicates a higher priority.

The Color Text menu button has its Visibility set to DYNAMICVISIBILITY | DEFAULTINVISIBLE | DEFAULTDISABLED. This sets the visibility to be invisible and disabled by default. In practice this means that the menu item is grayed out and inaccessible in the Edit menu until it is activated. In the Walkthrough: Adding a Command to an Editor Generated by the Package Wizard walkthrough, the Color Text menu button is programmed to be dynamically visible if a portion of text in the editor pane is selected.

You can also use predefined commands that do not need to be defined in the ctc-file. For example, if you examine the EditorPane.cs file generated by the Visual Studio Integration Package wizard, you find that a set of predefined commands, such as SelectAll defined by GUID_VSStandardCommandSet97, are handled in the QueryStatus and Exec methods without ctc-file entries. For more information on the Visual Studio Integration Package wizard, see How to: Create VSPackages (C# and VB).

Executing the Commands

Executing the commands is a two step process, detailed below:

  1. Implement the QueryStatus method for the object that is monitoring your commands. The Visual Studio IDE calls the QueryStatus method to determine if a menu item should be visible and if it should be enabled or disabled. The following code sample is taken from the Walkthrough: Adding a Command to an Editor Generated by the Package Wizard walkthrough and shows the implementation for the commands generated by the Visual Studio Integration Package wizard, and the Color Text command added in the walkthrough:

    Public Function QueryStatus(ByRef guidCmdGroup As Guid, ByVal cCmds As UInteger, ByVal prgCmds As OLECMD(), ByVal pCmdText As System.IntPtr) As Integer
        Debug.Assert(cCmds = 1, "Multiple commands")
        Debug.Assert(Not prgCmds Is Nothing, "NULL argument")
    
        If (prgCmds Is Nothing) Then
            Return VSConstants.E_INVALIDARG
        End If
    
        Dim cmdf As OLECMDF = OLECMDF.OLECMDF_SUPPORTED
    
        If guidCmdGroup = VSConstants.GUID_VSStandardCommandSet97 Then
            ' Process standard Commands.
            Select Case prgCmds(0).cmdID
                Case CUInt(VSStd97CmdID.SelectAll)
                    ' Command is always enabled.
                    cmdf = OLECMDF.OLECMDF_SUPPORTED Or OLECMDF.OLECMDF_ENABLED
                    Exit Select
                Case CUInt(VSStd97CmdID.Copy), CUInt(VSStd97CmdID.Cut)
                    ' Enable command if something is selected.
                    If textBox1.SelectionLength > 0 Then
                        cmdf = cmdf Or OLECMDF.OLECMDF_ENABLED
                    End If
                    Exit Select
                Case CUInt(VSStd97CmdID.Paste)
                    ' Enable if clipboard has content that we can paste.
                    If textBox1.CanPaste(DataFormats.GetFormat(DataFormats.Text)) Then
                        cmdf = cmdf Or OLECMDF.OLECMDF_ENABLED
                    End If
                    Exit Select
                Case CUInt(VSStd97CmdID.Redo)
                    If textBox1.CanRedo Then
                        cmdf = cmdf Or OLECMDF.OLECMDF_ENABLED
                    End If
                    Exit Select
                Case CUInt(VSStd97CmdID.Undo)
                    If textBox1.CanUndo Then
                        cmdf = cmdf Or OLECMDF.OLECMDF_ENABLED
                    End If
                    Exit Select
                Case Else
                    Return CInt(Fix(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED))
            End Select
        ElseIf guidCmdGroup = GuidList.guidTestEditorCmdSet Then
            ' Process our Commands
            Select Case prgCmds(0).cmdID
                Case PkgCmdIDList.cmdidColorFont
                    ' Enable if something is selected.
                    If textBox1.SelectionLength > 0 Then
                        cmdf = cmdf Or OLECMDF.OLECMDF_ENABLED
                    End If
                    Exit Select
                Case Else
                    Return CInt(Fix(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED))
            End Select
        Else
            Return CInt(Fix(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED))
        End If
    
    public int QueryStatus(ref Guid guidCmdGroup, uint cCmds, OLECMD[] prgCmds, System.IntPtr pCmdText)
    {
        Debug.Assert(cCmds == 1, "Multiple commands");
        Debug.Assert(prgCmds!=null, "NULL argument");
    
        if ((prgCmds == null))
          return VSConstants.E_INVALIDARG;
    
          OLECMDF cmdf = OLECMDF.OLECMDF_SUPPORTED;
    
          if (guidCmdGroup == VSConstants.GUID_VSStandardCommandSet97)
          {
          // Process standard Commands.
            switch (prgCmds[0].cmdID)
            {
              case (uint)VSStd97CmdID.SelectAll:
              {
              // Command is always enabled.
                cmdf = OLECMDF.OLECMDF_SUPPORTED | 
                OLECMDF.OLECMDF_ENABLED;
                break;
              }
              case (uint)VSStd97CmdID.Copy:
              case (uint)VSStd97CmdID.Cut:
              {
              // Enable command if something is selected.
                if (textBox1.SelectionLength > 0)
                  cmdf |= OLECMDF.OLECMDF_ENABLED;
                break;
              }
              case (uint)VSStd97CmdID.Paste:
              {
                // Enable if clipboard has content that we can paste.
                if (textBox1.CanPaste(DataFormats.
                  GetFormat(DataFormats.Text)))
                  cmdf |= OLECMDF.OLECMDF_ENABLED;
                break;
              }
              case (uint)VSStd97CmdID.Redo:
              {
                if (textBox1.CanRedo)
                  cmdf |= OLECMDF.OLECMDF_ENABLED;
                break;
              }
              case (uint)VSStd97CmdID.Undo:
              {
                if (textBox1.CanUndo)
                  cmdf |= OLECMDF.OLECMDF_ENABLED;
                break;
              }
              default:
              return (int)(Microsoft.VisualStudio.OLE.
                Interop.Constants.OLECMDERR_E_NOTSUPPORTED);
          }
             else if (guidCmdGroup == GuidList.guidTestEditorCmdSet)
              {
              // Process our Commands
                switch (prgCmds[0].cmdID)
                {
                  case PkgCmdIDList.cmdidColorFont:
                 {
                    // Enable if something is selected.
                    if (textBox1.SelectionLength > 0)
                      cmdf |= OLECMDF.OLECMDF_ENABLED;
                    break;
                }
              default:
              return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED);
              }
            }
              else
              return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED);
    

    Note that only the SelectAll command is always enabled, whereas all the other commands are enabled only if text is selected, or some other condition is met.

  2. The next step is to handle your commands in the Exec method. This method handles all of the commands that the Visual Studio Integration Package wizard generates, along with the Color Text command that is added in the Walkthrough: Adding a Command to an Editor Generated by the Package Wizard walkthrough. This step is illustrated in the code below:

    Public Function Exec(ByRef guidCmdGroup As Guid, ByVal nCmdID As UInteger, ByVal nCmdexecopt As UInteger, _ 
               ByVal pvaIn As System.IntPtr, ByVal pvaOut As System.IntPtr) As Integer
        Trace.WriteLine(String.Format(CultureInfo.CurrentCulture, "Entering Exec() of: {0}", Me.ToString()))
        If guidCmdGroup = VSConstants.GUID_VSStandardCommandSet97 Then
            ' Process standard Visual Studio Commands.
            Select Case nCmdID
                Case CUInt(VSStd97CmdID.Copy)
                    textBox1.Copy()
                    Exit Select
                Case CUInt(VSStd97CmdID.Cut)
                    textBox1.Cut()
                    Exit Select
                Case CUInt(VSStd97CmdID.Paste)
                    textBox1.Paste()
                    Exit Select
                Case CUInt(VSStd97CmdID.Redo)
                    textBox1.Redo()
                    Exit Select
                Case CUInt(VSStd97CmdID.Undo)
                    textBox1.Undo()
                    Exit Select
                Case CUInt(VSStd97CmdID.SelectAll)
                    textBox1.SelectAll()
                    Exit Select
                Case Else
                    Return CInt(Fix(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED))
            End Select
        ElseIf guidCmdGroup = GuidList.guidTestEditorCmdSet Then
            Select Case nCmdID
                Case PkgCmdIDList.cmdidColorFont
                    textBox1.SelectionColor = Color.Crimson
                Case Else
                    Return CInt(Fix(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED))
            End Select
        Else
        Return CInt(Fix(Microsoft.VisualStudio.OLE.Interop.)) Constants.OLECMDERR_E_UNKNOWNGROUP
        End If
        Return VSConstants.S_OK
    End Function
    
    public int Exec(ref Guid guidCmdGroup, uint nCmdID, uint 
    nCmdexecopt, System.IntPtr pvaIn, System.IntPtr pvaOut)
    {
    Trace.WriteLine (string.Format(CultureInfo.CurrentCulture, "Entering 
    Exec() of: {0}", this.ToString()));
    if (guidCmdGroup == VSConstants.GUID_VSStandardCommandSet97)
        {
        // Process standard Visual Studio Commands.
          switch (nCmdID)
          {
            case (uint)VSStd97CmdID.Copy:
            {
              textBox1.Copy();
              break;
            }
            case (uint)VSStd97CmdID.Cut:
            {
              textBox1.Cut();
              break;
            }
            case (uint)VSStd97CmdID.Paste:
            {
              textBox1.Paste();
              break;
            }
            case (uint)VSStd97CmdID.Redo:
            {
              textBox1.Redo();
              break;
            }
            case (uint)VSStd97CmdID.Undo:
            {
              textBox1.Undo();
              break;
            }
            case (uint)VSStd97CmdID.SelectAll:
            {
              textBox1.SelectAll();
              break;
            }
              default:
              return (int)(Microsoft.VisualStudio.OLE.Interop.
                Constants.OLECMDERR_E_NOTSUPPORTED);
          }
        }
    else if (guidCmdGroup == GuidList.guidTestEditorCmdSet)
      {
        switch (nCmdID)
          {
          case PkgCmdIDList.cmdidColorFont:
            textBox1.SelectionColor = Color.Crimson;
            break;
          default:
            return 
              (int)(Microsoft.VisualStudio.OLE.Interop.
              Constants.OLECMDERR_E_NOTSUPPORTED);
          }
      }
    else
        return (int)Microsoft.VisualStudio.OLE.Interop.
          Constants.OLECMDERR_E_UNKNOWNGROUP;
    return VSConstants.S_OK;
    }
    

See Also

Concepts

Menus and Toolbars

Editors