Having a Visual Studio extension (VSIX) project: In Window
we got UserControl
, which have Button
binded to some ICommand
. Thi
Good answer from user1892538
Also, beware that some shortcuts are taken by the operating system or by other software running on the machine.
Ctrl+Esc will activate the start menu on Windows machines.
Other examples: - Intel graphics software takes over Ctrl+Alt+F8. - Some Ctrl+Alt combinations can have trouble getting through remote desktop connections.
You're right, the shortcut is used by a default Visual Studio command which takes precedence over the extension.
From a similar msdn post, this behavior is confirmed and the suggestion is to choose a different combination.
Find a reference for the complete list of VS shortcuts. Shortcut keys apply at various scopes (for example, when you are in a text editor, the Text Editor scoped shortcuts take precedence over the Global shortcuts). Aside from that, you can customize the shortcut's behavior and also import a new keyboard mapping scheme and select it under Tools > Options > Environment > Keyboard.
The KeyBindings
section in .vsct is where you can associate a command with a keyboard shortcut. Microsoft sample is on github
KeyBindings
seems complicated and needs to be definied on several steps (also depends on requirements). This answer is written as a bonus to answer of user1892538.
Scenario: We got toolWindow which is already showing, but we want to add some command, which will invoke method in the view/view-model.
1. Create new Command
(Step 3 in this tutorial):
Right-click to the project -> Add New Item
-> Custom command
. This will create 2 files and modify file with package:
CommandName.png
- icon for the menuCommandName.cs
- class file including the source code of commandProjectWindowPackage.cs
- Package class with Initialize()
method, which invokes Initialize()
of CommandName.cs
MyWindowPackage.cs
:
public sealed class MyWindowPackage : Package
{
public const string PackageGuidString = "00000000-0000-0000-0000-000000000003";
public MyWindowPackage() { }
protected override void Initialize()
{
MyToolWindowCommand.Initialize(this);
MySaveCommand.Initialize(this);
base.Initialize();
}
}
CommandName.cs
:
// these 2 values will do the binding
public static readonly Guid ApplicationCommands
= new Guid("00000000-0000-0000-0000-000000000002");
public const int SaveCommandId = 0x0201;
private readonly Package package;
private CommandName(Package package)
{
// we need to have package (from Initialize() method) to set VS
if (package == null) throw new ArgumentNullException("package");
this.package = package;
// this provides access for the Menu (so we can add our Command during init)
OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
if (commandService != null)
{
// Creates new command "reference" (global ID)
var menuCommandID = new CommandID(ApplicationCommands, SaveCommandId);
// Create new command instance (method to invoke, ID of command)
var menuItem = new MenuCommand(this.Save, menuCommandID);
// Adding new command to the menu
commandService.AddCommand(menuItem);
}
private void Save()
{
// Get instance of our window object (param false -> won't create new window)
ToolWindowPane lToolWindow = this.package.FindToolWindow(typeof(MyToolWindow), 0, false);
if ((null == lToolWindow) || (null == lToolWindow.Frame)) return;
// Handle the toolWindow's content as Window (our control)
((lToolWindow as MyToolWindow)?.Content as MyWindowControl)?.Save();
}
}
2. Set content of MyToolWindow to MyWindowControl (done when VSIX created):
MyToolWindow.cs
:
[Guid("00000000-0000-0000-0000-000000000001")] //GUID of ToolWindow
public class MyToolWindow : ToolWindowPane
{
public MyToolWindow() : base(null)
{
this.Content = new MyWindowControl();
}
}
3. Set code in code-behind to invoke ViewModel (or do the job itself):
MyWindowControl.cs
:
public partial class MyWindowControl : UserControl
{
// output omitted for brevity
public void Save()
{
// Do the call towards view-model or run the code
(this.DataContext as MyViewModel)?.SaveCmd.Execute(null);
}
}
4. Set Command
with Shortcut
so VS know how to handle them:
In MZTools' article can be found solution how to add Command
without seeing it in menu, but if You will go to Tools->Window->Keyboard, You might find them there (so You can set up shortcut).
I will show both origin Button
(for displaying tool window) and 2nd invisible Button
used for the shortcut (Keybind
) only.
MyWindowPackage.vsct
(in several parts):
<!-- shows the definition of commands/buttons in menu, Canonical name is how You can find the command in VS [Tools -> Keyboard -> CommandName] -->
<Commands package="guidMyWindowPackage">
<Button guid="guidMyWindowPackageCmdSet"
id="MyWindowCommandId"
priority="0x0100"
type="Button">
<Parent guid="guidSHLMainMenu" id="IDG_VS_WNDO_OTRWNDWS1" />
<Strings>
<ButtonText>My ToolWindow</ButtonText>
<CommandName>MyCommand</CommandName>
<MenuText>My ToolWindow</MenuText>
<LocCanonicalName>MyToolWindow</LocCanonicalName>
</Strings>
</Button>
<Button guid="guidMyWindowPackageCmdSet"
id="MySaveCommandId"
priority="0x0100"
type="Button">
<Strings>
<ButtonText>My ToolWindow Save</ButtonText>
<LocCanonicalName>MyToolWindow.Save</LocCanonicalName>
</Strings>
</Button>
</Buttons>
</Commands>
KeyBindings (shortcut definition):
<KeyBindings>
<KeyBinding guid="guidMyWindowPackageCmdSet"
id="MySaveCommandId"
editor="guidVSStd97"
key1="1" mod1="Control" />
</KeyBindings>
And the Symbols
, which set and bind GUID
, Command definition
and Command logic
together:
<Symbols>
<!-- This is the package guid. -->
<GuidSymbol name="guidMyWindowPackage" value="{00000000-0000-0000-0000-000000000003}" />
<!-- This is the guid used to group the menu commands together -->
<GuidSymbol name="guidMyWindowPackageCmdSet" value="{00000000-0000-0000-0000-000000000002}">
<IDSymbol name="MyWindowCommandId" value="0x0100" />
<IDSymbol name="MySaveCommandId" value="0x0201" />
</GuidSymbol>
<!-- some more GuidSymbols -->
</Symbols>
Bonus:
KeyBinding
does have property editor="guidVSStd97"
, this will set the scope of binding to "GENERAL" (useable in each window). If You can set this to the GUID
of Your ToolWindow
, it will be used only when the ToolWindow
is selected. How it works, is described behind this link. And to acomplish it full, get to this link.