Useful Code: Keyboard shortcuts

People often wonder about how we deal with Keyboard shortcuts in Visual Studio. There are a couple of things to know and I'll throw in a neat macro too.

Profiles

When you run Visual Studio for the first time, you will get a prompt asking which default settings you wish to use. We created these defaults to accommodate developers have become accustomed to a certain IDE layout and we don't want to impose a new one. Therefore we ship VS with 7 default profiles corresponding to types of development activities: J#, C#, VB, C++, Web, Team and General (kind of a default for defaults). These profiles encode things like the window layout, options, help filters, as well as keyboard shortcuts. If you are accustomed to VC6 shortcuts, you'll probably be most comfortable in the C++ profile. On the other hand, if you're coming from the C#/VB world, you might prefer to stick with their settings. Of course, the elite among you will care nothing for these imposed "defaults" and I'll be happy to show you how you can set up your own shortcuts.

Keyboard Options

There is a single location for all your shortcut management needs, the Keyboard options dialog. It is located within the IDE options dialog (Tools\Options...). You can find it under the top-level Environment node. I like to say that it's a highly functional dialog stuck in a poor UI. Here is a picture of it (the red boxes are my creative addition).

As you can see, it looks quite dense so I have highlighted what I consider the two features of this dialog everyone should know (I leave the rest up to the reader). The first box is a substring search filter to find any command which can be assigned a shortcut, which is great when you can't remember what the shortcut for a command is. The second box is somewhat the compliment of the first as it allows you to do a reverse lookup of a shortcut. In other words, given a shortcut, it will display what command(s) it is assigned to.

View all shortcuts

Over time, some of my colleagues decided it would be nice to query Visual Studio to return the list of all assigned shortcuts (in order to print it out or test it or something…). This seemed like an interesting opportunity to flex our extensibility model and write yet another macro. First, let's define a couple structures for this purpose.

Structure VSShortcut

Public Scope As String

Public KeyCombo As String

Sub New(ByVal fullshortcut As String)

Scope = fullshortcut.Split("::").GetValue(0)

KeyCombo = fullshortcut.Split("::").GetValue(2)

End Sub

End Structure

Structure VSCommand

Public Name As String

Public Category As String

Public Shortcuts As List(Of VSShortcut)

Sub New(ByVal fullname As String, ByVal keys As Array)

Name = fullname

Category = fullname.Split(".").GetValue(0)

Shortcuts = New List(Of VSShortcut)(5)

For Each key As String In keys

Shortcuts.Add(New VSShortcut(key))

Next

End Sub

End Structure

These two structures are pretty self-explanatory, they represent a shortcut key combination and a command respectively. We can now iterate through the list of all commands and generate a list of commands with assigned shortcuts.

Sub GetAssignedCommands(ByRef CommandList As List(Of VSCommand))

Dim keys As System.Array

' iterate through each possible command in VS

For Each item As EnvDTE.Command In DTE.Commands

If (item.Name <> Nothing) And (item.Name <> "") Then

' get the array of shortcuts for this command

keys = item.Bindings

' add command only if there is at least a shortcut assigned to it

If keys.Length > 0 Then

CommandList.Add(New VSCommand(item.Name, keys))

End If

End If

Next

End Sub

At this point, we simply need to print this out. It turns out there's a quick and dirty way of getting some output from a macro in VS. The Output window. Here is the relevant code to spit out some XML (HTML might be more appropriate for a print-out).

Sub WriteStringToOutput(ByRef s As String)

Dim pane As OutputWindowPane = GetOutputWindowPane("Commands")

pane.OutputString(s)

End Sub

Sub GenerateShortcutsInXML(ByRef CommandList As List(Of VSCommand), ByRef XMLDoc As XmlDocument)

Dim commandNode As XmlElement

Dim shortcutNode As XmlElement

Dim shortcutText As XmlText

Dim rootNode As XmlElement

' xml preamble

XMLDoc.CreateXmlDeclaration("1.0", "utf-8", Nothing)

XMLDoc.InsertBefore(XMLDoc.CreateXmlDeclaration("1.0", "utf-8", Nothing), XMLDoc.DocumentElement)

rootNode = xmldoc.CreateElement("commands")

XMLDoc.AppendChild(rootNode)

' iterate through commands

For Each command As VSCommand In CommandList

commandNode = XMLDoc.CreateElement("command")

commandNode.SetAttribute("name", command.Name)

For Each shortcut As VSShortcut In command.Shortcuts

shortcutNode = XMLDoc.CreateElement("shortcut")

shortcutNode.SetAttribute("scope", shortcut.Scope)

shortcutNode.AppendChild(XMLDoc.CreateTextNode(shortcut.KeyCombo))

commandNode.AppendChild(shortcutNode)

Next

rootNode.AppendChild(commandNode)

Next

End Sub

And finally, the glue that keeps it all together.

Sub OutputShortcutsAsXml()

Dim cmdList As List(Of VSCommand) = New List(Of VSCommand)

Dim xmlDoc As XmlDocument = New XmlDocument()

GetAssignedCommands(cmdList)

GenerateShortcutsInXML(cmdList, xmlDoc)

' generating the output

Dim sb As StringBuilder = New StringBuilder()

Dim writer As XmlTextWriter = New XmlTextWriter(New StringWriter(sb))

writer.Formatting = Formatting.Indented

writer.Indentation = 4

writer.IndentChar = " "

xmlDoc.WriteTo(writer)

writer.Flush()

writer.Close()

WriteStringToOutput(sb.ToString())

End Sub

 

keyboard.PNG