VSIX Tutorial-Adding a simple command

Add supporting classes and files

// PkgCmdID.cs
// MUST match PkgCmdID.h
using System;

namespace Company.MyExtension
{
    static class PkgCmdIDList
    {
        public const uint cmdidGenerateIni = 0x100;
    };
}
[ProvideMenuResource("Menus.ctmenu", 1)]
[ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExistsAndFullyLoaded_string)]
<?xml version="1.0" encoding="utf-8"?>
<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">
</CommandTable>
  <ItemGroup>
    <VSCTCompile Include="MyExtension.vsct">
      <ResourceName>Menus.ctmenu</ResourceName>
    </VSCTCompile>
  </ItemGroup>

Create the menu

Here comes troubles. The menu structure is inside the .vsct file. And is organized as follow, logically, for our project

<Commands>
  <Group guid="guidSHLMainMenu" id="IDM_VS_CTXT_ITEMNODE">
    <Menu guid="guidMyExtensionCmdSet" id="myExtensionSubMenu" priority="0x0000" type="Menu">
      <Group guid="guidMyExtensionCmdSet" id="myExtensionSubMenuGroup" priority="0x0600">
        <Button guid="guidMyExtensionCmdSet" id="cmdidGenerateIni" priority="0x0100" type="Button">
        </Button>
      </Group>
    </Menu>  
  </Group>
</Commands>

The command table, the .vsct will then contain

  <!--This is the file that defines the IDs for all the commands exposed by VisualStudio. -->
  <Extern href="stdidcmd.h"/>

  <!--This header contains the command ids for the menus provided by the shell. -->
  <Extern href="vsshlids.h"/>

  <Commands package="guidMyExtensionPkg">
    <Menus>
      <!-- Define a new menu below the mainItemMenuGroup-->
      <Menu guid="guidMyExtensionCmdSet" id="myExtensionSubMenu" priority="0x0000" type="Menu">
        <Parent guid="guidMyExtensionCmdSet" id="mainItemMenuGroup"/>
        <Strings>
          <ButtonText>My Extension</ButtonText>
        </Strings>
      </Menu>
    </Menus>

    <Groups>
      <!-- Define the mainItemMenuGroup, child of the item menu -->
      <Group guid="guidMyExtensionCmdSet" id="mainItemMenuGroup" priority="0x0600">
        <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_ITEMNODE"/>
      </Group>
      <!-- Define the myExtensionSubMenuGroup, child of the myExtensionSubMenu -->
      <Group guid="guidMyExtensionCmdSet" id="myExtensionSubMenuGroup" priority="0x0600">
        <Parent guid="guidMyExtensionCmdSet" id="myExtensionSubMenu"/>
      </Group>
    </Groups>

    <Buttons>
      <!-- Define the button cmdidGenerateIni inside the myExtensionSubMenuGroup-->
      <Button guid="guidMyExtensionCmdSet" id="cmdidGenerateIni" priority="0x0100" type="Button">
        <Parent guid="guidMyExtensionCmdSet" id="myExtensionSubMenuGroup" />
        <Strings>
          <ButtonText>Generate Ini</ButtonText>
        </Strings>
      </Button>
    </Buttons>
  </Commands>

  <Symbols>
    <!-- This is the package guid. -->
    <GuidSymbol name="guidMyExtensionPkg" value="{14796bd1-9ea5-4eff-b4c0-bee11efbb734}" />

    <!-- This is the guid used to group the menu commands together -->
    <GuidSymbol name="guidMyExtensionCmdSet" value="{6abb0b81-9bb9-450d-849d-554a6501b931}">
      <IDSymbol name="mainItemMenuGroup" value="0x1020" />
      <IDSymbol name="myExtensionSubMenu" value="0x2020"/>
      <IDSymbol name="myExtensionSubMenuGroup" value="0x1021" />
      <IDSymbol name="cmdidGenerateIni" value="0x0100" />
    </GuidSymbol>
  </Symbols>

Note that all the guids are declared. The guidMyExtensionPkg is the guid of the MyExtension project and is set inside the "guidMyExtensionPkgString" in Guids.cs. The same goes for the guidMyExtensionCmdSet that has the variable "guidMyExtensionCmdSetString"

Note too that the "cmdidGenerateIni" has the same value as in the PkgCmdID.cs file.

The "guidSHLMainMenu" is present inside the two .h files declared a the top of the file. as the id IDMVSCTXT_ITEMNODE. You can find more id on MSDN

Screenshot

Connecting the action

    OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
    if (null != mcs)
    {
        // Create the command for the menu item.
        CommandID menuCommandID = new CommandID(GuidList.guidMyExtensionCmdSet, (int)PkgCmdIDList.cmdidGenerateIni);
        MenuCommand menuItem = new MenuCommand(MenuItemCallback, menuCommandID);
        mcs.AddCommand(menuItem);
    }
private void MenuItemCallback(object sender, EventArgs e)
{
    
}
    private void MenuItemCallback(object sender, EventArgs e)
    {
        var dte = GetService(typeof(SDTE)) as DTE;
        if (dte.SelectedItems.Count <= 0) return;

        foreach (SelectedItem selectedItem in dte.SelectedItems)
        {
            if (selectedItem.ProjectItem == null) return;
            var projectItem = selectedItem.ProjectItem;
            var fullPathProperty = projectItem.Properties.Item("FullPath");
            if (fullPathProperty == null) return;
            var fullPath = fullPathProperty.Value.ToString();
            MessageBox.Show(string.Format("Required '{0}'.", fullPath));
        }
    }

Note that we use the foreach. Since SelectedItems does not expose directly an array.

Conclusion

Now everything is in place!

Screenshot

You can download the code


Last modified on: December 01, 2013