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="{f9a8aea3-f579-4816-9cb5-4ae3a5d68ef7}" />
<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.