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