How to add a keyboard shortcut on a Visual Studio extension?

Cesar 20 Reputation points
2024-11-05T09:21:24.5366667+00:00

I'm trying to learn how to create a keyboard shortcut on a Visual Studio 2022 extension.

I would like to get the shortcut visible and editable at Tools > Options > Environment > Keyboard

Steps i tried:

  • Addded: [ProvideMenuResource("Menus.ctmenu", 1)]
  • Created the package and the shortcut button GUIDs at public static class PackageIds
  • Created the .vsct file and set the same GUID used on the button.
  • Modified the .vsct include on the .csproj to VSCTCompile
  <ItemGroup>
    <VSCTCompile Include="VSIXPackage.vsct">
      <ResourceName>Menus.ctmenu</ResourceName>
      <SubType>Designer</SubType>
    </VSCTCompile>
  </ItemGroup>

It does compile and install, when i hit Ctrl + W i can see the cursor changing to the loading icon for a short period of time, but the Execute() functio never gets called.

What i'm missing?

VSIXPackage.cs

using Microsoft;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.ComponentModel.Design;
using System.Windows;
using Task = System.Threading.Tasks.Task;

namespace VSIX;

[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[ProvideMenuResource("Menus.ctmenu", 1)]
[Guid(VSIXPackage.PackageGuidString)]
[ProvideAutoLoad(UIContextGuids.NoSolution, PackageAutoLoadFlags.BackgroundLoad)]
public sealed class VSIXPackage : AsyncPackage
{
    public const string PackageGuidString = "f9a8aea3-f579-4816-9cb5-4ae3a5d68ef7";

    protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
    {
        await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);

        var commandService = await GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;
        Assumes.Present(commandService);

        var menuCommandID = new CommandID(PackageIds.guidVSIXPackageCmdSet, PackageIds.MyCommandId);
        var menuItem = new OleMenuCommand((s, e) => Execute(), menuCommandID);
        commandService.AddCommand(menuItem);
    }

    public static void Execute()
    {
        MessageBox.Show("Hello World!");
    }

    public static class PackageIds
    {
        public const string CmdSetGuid = "d858b0de-b9fa-49e8-a7ae-e080d04a78be"; // Match the GUID in .vsct
        public const int MyCommandId = 0x0100;
        public static readonly Guid guidVSIXPackageCmdSet = new Guid(CmdSetGuid);
    }
}

VSIXPackage.vsct

<?xml version="1.0" encoding="utf-8"?>
<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable">
	<Extern href="stdidcmd.h"/>
	<Extern href="vsshlids.h"/>

	<Commands package="guidVSIXPackagePkg">

		<Groups>
			<Group guid="guidVSIXPackageCmdSet" id="ButtonGroup" priority="0x0600">
				<Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS" />
			</Group>
		</Groups>

		<Buttons>
			<Button guid="guidVSIXPackageCmdSet" id="MyCommandId" priority="0x0100" type="Button">
				<Parent guid="guidVSIXPackageCmdSet" id="ButtonGroup" />
				<Strings>
					<CommandName>MyCommandId</CommandName>
					<ButtonText>MyShortcut</ButtonText>
				</Strings>
			</Button>
		</Buttons>

	</Commands>

	<KeyBindings>
		<KeyBinding guid="guidVSIXPackageCmdSet" id="MyCommandId" editor="guidVSStd97" mod1="Control" key1="W" />
	</KeyBindings>

	<Symbols>
		<!-- Define a unique GUID for your command set -->
		<GuidSymbol name="guidVSIXPackagePkg" value="{908f5230-d345-4ee5-a854-d9397bfa6ede}" />
		<GuidSymbol name="guidVSIXPackageCmdSet" value="{d858b0de-b9fa-49e8-a7ae-e080d04a78be}">
			<IDSymbol name="ButtonGroup" value="0x1020" />
			<IDSymbol name="MyCommandId" value="0x0100"/>
		</GuidSymbol>
	</Symbols>
</CommandTable>

VSIX.csproj

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <MinimumVisualStudioVersion>17.0</MinimumVisualStudioVersion>
    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
  </PropertyGroup>
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <ProjectGuid>{C28A9253-3A5D-41ED-A3F9-FAA709A70BD9}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>VSIX</RootNamespace>
    <AssemblyName>VSIX</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <LangVersion>12</LangVersion>
    <GeneratePkgDefFile>true</GeneratePkgDefFile>
    <UseCodebase>true</UseCodebase>
    <IncludeAssemblyInVSIXContainer>true</IncludeAssemblyInVSIXContainer>
    <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer>
    <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment>
    <CopyBuildOutputToOutputDirectory>true</CopyBuildOutputToOutputDirectory>
    <CopyOutputSymbolsToOutputDirectory>true</CopyOutputSymbolsToOutputDirectory>
    <StartAction>Program</StartAction>
    <StartProgram Condition="'$(DevEnvDir)' != ''">$(DevEnvDir)devenv.exe</StartProgram>
    <StartArguments>/rootsuffix Exp</StartArguments>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="VSIXPackage.cs" />
  </ItemGroup>
  <ItemGroup>
    <None Include="source.extension.vsixmanifest">
      <SubType>Designer</SubType>
    </None>
  </ItemGroup>
  <ItemGroup>
    <Reference Include="PresentationFramework" />
    <Reference Include="System" />
    <Reference Include="System.Design" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.VisualStudio.SDK" Version="17.0.32112.339" ExcludeAssets="runtime" />
    <PackageReference Include="Microsoft.VSSDK.BuildTools" Version="17.12.2069" />
    <PackageReference Include="System.Drawing.Common">
      <Version>5.0.2</Version>
    </PackageReference>
  </ItemGroup>
  <ItemGroup>
    <VSCTCompile Include="VSIXPackage.vsct">
      <ResourceName>Menus.ctmenu</ResourceName>
      <SubType>Designer</SubType>
	</VSCTCompile>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
</Project>

The full extension project can be found here on GitHub.

Visual Studio
Visual Studio
A family of Microsoft suites of integrated development tools for building applications for Windows, the web and mobile devices.
5,181 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
11,003 questions
Visual Studio Extensions
Visual Studio Extensions
Visual Studio: A family of Microsoft suites of integrated development tools for building applications for Windows, the web and mobile devices.Extensions: A program or program module that adds functionality to or extends the effectiveness of a program.
227 questions
0 comments No comments
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.