Dotneteers.net
All for .net, .net for all!

VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars

Here is the second chapter from my forthcoming book about Visual Studio 2010 Package Development.

I hope, you will find this chapters as useful as the first one.


Almost all packages are created to allow the user interact with through the corresponding user interface. This interaction generally means the user can click on a menu or toolbar item and activates a function of the package.

From the user interface perspective it is pretty easy to imagine what a menu, a menu item or a toolbar is. Developers either creating Windows Forms or ASP.NET applications can understand these ideas and know their semantics. WFP developers even meet with the concept of command binding. Coming from the world of Windows Forms application programming, most developers put an equation sign between the menu item and the event handler for that item. Using the same approach for Visual Studio simply will not work: the model behind the IDE is more generic and from this aspect is more complex.

In this chapter you’ll cruise around the concepts related to command handling. Command is a central concept of user interaction in Visual Studio — and as you are going to see, it is different from the idea most Windows Forms or ASP.NET programmers thinks about.

After reading this chapter you will be familiar with the following things:

  • Important concepts behind the command handling mechanism, like menu item, command, context, command binding and state.
  • The way the visibility and state of commands is determined by Visual Studio IDE. The CommandState sample will provide you a demonstration about how to enable or disable commands, and how to set their visual properties programmatically.
  • The role and structure of the Visual Studio Command Table (.vsct) file. The BasicVSCTSample and the AdvancedVSCTSample packages will show you a few simple tasks to carry out with a .vsct file:
    • Creating a main menu
    • Using command groups and submenus
    • Decorating commands with icons and using command flags
    • Creating toolbars and menu controllers
  • The concept and usage of visibility contexts. The VisibilityContextSample will demonstrate how to set up commands to be visible only in specific contexts.
  • Command routing used by Visual Studio to direct commands to an appropriate command target.

Commands are indispensable and frequently used entities in Visual Studio. Although the fundamental ideas behind them and many practical details are treated in this chapter, you will find a lot of command-related information also in the subsequent chapters.

Basic Command Handling Concepts

You generally create a VSPackage (and it is true for many other kind of software artifacts) in order to encapsulate functions. A part of these functions can be used from outside — for instance, by services your package offers for the Shell and for other packages. A part of these functions even can be used by users — assuming you provide appropriate interaction points to invoke them. These functions are called commands in the Visual Studio Extensibility terminology. A few samples are opening a solution, printing a source file, copying the selected text in the editor to the clipboard, adding a new file to the project hierarchy, and so on.

A command can be invoked by users only if you provide a way to do that. The most obvious way is to create a menu item or a button that can be clicked by the mouse or triggered by a keypress. There are other alternatives, for example you can provide a way where the user can type something in a console-like way to activate a command. If you do not think it is a real alternative, just look what the Command Window does in Visual Studio — visit the View ð Other Windows ð Command Window function!

Of course you have the “clickable” form of user interface representing commands in the Visual Studio IDE as menu and toolbar items. Each of them has the same function: when the user activates them, Visual Studio invokes the command bound to the item. From this perspective there is no difference if a command has been invoked from a menu or a toolbar.

  • Menus are generally displayed in a row at the top of the IDE and they generally provide a visual hierarchy of the items executing commands. Other user interface elements of the IDE — like tool windows, document windows, window frames — also can have their own context menus that pop up when the user right-clicks on them.
  • Toolbars hold a set of visual controls — typically organized in a row — that have the same function as menu items: execute commands. Toolbars can have a variety of control types, like buttons, text boxes, combo boxes and labels.

In this book the term of menu item is mentioned for user interface items that can be used to invoke a command independently of whether that is a menu item or a control on a toolbar.

Commands also can be executed programmatically. For example, the DTE object which is the root of the automation object model contains a method named ExecuteCommand that accepts a command name string and an optional parameter string — and as you guess it executes the action behind the command.

When pressing a button or triggering a command invocation action on any other kind of control, an event is raised that initiates the command action.

In the traditional Windows Forms programming many developers have only the idea of menu and toolbar items and event handler methods. Often they attach the same event handler method to more menu and toolbar items and handle the state of the UI separately. For example, if the same functionality is used by a menu item and a toolbar item they attach the same event handler method but handle the enabled/disabled state of the menu item and the toolbar item separately.

Visual Studio makes a clear separation on the concept of menu items and commands.

A command is responsible for determining its state (name, visibility, enabled, disabled, etc.) and executing the command triggered. A menu item is responsible for presenting the visual properties of a command and providing a way the user can trigger the execution of the command.

This means a command object can be bound to zero, one or more menu items. The command knows its state and can report it to the related menu items: developers have one central location to handle the state of the command independently of how many menu items make that accessible. They only have to deal with the state of the command; menu items adjust their appearance by themselves. Also there is only one location for the command action code independently of the number of interaction points which can trigger the command.

So the idea of a menu (or toolbar) item and the command behind them are separated. There is another twist in the story: a command does not own the code that is intended to run when the command is invoked or when the status of the command is queried.

A command itself is a logical entity that can be forwarded to so-called command targets which know how to handle the semantics of a specific command. There is a command routing model in the IDE that forwards a command to a command target. The target either can do something with the request related to a command (for example, sets the command state disabled, execute the command, etc.) or can pass back the command as not supported (the target does not know what to do with that). The target even can pass the command to other command targets.

To make it easier to follow, let me tell you an example. We have Cut, Copy and Paste menu items in the Edit menu and on the standard toolbar of Visual Studio. Moreover, these items are added to a number of context menus — each of them can be accessed at least from three different locations. The items are bound to commands having the logical name of Cut, Copy and Paste. There is not a single object in Visual Studio that knows how to execute those operations. The IDE forwards these commands to command targets depending on the current context. For example, when the text editor is focused the IDE sends the commands to the text editor. When you are in the Properties window, commands are sent there. When you edit an .aspx page visually, the designer receives these operation commands. So, the text editor, the Properties window and the ASP.NET page designer all are command targets. They can decide if they support the command — they understand what the command means — and how to respond to that. If they support the command they are also able to execute the corresponding action.

Figure 1 visualizes the logical relation among basic entities that are involved in the process of executing a command, and Table 1 describes them:

CommandArchitecture

Figure 1: Logical diagram of basic command handling entities

 Table 1: Summary of basic command handling concepts

 

 

Entity Responsibility
Menu Item (toolbar item) Provides a user interface to invoke commands. Allows feedback about the command state to the user.
Context A logical context representing the state of the environment. The context determines the status of the command. For example, if there is no text copied to the clipboard, the Paste function is not enabled.
State Commands are logical entities and they have states that influence if commands are allowed executing their related action or not and also how commands are visualized. Commands can be enabled or disabled and they can have shown or hidden states.
Action The physical action to be invoked when an enabled command is executed by the command target.
Command A logical entity representing an action that can be queried about its state within the current context, and can be resulted in executing some code. The command itself is just a logical entity, and it does not own the code responsible for status query and action execution.
Command Target An entity that knows how to execute a status query and action belonging to a command entity. It can route, execute or even refuse actions of the command received.
Command Binder An entity responsible for understanding a specific command received by the command target in the current context. The Command Binder invokes physically the action bound to the command.

The best way of getting more information about how these concepts work in practice is to create an example and diving into the code.

 

The best way of getting more information about how these concepts work in practice is to create an example and diving into the code.

Physical Representation of Command Handling Concepts

You are going to use the FirstLook package sample code introduced in the previous chapter as the first example, because this is one of the smallest which can demonstrate how the logical concepts are represented in code. This sample application adds a command named Simple Message to the Tools menu. The command displays a message box when the user clicks on the related menu item.

First let’s have a look the code with the visual representations of commands. In the FirstLook.vsct file you can see the items for defining and placing the menu item:

  1. <Groups>
  2.   <Group guid="guidFirstLookCmdSet" id="MyMenuGroup" priority="0x0600">
  3.     <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
  4.   </Group>
  5. </Groups>
  6.     
  7. <Buttons>
  8.   <Button guid="guidFirstLookCmdSet" id="cmdidShowMyMessage" priority="0x0100"
  9.     type="Button">
  10.     <Parent guid="guidFirstLookCmdSet" id="MyMenuGroup" />
  11.     <Icon guid="guidImages" id="bmpPic1" />
  12.     <Strings>
  13.       <CommandName>cmdidShowMyMessage</CommandName>
  14.       <ButtonText>Simple Message</ButtonText>
  15.     </Strings>
  16.   </Button>
  17. </Buttons>

By this definition the Simple Message menu item is represented by a Button element that is parented by a Group element. The Group, Button and Parent elements have guid and id attributes that are used as compound identifiers. The Parent elements create the relationships so that the Group is inserted into the Tools menu, the Button is added to the Group so as a result you’ll have the Simple Message item added to the Tools menu.

The (guid, id) pairs are the compound keys of command table elements. The guid represents a logical container of elements belonging together and the id represents the unique element identifier within that container. Here the identifier of the Button element is actually the identifier of the command represented by this Button node. The command here is used implicitly with its ID. This nature of the compound key is also represented by the Symbols section where GuidSymbol nodes have IDSymbol child nodes describe this logical containment:

  1. <GuidSymbol name="guidFirstLookCmdSet" value="{4423366f-0518-4fd9-832f-9efd21a9013b}">
  2.   <IDSymbol name="MyMenuGroup" value="0x1020" />
  3.   <IDSymbol name="cmdidShowMyMessage" value="0x0100" />
  4. </GuidSymbol>

 

Let’s see how this command is represented in the code. The Initialize method of the FirstLookPackage class contains the following code:

  1. protected override void Initialize()
  2. {
  3.   Trace.WriteLine(string.Format(CultureInfo.CurrentCulture,
  4.     "Entering Initialize() of: {0}", this.ToString()));
  5.   base.Initialize();
  6.   OleMenuCommandService mcs = GetService(typeof(IMenuCommandService))
  7.     as OleMenuCommandService;
  8.   if (null != mcs)
  9.   {
  10.     CommandID menuCommandID = new CommandID(GuidList.guidFirstLookCmdSet,
  11.       (int)PkgCmdIDList.cmdidShowMyMessage);
  12.     MenuCommand menuItem = new MenuCommand(MenuItemCallback, menuCommandID);
  13.     mcs.AddCommand(menuItem);
  14.   }
  15. }

The package is a command target, so it can receive command notifications and execute actions of commands it understands. This behavior is defined by the IOleCommandTarget interface and the Microsoft.VisualStudio.Shell.Package class that is the ancestor of FirstLookPackage implements this interface. Although the package declares and understands only a single command, it receives many command notifications. However, it will refuse all commands except the one it knows. This command target behavior is delegated — the Package class implements it this way — to an object instance of type OleMenuCommandService. In Line 06 and 07 the GetService method is addressed with the type of IMenuCommandService to retrieve this OleMenuCommandService instance. You can add commands to this OleMenuCommandService instance to handle. When the package as a command target receives a command notification, it is processed by the command service instance. The command service checks if the command received is on its list or not. If the command is on the list, the required action is executed; otherwise command execution is refused. The sender object is notified about the command execution result (executed or refused).

Lines 10-13 contain the steps required to add a command to the list of the command service. First a CommandID instance is created which is a simple wrapper type for the compound key of a command. It uses a pair of System.Guid and System.UInt32 values to identify a command. When you check the identifiers used as the two construction parameters you can guess they are the same as the ones used for the Button node in the FirstLook.vsct file.

Line 12 creates a MenuCommand instance. MenuCommand is defined in the System.ComponentModel.Design namespace in the System.dll assembly and is the part of the .NET Framework and not the part of Visual Studio. It represents a part of the Command Binding entity id Figure 1. In Line 12 you construct it using the MenuItemCallback method delegate and the identifier of the command you have created in Line 10 and 11. MenuCommand — among the others — has an Invoke method to execute the menu action, and properties like Enabled and Visible to get or set the status of the related command item. The command service instance leverages on these properties and method to implement its behavior.

Line 13 adds the MenuCommand instance to the container of the command service object.

Now you can create Table 2 for associating the types for the roles summarized in Table 1.

Table 2: Summary of types implementing the command handling concepts

Entity Implementation
Menu Item (toolbar item) Menu and toolbar items do not appear as concrete .NET types in the package, they are defined in the .vsct file of the package project.
Command State In the FirstLook sample command states are not used. However, the properties of MenuCommand — such as Enabled and Visible — represent the state.
Command The command as a standalone physical entity is not used. The concept of command is represented by a CommandID instance that is used in several places. For example, a MenuCommand refers to a command by its CommandID property which is a CommandID instance.
Command Target Objects implementing the IOleCommandTarget interface. In the FirstLook sample this is the FirstLookPackage class that implements the interface indirectly through the Package class. However, the package delegates this responsibility to its command service instance with the type of OleMenuCommandService.
Command Binder In the FirstLook sample binders are represented by MenuCommand instances.

You have seen the implementation of a very simple command. It’s time to look other examples demonstrating more aspects of Visual Studio’s command handling architecture.

Command State and Visibility

Each command has a state in the current Visual Studio context. This state is built up from two orthogonal factors:

  • The availability of a command (enabled or disabled) tells if it is allowed to be invoked within the context or not. For example, if you do not have any text selected in the active text editor, the Copy or Cut commands should not be allowed being invoked.
  • The visual properties of a command tell if the menu and toolbar items related to the command are visible and what the displayed label or icon are.

Although these factors are independent from each other, the availability of a command is also reflected visually: menu items of disabled commands are grayed out.

The properties determining the state of commands can be set dynamically. Visual Studio updates the user interface asynchronously. It sends status requests to the appropriate command targets and asks them to tell the status of specific commands. This is done in Visual Studio idle time with an algorithm that sends status queries only for commands that have a chance to be displayed in the current context.

There is one thing that needs some consideration: how the initial state of a command is set up? Packages are on-demand loaded, so there is a part of the command lifecycle when their owner package is not loaded, but menu items representing them are already merged with Visual Studio menus. In this part of their life Visual Studio does not send status requests to the command target of unloaded commands, because it actually would require the command to be loaded.

Visual Studio uses the following approach to handle the visibility status of commands:

  • While a command is not flagged in the command table (.vsct file) as one that changes any of its visual properties during its life — this flag wears the remarkable DynamicVisibility name —, the explicit visual status of the command is never queried and never updated, the initial state set up in the command table is used during the whole IDE session. Of course, you should initialize these commands to be visible (this is their default state); otherwise they are not very useful.
  • If a command is flagged to dynamically change its visual properties, the initial settings in the command table are used unless the package is loaded. After the package gets sited the appropriate command target object is queried for the status of the command.

Command availability is not part of the explicit visual state. The enabled or hidden state of a command is maintained independently of the DynamicVisibility flag is used or not. While the package of the command is not loaded, the initial availability state defined in the command table is used. As soon as the package gets loaded the command target is queried for availability even if explicit visual state is not queried.

The Command State Sample

To follow the concepts above you’ll examine a package example named CommandState. This package adds five menu items to the Tools menu as indicated in Figure 2.

f0302

Figure 2: Menu items of the CommandState package

There is a hidden fifth menu item with the text “Changes Visibility #2”. When you click any of the Changes Visibility items, #1 and #2 are swapped. The “Click count: 0” item counts every click and indicates this counter in the menu text. The bottom menu item changes its availability. It is enabled when the click count above is an even number; otherwise it is disabled. So clicking 3 times on the Changes Visibility items and three times on the Click count item leads to the command status result that is illustrated in Figure 3.

f0303

Figure 3: Menu items after changing their visibility state

Listing 1 shows the command table defining the layout of the menu and initial state of commands.

Listing 1: CommandState.vsct

 

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable"
  3.   xmlns:xs="http://www.w3.org/2001/XMLSchema">
  4.   <Extern href="stdidcmd.h"/>
  5.   <Extern href="vsshlids.h"/>
  6.   <Extern href="msobtnid.h"/>
  7.   <Commands package="guidCommandStatePkg">
  8.     <Groups>
  9.       <Group guid="guidCommandStateCmdSet" id="MyMenuGroup" priority="0x0600">
  10.         <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
  11.       </Group>
  12.     </Groups>
  13.  
  14.     <Buttons>
  15.       <Button guid="guidCommandStateCmdSet" id="AlwaysVisibleAndEnabled"
  16.         priority="0x0100" type="Button">
  17.         <Parent guid="guidCommandStateCmdSet" id="MyMenuGroup" />
  18.         <Icon guid="guidOfficeIcon" id="msotcidClock" />
  19.         <Strings>
  20.           <CommandName>AlwaysVisibleAndEnabled</CommandName>
  21.           <ButtonText>Always visible and enabled</ButtonText>
  22.         </Strings>
  23.       </Button>
  24.  
  25.       <Button guid="guidCommandStateCmdSet" id="ChangesVisibility1"
  26.         priority="0x0200" type="Button">
  27.         <Parent guid="guidCommandStateCmdSet" id="MyMenuGroup" />
  28.         <Icon guid="guidOfficeIcon" id="msotcid1" />
  29.         <CommandFlag>DynamicVisibility</CommandFlag>
  30.         <Strings>
  31.           <CommandName>ChangesVisibility1</CommandName>
  32.           <ButtonText>Changes Visibility #1</ButtonText>
  33.         </Strings>
  34.       </Button>
  35.       <Button guid="guidCommandStateCmdSet" id="ChangesVisibility2"
  36.         priority="0x0300" type="Button">
  37.         <Parent guid="guidCommandStateCmdSet" id="MyMenuGroup" />
  38.         <Icon guid="guidOfficeIcon" id="msotcid2" />
  39.         <CommandFlag>DynamicVisibility</CommandFlag>
  40.         <CommandFlag>DefaultInvisible</CommandFlag>
  41.         <CommandFlag></CommandFlag>
  42.         <Strings>
  43.           <CommandName>ChangesVisibility2</CommandName>
  44.           <ButtonText>Changes Visibility #2</ButtonText>
  45.         </Strings>
  46.       </Button>
  47.  
  48.       <Button guid="guidCommandStateCmdSet" id="ChangesText"
  49.         priority="0x0400" type="Button">
  50.         <Parent guid="guidCommandStateCmdSet" id="MyMenuGroup" />
  51.         <Icon guid="guidOfficeIcon" id="msotcidPaperStack" />
  52.         <CommandFlag>TextChanges</CommandFlag>
  53.         <Strings>
  54.           <CommandName>ChangesText</CommandName>
  55.           <ButtonText>Click count: 0</ButtonText>
  56.         </Strings>
  57.       </Button>
  58.  
  59.       <Button guid="guidCommandStateCmdSet" id="ChangesEnabledState"
  60.         priority="0x0500" type="Button">
  61.         <Parent guid="guidCommandStateCmdSet" id="MyMenuGroup" />
  62.         <Icon guid="guidOfficeIcon" id="msotcidBlank" />
  63.         <Strings>
  64.           <CommandName>ChangesEnabledState</CommandName>
  65.           <ButtonText>Enabled on even click count</ButtonText>
  66.         </Strings>
  67.       </Button>
  68.     </Buttons>
  69.   </Commands>
  70.  
  71.   <Symbols>
  72.     <GuidSymbol name="guidCommandStatePkg"
  73.       value="{ff5ab687-afbb-46c1-8542-17510542549a}"/>
  74.     <GuidSymbol name="guidCommandStateCmdSet"
  75.       value="{ab0f163e-cd16-404d-a378-60423d214c32}">
  76.       <IDSymbol name="MyMenuGroup" value="0x1020" />
  77.       <IDSymbol name="AlwaysVisibleAndEnabled" value="0x0100" />
  78.       <IDSymbol name="ChangesVisibility1" value="0x0101" />
  79.       <IDSymbol name="ChangesVisibility2" value="0x0102" />
  80.       <IDSymbol name="ChangesText" value="0x0103" />
  81.       <IDSymbol name="ChangesEnabledState" value="0x0104" />
  82.     </GuidSymbol>
  83.   </Symbols>
  84.  
  85. </CommandTable>

Each menu item representing a command is set up a bit differently than the others. Button elements can define flags determining their behavior in CommandFlag elements as highlighted in the listing. Table 3 describes the behavior defined by the initial settings of Button elements.

Table 3: Behavior of Button elements

Button element ID Description
AlwaysVisibleAndEnabled This element does not define any flags to handle the visibility properties dynamically, so its visibility state is always the default (visible). Even if the package is loaded, its visibility state is never queried.
ChangesVisibility1 By using the DynamicVisibility command flag this item is visible unless the package is loaded. When the package gets sited, the command target is queried for visibility status.
ChangesVisibility2 This element combines the DefaultInvisible flag with DynamicVisibility. As a result, it behaves exactly like ChangesVisibility1 except that its initial state is invisible.
ChangesText Here the TextChanges command flag is used. While the package is not loaded the string specified in the ButtonText element is used as the label of the command. When the package is loaded Visual Studio asks the package for the label text before displaying the command.
ChangesEnabledState Similarly to the AlwaysVisibleAndEnabled command this element does not define any flags to handle visibility state dynamically. However, you change the availability of the command programmatically and the appearance of the menu item follows it. This is because as soon as the package gets loaded availability status is queried before displaying the menu item.

The command table defines only the initial visual state of commands and some of them are changed during runtime. Listing 2 contains the source code for the CommandStatePackage class that defines this behavior:

Listing 2: CommandStatePackage.cs

  1. using System;
  2. using System.Runtime.InteropServices;
  3. using System.ComponentModel.Design;
  4. using System.Text;
  5. using Microsoft.VisualStudio.Shell.Interop;
  6. using Microsoft.VisualStudio.Shell;
  7.  
  8. namespace DeepDiver.CommandState
  9. {
  10.   [PackageRegistration(UseManagedResourcesOnly = true)]
  11.   [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
  12.   [ProvideMenuResource("Menus.ctmenu", 1)]
  13.   [Guid(GuidList.guidCommandStatePkgString)]
  14.   public sealed class CommandStatePackage : Package
  15.   {
  16.     private OleMenuCommandService _CommandService;
  17.     private OleMenuCommand _ChangesVisibility1;
  18.     private OleMenuCommand _ChangesVisibility2;
  19.     private OleMenuCommand _ChangesText;
  20.     private OleMenuCommand _ChangesEnabledState;
  21.     private int _ClickCount;
  22.  
  23.     protected override void Initialize()
  24.     {
  25.       base.Initialize();
  26.       _CommandService = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
  27.       RegisterCommand(CmdIDs.AlwaysVisibleAndEnabled, AlwaysVisibleCallback);
  28.       _ChangesVisibility1 =
  29.         RegisterCommand(CmdIDs.ChangesVisibility1, ChangesVisibilityCallback);
  30.       _ChangesVisibility2 =
  31.         RegisterCommand(CmdIDs.ChangesVisibility2, ChangesVisibilityCallback);
  32.       _ChangesVisibility2.Visible = false;
  33.       _ChangesText =
  34.         RegisterCommand(CmdIDs.ChangesText, ChangesTextCallback);
  35.       _ChangesEnabledState =
  36.         RegisterCommand(CmdIDs.ChangesEnabledState, ChangesEnabledStateCallback);
  37.     }
  38.  
  39.     private void AlwaysVisibleCallback(object caller, EventArgs args)
  40.     {
  41.       OutputCommandString("'Always visible and enabled' command executed.");
  42.     }
  43.  
  44.     private void ChangesVisibilityCallback(object caller, EventArgs args)
  45.     {
  46.       _ChangesVisibility1.Visible = !_ChangesVisibility1.Visible;
  47.       _ChangesVisibility2.Visible = !_ChangesVisibility1.Visible;
  48.       OutputCommandString("Visibility of buttons #1 and #2 swapped.");
  49.     }
  50.  
  51.     private void ChangesTextCallback(object caller, EventArgs args)
  52.     {
  53.       _ChangesText.Text = String.Format("Click count: {0}", ++_ClickCount);
  54.       _ChangesEnabledState.Enabled = _ClickCount % 2 == 0;
  55.       OutputCommandString("Current click counter is " + _ClickCount);
  56.     }
  57.  
  58.     private void ChangesEnabledStateCallback(object caller, EventArgs args)
  59.     {
  60.       OutputCommandString("Command is enabled at click count " + _ClickCount);
  61.     }
  62.  
  63.     private OleMenuCommand RegisterCommand(uint id, EventHandler callback)
  64.     {
  65.       if (_CommandService == null) return null;
  66.       var menuCommandID = new CommandID(GuidList.guidCommandStateCmdSet, (int)id);
  67.       var menuItem = new OleMenuCommand(callback, menuCommandID);
  68.       _CommandService.AddCommand(menuItem);
  69.       return menuItem;
  70.     }
  71.  
  72.     private void OutputCommandString(string text)
  73.     {
  74.       // --- Build the string to write on the debugger and Output window.
  75.       var outputText = new StringBuilder();
  76.       outputText.AppendFormat("CommandStatePackage: {0} ", text);
  77.       // --- Get a reference to IVsOutputWindow.
  78.       var outputWindow = GetService(typeof(SVsOutputWindow)) as IVsOutputWindow;
  79.       if (outputWindow == null) return;
  80.  
  81.       // --- Get the window pane for the general output.
  82.       var guidGeneral = Microsoft.VisualStudio.VSConstants.GUID_OutWindowDebugPane;
  83.       IVsOutputWindowPane windowPane;
  84.       if (Microsoft.VisualStudio.ErrorHandler.Failed(
  85.         outputWindow.GetPane(ref guidGeneral, out windowPane)))
  86.       {
  87.         return;
  88.       }
  89.       // --- As the last step, write to the output window pane
  90.       windowPane.OutputString(outputText.ToString());
  91.       windowPane.Activate();
  92.     }
  93.   }
  94. }

The package sets up command bindings in the overridden Initialize methods. Commands use the event handler methods having a Callback suffix. The RegisterCommand helper method carries out the necessary steps to bind a command handler to its logical command. Each command generates a diagnostic message to the Debug pane of the Output window in Visual Studio, OutputCommandString implements this functionality. Output window handling is treated in details in Chapter 3: Window Management and Tool Windows so there you will find more explanation about how OutputCommandString method works.

In the FirstLook sample at the beginning of this chapter you could see a MenuCommand instance was used as the command binder, whilst in this example OleMenuCommand instances are used. OleMenuCommand derives from MenuCommand and this kind of object is passed as the sender argument in the event handler methods. Each OleMenuCommand instances represent the command binders in private fields, and later these are used to change command status. The RegisterCommand method is implemented so that it retrieves the binder object.

AlwaysVisibleCallback simply generates an output message.

You can observe that both _ChangesVisibility1 and _ChangesVisibility2 are bound to ChangesVisibilityCallback that is a very simple method swapping the current visibility states of these two commands:

  1. private void ChangesVisibilityCallback(object caller, EventArgs args)
  2. {
  3.   _ChangesVisibility1.Visible = !_ChangesVisibility1.Visible;
  4.   _ChangesVisibility2.Visible = !_ChangesVisibility1.Visible;
  5.   OutputCommandString("Visibility of buttons #1 and #2 swapped.");
  6. }

The menu binder triggering the event is passed in the caller argument, so you could write the code above like this:

 

  1. private void ChangesVisibilityCallback(object caller, EventArgs args)
  2. {
  3.   var command = caller as OleMenuCommand;
  4.   if (command == null || (command.CommandID.ID != CmdIDs.ChangesVisibility1 &&
  5.     command.CommandID.ID != CmdIDs.ChangesVisibility2))
  6.   {
  7.     return;
  8.   }
  9.   _ChangesVisibility1.Visible = command.CommandID.ID == CmdIDs.ChangesVisibility2;
  10.   _ChangesVisibility2.Visible = !_ChangesVisibility1.Visible;
  11.   OutputCommandString("Visibility of buttons #1 and #2 swapped.");
  12. }

You need an additional step to make the swapping commands work correctly: you have to initialize the visibility state of _ChangesVisibility2 to hidden in the Initialize method:

  1. _ChangesVisibility1 =
  2.   RegisterCommand(CmdIDs.ChangesVisibility1, ChangesVisibilityCallback);
  3. _ChangesVisibility2 =
  4.   RegisterCommand(CmdIDs.ChangesVisibility2, ChangesVisibilityCallback);
  5. _ChangesVisibility2.Visible = false;

If you omit the last line, the package seems working properly. But click on the Always visible and enabled item first and then drop down the Tools menu. You will observe some unexpected thing as Figure 4 shows.

f0304

Figure 4: Both Changes Visibility commands are visible!

What’s wrong with the command logic? If you invoke the Always visible and enabled item first, it causes the package to load. The Initialize method runs and creates the binders for the Changes Visibility items. By default these binders are created with their Visibility property set to true. Because the package is loaded, the IDE uses the package to query the visibility status when you drop down the Tools menu and this time both commands are visible.

The ChangesText command also uses a very simple logic:

  1. private void ChangesTextCallback(object caller, EventArgs args)
  2. {
  3.   _ChangesText.Text = String.Format("Click count: {0}", ++_ClickCount);
  4.   _ChangesEnabledState.Enabled = _ClickCount%2 == 0;
  5.   OutputCommandString("Current click counter is " + _ClickCount);
  6. }

It uses the _ClickCount member variable to count the number of clicks and updates the label of the command through the Text property. This command controls the availability of the ChangesEnabledState command. Try to omit the TextChanges flag from the command table and run the package. You can use the ChangesText command — as you click the availability status of ChangesEnabledState switches between enabled and hidden — but the count of click is not displayed.

You do not need an event handler method for the ChangesEnabledState command, because its state is handled by the ChangesTextCallback method, but in this example the handler is used to display a trace message.

Commands and Visibility

When you move around in Visual Studio you can see that a few toolbars, menus and menu items are visible or invisible depending on the context you are in. For example, the Project and Debug menu items cannot be seen while there is no project open. Similarly, you cannot reach the Team menu unless you connect to a Team Server.

Visual Studio shows or hides commands according to the current context. Please note, showing or hiding commands means to make them visible or invisible. If a command is visible, availability state also influences the appearance — disabled commands are dimmed — but availability is totally independent of visibility.

Commands can be defined in different locations — they logically belong to one of the following entities:

  • The VS IDE environment. All commands defined by the Visual Studio IDE are always visible.
  • Packages registered in the IDE. Packages may define commands and decide whether to show or hide a certain command. Please remember that Visual Studio IDE is a combination of the core IDE (Shell) and the packages registered with the IDE. When you install Visual Studio many packages also get registered.
  • The active project. In Visual Studio you work with solutions that contain projects. There can be only one active project (the currently selected project) determined by either the active editor file or the item selected in the Solution Explorer. All the other projects (if there are any others in the solution) are inactive. Only the commands belonging to the active project are visible.
  • Active editor. When you edit a source file in the text editor or a form with the form editor or creating a class diagram, you work with an editor. If you have documents open you always have one active editor (and might have a set of inactive ones). The active editor can define its own commands (think about the Windows Forms editor). The commands defined by the active editors are visible, but any commands of other (inactive) editors are hidden. There are editors that support a variety of file types (e.g. the Image Editor). There can be commands that are available for one file type but not for another file type. For instance, you can set the transparent pixels for an icon but not for a .BMP file in the image editor. It is always the responsibility of the editor to show or hide a command depending on the file type currently used.
  • Tool windows. Packages can register tool windows and tool windows can have their own commands. When you register a package providing a tool window with Visual Studio, the related commands are visible by default. Of course, to make them appear on the screen you must show up the tool window.

Any time when you are in a concrete Visual Studio context all visible commands from the locations above are merged. So, the union of IDE environment commands, package commands, active project commands, active editor commands (depending on the file type) and visible tool window commands determine the set of visible commands.

You may feel that something is missing. As it has been treated here commands are visible or hidden for a package, editor or tool window. An example was also mentioned earlier that the Project and Debug menus are not available unless a file or a project is opened. How does this fit into the picture? How does Visual Studio handle this thing? How can a package make a command visible or hidden depending on whether a project is open or closed?

Visual Studio allows further control about command visibility. The IDE defines visibility contexts and commands can be bound to them. The most frequently used contexts are summarized in Table 4.

Table 4: User interface context definitions

Context name Description
NoSolution No solution is opened in the IDE (the Solution Explorer tool window is empty).
SolutionExists There is an existing solution opened in the IDE. This can be an empty solution, a solution created by opening a single file, a solution with one or more project. Commands bound to this context are visible if you can see a root solution in the Solution Explorer.
EmptySolution There is a solution opened in the IDE and this solution is empty (does not contain any item).
SolutionHasSingleProject There is a solution opened in the IDE and this solution contains exactly one project of any type.
SolutionHasMultipleProjects There is a solution opened in the IDE and this solution contains multiple projects loaded.
SolutionBuilding The current solution or any of its projects is being built. The IDE stays in this context unless the build process finishes.
Debugging The IDE is in Debug mode: its debugger is attached to a running process.
DesignMode The IDE is in design mode (not in Debug mode).
FullScreenMode The IDE is running in Full screen mode (it can be activated with the View | Full Screen function.
Dragging Currently there is an active drag/drop operation in the Visual Studio IDE.

When a command is bound to a set of visibility contexts it is visible only when Visual Studio IDE is in one of the contexts bound to it. You can define your own contexts and notify Visual Studio about entering into the specified context. Commands can be also bound to your custom contexts. Later in this chapter you are going to see a few code samples about handling visibility contexts.

The Visual Studio Command Table

You have already seen the Visual Studio Command Table in a few examples in the previous and also in this chapter. The command table is defined in the .vsct file of the package project and is compiled to a binary resource which is embedded into the package infrastructure resources during the build process. When deploying the package the command table is merged into the menus and toolbars of the IDE.

In this section you dive into the details of the command table structure and create a few more samples to understand the basic concepts. Of course, all nitty-gritty details cannot be handled, but the information here is enough that you can to discover the advanced aspects of the command table by yourself.

Note: The .vsct file was introduced as a new format for the command table substituting the .ctc file in the previous versions of Visual Studio SDK. Visual Studio 2005 SDK used a textual format (using files with .ctc extension). Editing and understanding a .ctc file was not a simple task. With the release of Visual Studio 2008 SDK Microsoft created the new XML-based file format with the .vsct extension and a new compiler to produce the binary .cto format.

The main advantage of using the .vsct file is that it is editable just like other XML files and so has all the great XML editing features like automatic generation of closing tags or IntelliSense based on the VSCT XML schema. Due to the fact XML format has so many advantage the old .ctc format has been deprecated. Microsoft recommends using the VSCT compiler to generate the .cto files, although CTC is still supported.

VSCT File Structure

When describing the elements of an XML file one alternative is to provide an XSD schema. It tells a lot about the syntax and the semantics and is said to be readable by either a human or a machine. There is no doubt that machines can easily understand an XSD, but for understanding concepts behind a certain XML file XSD is not the best way. Instead, here you will be shown small examples to help you understand the structure of the command file.

The root element of this structure is the CommandTable element:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <CommandTable
  3.   xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable"
  4.   xmlns:xs="http://www.w3.org/2001/XMLSchema">
  5.   <!-- Content of the command table -->
  6. </CommandTable>

The elements of the command table are defined by the http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable namespace. You will rarely create the startup state of a command table by hand, because the VSPackage wizard does it for you when you ask it to create an initial command or tool window. When you create the command table by hand, do not forget about specifying this namespace otherwise your command table will not compile. In the code examples later the namespace information will be omitted just for the sake of brevity.

The CommandTable itself uses a few child elements to define the content of the table:

  1. <CommandTable xmlns="..." xmlns:xs="...">
  2.   <Extern/>
  3.   <Include/>
  4.   <Define/>
  5.   <Commands/>
  6.   <CommandPlacements>
  7.   <VisibilityConstraints/>
  8.   <KeyBindings/>
  9.   <UsedCommands/>
  10.   <Symbols/>
  11. </CommandTable>

For a developer new to Visual Studio Extensibility, the Extern, Commands and Symbols elements are the most important and, of course, the most frequently used ones. When the VSPackage wizard creates a package with a simple menu command, you get something similar (the wizard injects a lot of comments to explain the content but here those are omitted):

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <CommandTable xmlns="..." xmlns:xs="...">
  3.   <Extern href="stdidcmd.h"/>
  4.   <Extern href="vsshlids.h"/>
  5.   <Extern href="msobtnid.h"/>
  6.  
  7.   <Commands package="guidSimpleCommandPkg">
  8.     <Groups>
  9.       <Group guid="guidSimpleCommandCmdSet" id="MyMenuGroup" priority="0x0600">
  10.         <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
  11.       </Group>
  12.     </Groups>
  13.  
  14.     <Buttons>
  15.       <Button guid="guidSimpleCommandCmdSet" id="cmdidMyFirstCommand"
  16.         priority="0x0100" type="Button">
  17.         <Parent guid="guidSimpleCommandCmdSet" id="MyMenuGroup" />
  18.         <Icon guid="guidImages" id="bmpPic1" />
  19.         <Strings>
  20.           <CommandName>cmdidMyFirstCommand</CommandName>
  21.           <ButtonText>My First Command</ButtonText>
  22.         </Strings>
  23.       </Button>
  24.     </Buttons>
  25.  
  26.     <Bitmaps>
  27.       <Bitmap guid="guidImages" href="Resources\Images_32bit.bmp"
  28.         usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows"/>
  29.     </Bitmaps>
  30.   </Commands>
  31.  
  32.   <Symbols>
  33.     <GuidSymbol name="guidSimpleCommandPkg"
  34.       value="{2291da24-92e5-4ea4-bdb7-72a9b5ac7d59}" />
  35.     <GuidSymbol name="guidSimpleCommandCmdSet"
  36.       value="{a982b107-4ad4-437e-b2bc-cdf2708aa376}">
  37.       <IDSymbol name="MyMenuGroup" value="0x1020" />
  38.       <IDSymbol name="cmdidMyFirstCommand" value="0x0100" />
  39.     </GuidSymbol>
  40.     <GuidSymbol name="guidImages" value="{5c3faf04-8190-48c4-a6e9-71f04f1848e5}" >
  41.       <IDSymbol name="bmpPic1" value="1" />
  42.       <IDSymbol name="bmpPic2" value="2" />
  43.       <IDSymbol name="bmpPicSearch" value="3" />
  44.       <IDSymbol name="bmpPicX" value="4" />
  45.       <IDSymbol name="bmpPicArrows" value="5" />
  46.     </GuidSymbol>
  47.   </Symbols>
  48.  
  49. </CommandTable>

You already know that commands and other elements of the command table objects are uniquely identified by a compound key composed from a GUID and a 32-bit unsigned integer. Command table elements have references to each other and the unique identifiers are used to describe this reference. The parts of the key are defined by the guid and id attributes, respectively.

In a .vsct file you generally use most element identifiers at least twice: once for identifying an object and at least once for referencing it. If you would use the GUID and uint values directly those would not provide the best readability. If you had to type the same GUID value several times it would make the hand-edited .vsct file very fragile, as humans are not very good in typing, comparing and checking GUID values.

The Symbols section is a central place in the command table file where you can define the identifiers to be used in the other parts of the .vsct file. You can use the GuidSymbol element to define the logical container represented by the GUID and the nested IDSymbol elements to provide (optional) identifiers within the logical container. The name and the value attribute of these elements do exactly what you expect: associate the symbol name with its value. The GUID and uint values of the compound key are checked together, the VSCT compiler takes care of that the uint value should be defined by an IDSymbol nested in the corresponding GuidSymbol elements value.

In the example above there are three GUID containers defined. The first is an empty container (with the symbolic name of guidSimpleCommandPkg) the last two contain a few nested ID elements. The identifiers defined here are referenced in the upper parts of the command table definition as in the following extract:

  1. <Commands package="guidSimpleCommandPkg">
  2.   <Groups>
  3.     <Group guid="guidSimpleCommandCmdSet" id="MyMenuGroup" priority="0x0600">
  4.       <!-- ... -->
  5.     </Group>
  6.   </Groups>
  7.  
  8.   <Buttons>
  9.     <Button guid="guidSimpleCommandCmdSet" id="cmdidMyFirstCommand"
  10.       priority="0x0100" type="Button">
  11.       <!-- ... -->
  12.     </Button>
  13.   </Buttons>
  14.  
  15.   <Bitmaps>
  16.     <Bitmap guid="guidImages" href="Resources\Images_32bit.bmp"
  17.       usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows"/>
  18.   </Bitmaps>
  19. </Commands>

Objects defined by the Visual Studio IDE are frequently referenced in the command table. For example, when you add a menu item to the Tools menu, somehow you must refer to the Tools menu so that the VSCT compiler can understand it. Of course, the Tools menu is identified on the same way as any other command table elements:

  1. <Group guid="guidSimpleCommandCmdSet" id="MyMenuGroup" priority="0x0600">
  2.   <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
  3. </Group>

Here the Parent element defines the location where the logical command group declared by the Group element should be placed. The guidSHLMainMenu defines the logical container of the main menu bar of the IDE and the IDM_VS_MENU_TOOLS is the identifier of the Tools menu within the logical container. As you guess, there are thousands of GUIDs and IDs related to Visual Studio IDE elements.

The Extern element is the key to access them:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <CommandTable xmlns="..." xmlns:xs="...">
  3.   <Extern href="stdidcmd.h"/>
  4.   <Extern href="vsshlids.h"/>
  5.   <Extern href="msobtnid.h"/>
  6.   <!-- ... -->
  7. </CommandTable>

When the VSCT compiler processes the .vsct file it can run a preprocessor on its content. This preprocessor is by default the C++ preprocessor that allows including files defining symbols and macros. The Extern element is one of the elements directing the preprocessor. The href attribute names the file with .h (standard C++ header file) extension. After the preprocess phase the symbols in the #define pragmas and in the macro definitions of referenced files can be used anywhere just like GUID and ID values defined in the Symbols section of the command table.

The files named in the href attributes are searched in the include path passed to the VSCT compiler. This is the VisualStudioIntegration\Common\Inc folder under the root installation folder of VS SDK by default when you create your package with the VSPackage wizard. The wizard also creates three Extern elements. Table 5 describes the role of the referenced files.

Table 5: External file references in the VSCT file

File Content
stdidcmd.h This file represents the command IDs for all commands exposed by Visual Studio. IDs include the visible (or hidden) menu command IDs prefixed with cmdid, standard editor commands with ECMD_ prefix and a few others.
vsshlids.h This file collects command IDs for the menus provided by the Visual Studio Shell.
msobtnid.h This file represents IDs of the standard Microsoft Office commands (many of them, like Cut, Copy and Paste are also used in VS IDE).

If you look into the header files, you can find the definitions for guidSHLMainMenu and IDM_VS_MENU_TOOLS in the vsshlids.h file. The preprocessor understands both the DEFINE_GUID macro and the #define pragma.

  1. ...
  2. DEFINE_GUID (guidSHLMainMenu,
  3.   0xd309f791, 0x903f, 0x11d0, 0x9e, 0xfc, 0x00, 0xa0, 0xc9, 0x11, 0x00, 0x4f);
  4. ...
  5. #define IDM_VS_MENU_TOOLS 0x0085
  6. ...

The DEFINE_GUID macro here sets the guidSHLMainMenu to the value of {d309f791-903f -11d0-9efc-00a0c911004f}. Any time you know a GUID value but you do not know which symbolic name is used for that value, search the VisualStudioIntegration\Common\Inc folder to find if the GUID value is defined there or not. If necessary add a new Extern directive to the command table.

Command Definitions

No doubt, the most important element of the .vsct file is Commands. Its role is to define commands, their initial layout and behavior:

  1. <Commands package="...">
  2.   <Groups/>
  3.   <Menus/>
  4.   <Buttons/>
  5.   <Combos/>
  6.   <Bitmaps/>
  7. </Commands>

Any command in the IDE must belong to the IDE itself or to a package. A package assembly can implement one or more packages. To assign a command to the appropriate (owning) VSPackage, the package attribute of the Commands element must name the GUID of the corresponding package. Generally you have only one package in one assembly so you put the package ID generated by the VSPackage wizard into this attribute:

  1. <Commands package="guidSimpleCommandPkg">
  2.   <!-- ... -->
  3. </Commands>

 

 

As you obviously guessed Visual Studio uses the package ID here to find out which package is to be loaded to handle a command. There are several types of nested child elements in Commands, and each of them has a specific role.

The Groups element defines so-called command groups using a nested Group element. A command group is a logical set of related commands that generally stand together. For example, the Build Solution, Rebuild Solution and Clean Solution menu items form a logical group: they stand together in the Build menu and in the context menu of a Solution item as shown in Figure 5.

f0305

Figure 5: Visual effect of a Group element

Instead of putting individual commands to an existing menu you can place them into a group and that group can be placed to the menu. Even if you have only one element to add to a menu, you must create a Group element for it and put the item into this group. Visual Studio menu groups also have related symbolic IDs in the header files, so you can add a command to one of the predefined groups. Each command in the logical set formed by the group appears in the related menu.

You can nest Menu elements as children of Menus to define a single menu. A single menu is a placeholder for items and of course, you also can put submenu items there.

Menus can have different appearance and behavior. The most frequent forms are:

  • Standard menus (for example the File, Edit and View menus in the IDE).
  • Context menus: They show up when right-clicking on the object they provide a command context for.
  • Toolbars: standard toolbars where commands are organized in rows with icons — and/or text labels — representing them.

The Buttons element is a container for nested Button elements. A Button represents a piece of user interface the user can interact with. The name is a bit confusing, because when saying the word button you generally associate it with the pushbutton. Here, Button relates to a menu or toolbar item. In .vsct you can define a few types of Button items:

  • Standard buttons represent a simple menu items which execute a command.
  • Menu buttons display a submenu.
  • Dropdown buttons allow functions like Undo and Redo on the main toolbar of the IDE.
  • “Swatch” buttons display color choices such as those in a font color dialog.

Not surprisingly Combos is a container for Combo elements. A Combo defines a set of commands that appear in a combo box.

Toolbars and menus would be poor without icons helping the user to associate a small image with the function. The Bitmaps section allows defining the visual elements (icons) used in menus. The section describes Bitmap elements that are uniquely identified. A bitmap can come from an external file or from a package resource.

When referring to bitmaps, you can use a few formats like .bmp, .gif, .png. Right now you cannot use all formats in the same way. For example, you may have problems with the 32-bit .bmp formats (alpha channel information for partial transparency). If you use 120 DPI in your display settings, the original 16x16 pixel images will be stretched to a 20x20 pixel size. If you use .png format, stretching is smooth without disturbing artifacts. In some cases only a few formats are accepted: for example for icons representing tool windows on their tabs, .png format is not welcomed (no icon appears), only 24-bit .bmp with fuchsia as the transparent color.

If you have problems with icon images, try a few formats and you can generally find the one expected by VS IDE.

Working with Commands and Bitmaps

To successfully define commands appearing in the VS IDE menus you have to use at least one Group and one Button element. If you want to add icons, you need at least one additional Bitmap element. If you prefer your own menu instead of inserting a command group in the middle of a Visual Studio menu, at least one Menu element should be defined. To establish the layout and the relation among the elements you have to look behind their attributes and child elements.

Menu, Group, Button and other elements have common attributes and child element with very similar semantics summarized in Table 6.

Table 6: Common attributes of command table elements

Attribute Description
guid The GUID part of the element identifier. This attribute is required.
id The uint part of the element identifier. This attribute is required.
priority An optional numeric value determining the order of the element. The lower this value is the closer the element is to the position of the first element. Visual Studio IDE sorts elements according to their priority. However, you cannot know the priority value of all other elements, so exact position is not guaranteed.
type An optional stereotype of the element determining its layout and behavior. The Group element does not have this attribute (as it is a behavior-less element). The possible values and the meaning of this attribute changes according to the element type.

There are child elements connecting UI elements together and adding properties which fine-tune layout and behavior. Except of Bitmap all other types of Commands children have the child elements in Table 7.

Table 7: Common child elements

Child element Description
Parent

Optional parent of the element. A command element can be attached to one or more menu items. Here you can define zero or one Parent element. If you want to attach a command to more than one menu item, the CommandPlacement element is for you.

The Parent element has the guid and id attribute to uniquely name the parent the element belongs to. For a Button the parent element must refer to a command Group. For a Group, the parent must refer to a Menu.

Annotation This element allows adding optional comment information to the element. Annotations can have either simple text or a nested structure.

Table 8 summarizes child elements extending the layout and behavior of Menu, Button and Combo.

Table 8: Child elements influencing behavior

Child element Description
CommandFlag This child element can be applied zero, one or more times to its parent element. As the name indicates it sets flags influencing the layout and behavior of its parent. The VSCT Schema Reference enumerates all the command flags that can be applied to a particular element. Several flags have effect only when combining with other flags. For example, applying the DynamicVisibility and DefaultInvisible flags causes a menu item to be hidden at Visual Studio startup time.
Strings This element is a container for children representing string information of UI elements. At least the ButtonText child of Strings should be applied to display a caption for the element. Possible child elements include ButtonText, ToolTipText, MenuText, CommandName and others. For details see the VSCT Schema reference.
Icon Available only for Button. Optionally defines the icon associated with the button.

The Bitmaps section of Commands provides a place to define bitmaps you want to use in menu and toolbar items. Each definition must be in a Bitmap element. The concept of Bitmap covers one bitmap with a physical size of 16x16 pixels or a bitmap strip that has 16xN pixels where N is a multiple of 16 (number of pixels in a column). Bitmap handles the simple 16x16 pixel bitmap as a bitmap strip, where N equals 1. A Bitmap is identified with a GUID that must be a separate GUID from the package ID, command set IDs, and so on. When you refer to a certain item in the bitmap strip you do it by using a one-based index for the bitmap in the strip.

Just as in case of commands, menu IDs you can use the Symbols section of the command table to define IDs related to a bitmap. For example when you create a new Tool window package With the VSPackage wizard, the following entries are related to bitmaps:

 

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <CommandTable xmlns="..." xmlns:xs="...">
  3.   <!-- Extern elements -->
  4.   <Commands package="...">
  5.     <!-- Menus, Groups and Buttons -->
  6.     <Bitmaps>
  7.       <Bitmap guid="guidImages" href="Resources\Images_32bit.bmp"
  8.         usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows"/>
  9.     </Bitmaps>
  10.   </Commands>
  11.   <Symbols>
  12.     <GuidSymbol name="guidImages" value="{...}" >
  13.       <IDSymbol name="bmpPic1" value="1" />
  14.       <IDSymbol name="bmpPic2" value="2" />
  15.       <IDSymbol name="bmpPicSearch" value="3" />
  16.       <IDSymbol name="bmpPicX" value="4" />
  17.       <IDSymbol name="bmpPicArrows" value="5" />
  18.     </GuidSymbol>
  19.   </Symbols>
  20. </CommandTable

The Symbols section defines a GUID used only by the Bitmap definition (guidImages) and symbolic names for the one-based bitmap indexes within the strip. In the example above bmpPicSearch is the third picture within the strip.

The Bitmap element’s href attribute names the file where the bitmap strip can be found. This folder is relative to the location of the .vsct file. The usedList attribute enumerates the symbolic indices that can be used to identify bitmaps in the strip. If you want to set a button icon to the third image in the strip, you should use the following Icon child element in a Button:

  1. <Button ...="">
  2.   <Icon guid="guidImages" id="bmpPicSearch" >
  3. </Button>

You are allowed to use stocked icons. For example, the CommandState sample uses such icons:

  1. <Icon guid="guidOfficeIcon" id="msotcidClock" />

The guidOfficeIcon symbol is defined in the vsshlids.h file and is a logical container for office icons. The msotcidClock and thousands of other IDs can be found in the msobtnid.h file. If you are looking for stocked icons, definitely this is the place you should search for them. The msobtnid.h contains comments that help you to identify icons you are looking for.

Basic VSCT Samples

By now you have an understanding of the essential VSCT concepts, moreover, you have seen a few samples. In order to illustrate the structure and usage of command table element you are going to build a few more samples. You start from a very simple package created with the help of VSPackage wizard using a simple menu command. The package does not have any real function, because it is just a container for menu resources, the package class definition is quite simple as Listing 3 shows.

Listing 3: The BasicVSCTSamplePackage definition

  1. // --- BasicVSCTSamplePackage.cs
  2.  
  3. using System.Runtime.InteropServices;
  4. using Microsoft.VisualStudio.Shell;
  5.  
  6. namespace DeepDiver.BasicVSCTSample
  7. {
  8.   [PackageRegistration(UseManagedResourcesOnly = true)]
  9.   [InstalledProductRegistration(false, "#110", "#112", "1.0", IconResourceID = 400)]
  10.   [ProvideMenuResource("Menus.ctmenu", 1)]
  11.   [Guid(GuidList.guidBasicVSCTSamplePkgString)]
  12.   public sealed class BasicVSCTSamplePackage : Package
  13.   {
  14.   }
  15. }
  16.  
  17. // --- Guids.cs
  18.  
  19. namespace DeepDiver.BasicVSCTSample
  20. {
  21.   static class GuidList
  22.   {
  23.     public const string guidBasicVSCTSamplePkgString =
  24.       "41fe6025-c174-4d02-af4f-ea948a272830";
  25.   }
  26. }

You are going to work only with the BasicVSCTSample.vsct file. You start from simple scenario and transform the .vsct file to discover new usage opportunities. You can use the VSPackage wizard to create the sample, or open the BasicVSCTSample project downloaded from the book’s website.

Creating a Main Menu Level Command

The VSPackage wizard puts menu items in a fixed place: in the Tools menu if you create a simple command or into the View menu if you create a simple tool window. However, when you create a package you often would like to put your own package-specific menu in the main menu bar of Visual Studio. Now you are going to learn how to do that.

You create a new main level menu VSCTSample with two menu command items. This task requires the following steps:

  • Step1: Create a GuidSymbol element with children for the symbols you are going to use. Nest this element in the Symbols section.
  • Step 2: Create a Menu element representing the main level menu. Put this item into the Menus section and set up the Parent of this menu to a main menu level menu group.
  • Step 3: Create a Group element representing the logical group holding the menu command items. Set the Parent of this group to the Menu created in the previous step.
  • Step 4: Create two Button elements representing the commands and set their Parent to the Group created previously.

Following the steps the .vsct file looks like in Listing 4.

Listing 4: Creating main menu commands

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable"
  3.   xmlns:xs="http://www.w3.org/2001/XMLSchema">
  4.   <Extern href="stdidcmd.h"/>
  5.   <Extern href="vsshlids.h"/>
  6.   <Extern href="msobtnid.h"/>
  7.  
  8.   <Commands package="guidBasicVSCTSamplePkg">
  9.     <Menus>
  10.       <Menu guid="guidBasicVSCTSampleCmdSet" id="TopLevelMenu" priority="0x100"
  11.         type="Menu">
  12.         <Parent guid="guidSHLMainMenu" id="IDG_VS_MM_BUILDDEBUGRUN" />
  13.         <Strings>
  14.           <ButtonText>VSCTSample</ButtonText>
  15.           <CommandName>VSCTSample</CommandName>
  16.         </Strings>
  17.       </Menu>
  18.     </Menus>
  19.  
  20.     <Groups>
  21.       <Group guid="guidBasicVSCTSampleCmdSet" id="TopLevelMenuGroup"
  22.         priority="0x0600">
  23.         <Parent guid="guidBasicVSCTSampleCmdSet" id="TopLevelMenu"/>
  24.       </Group>
  25.     </Groups>
  26.  
  27.     <Buttons>
  28.       <Button guid="guidBasicVSCTSampleCmdSet" id="FirstCommand" priority="0x0100"
  29.         type="Button">
  30.         <Parent guid="guidBasicVSCTSampleCmdSet" id="TopLevelMenuGroup" />
  31.         <Strings>
  32.           <CommandName>FirstCommand</CommandName>
  33.           <ButtonText>First Command</ButtonText>
  34.         </Strings>
  35.       </Button>
  36.       <Button guid="guidBasicVSCTSampleCmdSet" id="SecondCommand"
  37.         priority="0x0101" type="Button">
  38.         <Parent guid="guidBasicVSCTSampleCmdSet" id="TopLevelMenuGroup" />
  39.         <Strings>
  40.           <CommandName>SecondCommand</CommandName>
  41.           <ButtonText>Second Command</ButtonText>
  42.         </Strings>
  43.       </Button>
  44.     </Buttons>
  45.  
  46.   </Commands>
  47.  
  48.   <Symbols>
  49.     <GuidSymbol name="guidBasicVSCTSamplePkg"
  50.       value="{41fe6025-c174-4d02-af4f-ea948a272830}" />
  51.     <GuidSymbol name="guidBasicVSCTSampleCmdSet"
  52.       value="{234580c4-8a2c-4ae1-8e4f-5bc708b188fe}">
  53.       <IDSymbol name="TopLevelMenu" value="0x0100" />
  54.       <IDSymbol name="TopLevelMenuGroup" value="0x0200" />
  55.       <IDSymbol name="FirstCommand" value="0x0300" />
  56.       <IDSymbol name="SecondCommand" value="0x0301" />
  57.     </GuidSymbol>
  58.   </Symbols>
  59.  
  60. </CommandTable>

As you see, you have to write quite a lot for such a simple task. In the menu definition the IDs used in the Parent elements are highlighted. If you build and run your package, you can discover the new menu located just after the View menu group and before the Tools menu as Figure 6 shows.

f0306

Figure 6: New main menu item

When you open a solution the Build menu gets visible and VSCTSample menu goes between Build and Debug as it can be seen in Figure 7.

f0307

Figure 7: VSCTSample is located between Build and Debug

This is due to the Parent definition of Menu and the priority you assigned to it:

  1. <Menu guid="guidBasicVSCTSampleCmdSet" id="TopLevelMenu" priority="0x100" type="Menu">
  2.   <Parent guid="guidSHLMainMenu" id="IDG_VS_MM_BUILDDEBUGRUN" />
  3.   <Strings>
  4.     <ButtonText>VSCTSample</ButtonText>
  5.     <CommandName>VSCTSample</CommandName>
  6.   </Strings>
  7. </Menu>

 

 

 

The IDG_VS_MM_BUILDDEBUGRUN is the identifier of a logical group representing the Build and Debug menus on the main VS IDE menu bar. The priority value of 0x100 sets our menu to be shown before the Debug menu. If you change priority to 0x700 (and rebuild the package and run) VSCTSample moves after the Debug menu, as Figure 8 shows.

f0308

Figure 8: Changing the location of VSCTSample item

Looking into the Button definition you may say there is no need for a Group definition: let’s set the Parent attribute of Buttons directly to the Menu item like in the following example:

  1. <Button ...="">
  2.   <Parent guid=" guidBasicVSCTSampleCmdSet " id="TopLevelMenu" />
  3.   <!-- ... -->
  4. </Button>

When running the package, no VSCTSample menu will appear. The cause of this phenomenon is the fact that buttons cannot have menus as parents. They must have command groups as parents to be displayed in a menu. Because your VSCTSample menu does not have any child, it is not displayed.

Separating Command Groups in a Menu

As it’s been treated, a command group is a logical container for commands belonging together. This kind of grouping also can be used for visual effects. If you put more than one command group in a menu, a separator is created to visually emphasize the separation of command groups. Let’s add two more commands with a separate group to the .vsct file you’ve created above:

  • Step 1: Add new symbols representing the two new buttons and the group for new commands.
  • Step 2: Create new Group definition
  • Step 3: Define two new Button elements parented in the newly created Group.

After the third step your new .vsct file looks like in Listing 5. Please note, only new parts added in the steps above are indicated in this listing:

Listing 5: Separating Command Groups in a Menu

 

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <CommandTable xmlns="..." xmlns:xs="...">
  3.   <!-- Extern elements -->
  4.   <Commands package="...">
  5.     <!-- Menus section unchanged -->
  6.  
  7.     <Groups>
  8.       <!-- New group added -->
  9.       <Group guid="guidBasicVSCTSampleCmdSet" id="TopLevelMenuGroup2"
  10.         priority="0x0600">
  11.         <Parent guid="guidBasicVSCTSampleCmdSet" id="TopLevelMenu"/>
  12.       </Group>
  13.     </Groups>
  14.  
  15.     <Buttons>
  16.       <!-- New buttons added -->
  17.       <Button guid="guidBasicVSCTSampleCmdSet" id="ThirdCommand" priority="0x0100"
  18.         type="Button">
  19.         <Parent guid="guidBasicVSCTSampleCmdSet" id="TopLevelMenuGroup2" />
  20.         <Strings>
  21.           <CommandName>ThirdCommand</CommandName>
  22.           <ButtonText>Third Command</ButtonText>
  23.         </Strings>
  24.       </Button>
  25.       <Button guid="guidBasicVSCTSampleCmdSet" id="FourthCommand"
  26.         priority="0x0101" type="Button">
  27.         <Parent guid="guidBasicVSCTSampleCmdSet" id="TopLevelMenuGroup2" />
  28.         <Strings>
  29.           <CommandName>FourthCommand</CommandName>
  30.           <ButtonText>Fourth Command</ButtonText>
  31.         </Strings>
  32.       </Button>
  33.     </Buttons>
  34.   </Commands>
  35.  
  36.   <Symbols>
  37.     <GuidSymbol name="guidHowToPackageCmdSet" value="{...}" >
  38.       <!-- New IDSymbols added -->
  39.       <IDSymbol name="TopLevelMenuGroup2" value="0x0201" />
  40.       <IDSymbol name="ThirdCommand" value="0x0302" />
  41.       <IDSymbol name="FourthCommand" value="0x0303" />
  42.     </GuidSymbol>
  43.     <!-- Other Guids for the package -->
  44.   </Symbols>
  45.  
  46. </CommandTable>

Running the package after these modifications you can see the VSCTSample menu where the two command groups are separated, as shown in Figure 9.

f0309

Figure 9: Separating commands into groups

Adding Icons to the Menu Items

If you want to add icons to the menu items representing a command, you have to define a Bitmap node and assign it to the Button elements with their Icon property:

  • Step 1: Create IDs for the bitmap strip and each individual icon in the strip.
  • Step 2: Create the Bitmap element and set it up to use the icons in the strip.
  • Step 3: Add Icon properties to the buttons.

After this change the .vsct file changes as indicated in Listing 6.

Listing 6: Adding Icons to the Buttons

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <CommandTable xmlns="..." xmlns:xs="...">
  3.   <!-- Extern elements -->
  4.   <Commands package="...">
  5.     <!-- Menus section unchanged -->
  6.     <!-- Groups section unchanged -->
  7.     <Buttons>
  8.       <Button guid="guidBasicVSCTSampleCmdSet" id="FirstCommand" priority="0x0100"
  9.         type="Button">
  10.         <!-- Icon added, other children unchanged -->
  11.         <Icon guid="guidImages" id="bmpPic1" />
  12.       </Button>
  13.       <Button guid="guidBasicVSCTSampleCmdSet" id="SecondCommand"
  14.         priority="0x0101" type="Button">
  15.         <!-- Icon added, other children unchanged -->
  16.         <Icon guid="guidImages" id="bmpPic2" />
  17.       </Button>
  18.       <Button guid="guidBasicVSCTSampleCmdSet" id="ThirdCommand" priority="0x0100"
  19.         type="Button">
  20.         <!-- Icon added, other children unchanged -->
  21.         <Icon guid="guidImages" id="bmpPicX" />
  22.       </Button>
  23.       <Button guid="guidBasicVSCTSampleCmdSet" id="FourthCommand"
  24.         priority="0x0101" type="Button">
  25.         <!-- Icon added, other children unchanged -->
  26.         <Icon guid="guidImages" id="bmpPicArrows" />
  27.       </Button>
  28.     </Buttons>
  29.  
  30.     <Bitmaps>
  31.       <!-- A new Bitmap added -->
  32.       <Bitmap guid="guidImages" href="Resources\Images_24bit.bmp"
  33.         usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows"/>
  34.     </Bitmaps>
  35.   </Commands>
  36.  
  37.   <Symbols>
  38.     <!-- New GuidSymbol section added -->
  39.     <GuidSymbol name="guidImages" value="{...}" >
  40.       <IDSymbol name="bmpPic1" value="1" />
  41.       <IDSymbol name="bmpPic2" value="2" />
  42.       <IDSymbol name="bmpPicSearch" value="3" />
  43.       <IDSymbol name="bmpPicX" value="4" />
  44.       <IDSymbol name="bmpPicArrows" value="5" />
  45.     </GuidSymbol>
  46.     <!-- Other Guids for the package -->
  47.   </Symbols>
  48.  
  49. </CommandTable>

As you can see, separate identifiers with bmp prefix are defined to index icons in the bitmap strip. The Bitmap element references the physical resource and in the usedList attribute enumerates the symbols for the icons which can be referenced in Button definitions. Button elements use the Icon node to name the bitmap with guidImages and the appropriate strip index with its symbolic name.

Running the package with this .vsct file results the menu in Figure 10.

f0310

Listing 10: Adding Icons to the buttons

Playing with CommandFlag Values

Menu item behavior can be influenced by using so-called command flags. To discover a few of them you modify two buttons with CommandFlag elements. Add a TextOnly flag to the first command. Although the first command has an associated Icon, the TextOnly command flag sets the menu item appearance to show only the text and not the icon. Add a DynamicVisibility and a DefaultDisabled flag to the third command. These flags initialize the command in disabled state by default. Listing 7 shows the changes.

Listing 7: Applying CommandFlags

  1. <Buttons>
  2.   <Button guid="guidBasicVSCTSampleCmdSet" id="FirstCommand" priority="0x0100"
  3.     type="Button">
  4.     <!-- CommandFlag added, other children unchanged -->
  5.     <CommandFlag>TextOnly</CommandFlag>
  6.   </Button>
  7.   <Button guid="guidBasicVSCTSampleCmdSet" id="ThirdCommand" priority="0x0100"
  8.     type="Button">
  9.     <!-- CommandFlag added, other children unchanged -->
  10.     <CommandFlag>DynamicVisibility</CommandFlag>
  11.     <CommandFlag>DefaultDisabled</CommandFlag>
  12.   </Button>
  13.   <!-- Other buttons are unchanged -->
  14. </Buttons>

After building and running the package you can recognize the effect of command flag applied as Figure 11 shows.

f0311

Figure 11: Using CommandFlag elements to modify behavior

Creating Submenus

You have already seen how to create a new main menu item and separate items visually by using command groups. Now you make a little modification to use submenus for grouping related commands. Remove the command flags you have added in Listing 7 and change the VSCT content:

  • Step 1: Rename the TopLevelMenuGroup2 to SubMenuGroup (and all references). You turn the group of third and fourth command into a submenu group.
  • Step 2: Add a new menu representing the submenu and attach it to the existing TopLevelMenuGroup. Name it SubMenu and create a symbol for it.
  • Step 3: Attach the SubMenuGroup to the newly created submenu.

Listing 8 indicates the changes you have done.

Listing 8: Moving commands into a submenu

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <CommandTable xmlns="...">
  3.   <!-- Extern section unchanged -->
  4.   <Commands package="guidHowToPackagePkg">
  5.     <Menus>
  6.       <!-- New menu added -->
  7.       <Menu guid="guidBasicVSCTSampleCmdSet" id="SubMenu" priority="0x200"
  8.         type="Menu">
  9.         <Parent guid="guidBasicVSCTSampleCmdSet" id="TopLevelMenuGroup" />
  10.         <Strings>
  11.           <ButtonText>Other Commands</ButtonText>
  12.           <CommandName>Other Commands</CommandName>
  13.         </Strings>
  14.       </Menu>
  15.     </Menus>
  16.     <Groups>
  17.       <!-- Group changed to SubMenuGroup and attached to SubMenu -->
  18.       <Group guid="guidBasicVSCTSampleCmdSet" id="SubMenuGroup"
  19.         priority="0x0600">
  20.         <Parent guid="guidBasicVSCTSampleCmdSet" id="SubMenu"/>
  21.       </Group>
  22.     </Groups>
  23.  
  24.     <Buttons>
  25.       <!-- We attached these two buttons to SubMenuGroup -->
  26.       <Button guid="guidBasicVSCTSampleCmdSet" id="ThirdCommand" priority="0x0100"
  27.         type="Button">
  28.         <Parent guid="guidBasicVSCTSampleCmdSet" id="SubMenuGroup" />
  29.         <Icon guid="guidImages" id="bmpPicX" />
  30.         <Strings>
  31.           <CommandName>ThirdCommand</CommandName>
  32.           <ButtonText>Third Command</ButtonText>
  33.         </Strings>
  34.       </Button>
  35.       <Button guid="guidBasicVSCTSampleCmdSet" id="FourthCommand"
  36.         priority="0x0101" type="Button">
  37.         <Parent guid="guidBasicVSCTSampleCmdSet" id="SubMenuGroup" />
  38.         <Icon guid="guidImages" id="bmpPicArrows" />
  39.         <Strings>
  40.           <CommandName>FourthCommand</CommandName>
  41.           <ButtonText>Fourth Command</ButtonText>
  42.         </Strings>
  43.       </Button>
  44.     </Buttons>
  45.  
  46.   </Commands>
  47.  
  48.   <Symbols>
  49.     <!-- We add a SubMenu and changed SubMenuGroup -->
  50.     <GuidSymbol name="guidBasicVSCTSampleCmdSet" value="...">
  51.       <IDSymbol name="SubMenu" value="0x0101" />
  52.       <IDSymbol name="SubMenuGroup" value="0x0201" />
  53.     </GuidSymbol>
  54.   </Symbols>
  55. </CommandTable>

After building and running the package with the modified command table, you can see the submenu as Figure 12 shows.

f0312

Figure 12: Commands moved to a submenu

If you carefully examine the command table, you can observe the following nesting: TopLevelMenu --> TopLevelMenuGroup --> SubMenu --> SubMenuGroup --> Button elements. As you see, menus can be nested to each other through Group elements. Should you break this nesting the menu would not display as expected.

Adding Shortcut Keys to Menu Items

The command table allows you to bind shortcut keys to commands, and here you are going to examine this feature. Till this time you’ve used all commands in the BasicVSCTSample package that have no code behind to execute. In order to demonstrate that shortcut keys are working, add code to commands to display the name of the command invoked. Listing 9 shows the code of BasicVSCTSamplePackage:

Listing 9: Command handling in BasicVSCTSample

  1. using System;
  2. using System.ComponentModel.Design;
  3. using System.Runtime.InteropServices;
  4. using System.Text;
  5. using Microsoft.VisualStudio.Shell;
  6. using Microsoft.VisualStudio.Shell.Interop;
  7.  
  8. namespace DeepDiver.BasicVSCTSample
  9. {
  10.   [PackageRegistration(UseManagedResourcesOnly = true)]
  11.   [InstalledProductRegistration(false, "#110", "#112", "1.0", IconResourceID = 400)]
  12.   [ProvideMenuResource("Menus.ctmenu", 1)]
  13.   [Guid(GuidList.guidBasicVSCTSamplePkgString)]
  14.   public sealed class BasicVSCTSamplePackage : Package
  15.   {
  16.     private OleMenuCommandService _CommandService;
  17.  
  18.     protected override void Initialize()
  19.     {
  20.       base.Initialize();
  21.       _CommandService = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
  22.       RegisterCommand(CmdIDs.FirstCommand, "First Command", CommandHandlerCallback);
  23.       RegisterCommand(CmdIDs.SecondCommand, "Second Command", CommandHandlerCallback);
  24.       RegisterCommand(CmdIDs.ThirdCommand, "Third Command", CommandHandlerCallback);
  25.       RegisterCommand(CmdIDs.FourthCommand, "Fourth Command", CommandHandlerCallback);
  26.     }
  27.  
  28.     private void CommandHandlerCallback(object caller, EventArgs args)
  29.     {
  30.       var command = caller as OleMenuCommand;
  31.       if (command == null) return;
  32.       OutputCommandString(command.Text + " has been invoked");
  33.     }
  34.  
  35.     private void RegisterCommand(uint id, string initialText,
  36.       EventHandler callback)
  37.     {
  38.       if (_CommandService == null) return;
  39.       var menuCommandID = new CommandID(GuidList.guidBasicVSCTSampleCmdSet, (int)id);
  40.       var menuItem = new OleMenuCommand(callback, menuCommandID) { Text = initialText };
  41.       _CommandService.AddCommand(menuItem);
  42.     }
  43.  
  44.     private void OutputCommandString(string text)
  45.     {
  46.       // --- Build the string to write on the debugger and Output window.
  47.       var outputText = new StringBuilder();
  48.       outputText.AppendFormat("BasicVSCTSamplePackage: {0} ", text);
  49.       var outputWindow = GetService(typeof(SVsOutputWindow)) as IVsOutputWindow;
  50.       if (outputWindow == null) return;
  51.       var guidGeneral = Microsoft.VisualStudio.VSConstants.GUID_OutWindowDebugPane;
  52.       IVsOutputWindowPane windowPane;
  53.       if (Microsoft.VisualStudio.ErrorHandler.Failed(
  54.         outputWindow.GetPane(ref guidGeneral, out windowPane)))
  55.       {
  56.         return;
  57.       }
  58.       windowPane.Activate();
  59.       windowPane.OutputStringThreadSafe(outputText.ToString());
  60.     }
  61.   }
  62. }

Each command invokes the CommandHandlerCallback method as a response for their invocation. This method uses the OutputCommandString method to display the name of the command. The RegisterCommand method is used to bind the commands to the code to execute and setup their Text property. Because you do not use the TextChanges command flag the values set here never get reflected in the menu items.

While shortcut keys are generally assigned to menu items, in Visual Studio shortcuts are assigned to commands. Because the same command can be used in different contexts, shortcuts of commands are also interpreted in different contexts. It might be so that in a certain context a command does not have a keyboard shortcut while in another it has. In a third context the command may even have a different shortcut.

The VSCT schema has an element called KeyBinding that is put into the KeyBindings container like in the following example:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <CommandTable xmlns="..." xmlns:xs="...">
  3.   <Commands package="...">
  4.     <!-- Command information goes here -->
  5.   </Commands>
  6.  
  7.   <KeyBindings>
  8.     <KeyBinding guid=".." id=".." ... />
  9.     <!-- More key bindings -->
  10.   </KeyBindings>
  11.  
  12. </CommandTable>

Please note, that KeyBindings is located outside of the Command element, directly within the CommandTable element. In the BasicVSCTSample you can add shortcut keys as defined in Listing 10.

Listing 10: Shortcut key definitions in BasicVSCTSample

  1. <KeyBindings>
  2.   <KeyBinding guid="guidBasicVSCTSampleCmdSet" id="FirstCommand" editor="guidVSStd97"
  3.     key1="VK_F6" mod1="Control Alt" key2="VK_F1" />
  4.   <KeyBinding guid="guidBasicVSCTSampleCmdSet" id="SecondCommand" editor="guidVSStd97"
  5.     key1="VK_F6" mod1="Control Alt" key2="VK_F2" />
  6. </KeyBindings>

You can assign not only a simple hotkey for a command but also two. The key1 and mod1 attributes set the first hotkey, key2 and mod2 the second one. These hotkeys are not alternatives of each other: you have to press the first then the second to activate the command.

For example, in the code above for FirstCommand the first key is Ctrl+Alt+F7, the second F1. In values of key1 and key2 attributes you can use alphanumeric characters available on the keyboard, VK_ constants (IntelliSense will offer you as you type) or two digit hexadecimal constants with 0x prefix.

The values of mod1 and mod2 specify the modification keys. You can use the Control, Alt and Shift values or a combination of them where the value items are separated by a space.

With the guid and id attributes you can bind the specified key combination to the appropriate command. The editor attribute defines the context for a specific binding. The guidVSStd97 value defines that the command is global, so it is available in anywhere in Visual Studio.

Shortcut keys are displayed in menu items as Figure 13 shows.

f0313

Figure 13: Shortcut keys are displayed

Open the Output window and use the First Command and Second Command menu items with their keyboard shortcuts. You can see the trace messages in the Output window indicating that the commands are executed.

Advanced VSCT Samples

You have just scratched the surface of the command table features as you created a main menu item and played with groupings, icons and submenus. In this section you dive a bit deeper and look after other useful samples: you are going to build toolbars, menu controllers and examine visibility contexts.

You are going to examine a new package (AdvancedVSCTSample) that was created with VSPackage wizard. You can download it from the book’s website. The package is non-functional, it is only a container for resources, so it has an empty body.

Creating a Toolbar

In many cases a toolbar represents commands in a more useful way than a menu does. Here you are going to turn a menu above into a toolbar and then play with a few options. The steps to create the toolbar with the commands are very similar to the ones you’ve used when creating a main menu item.

  • Step1: Create a GuidSymbol element with children for the symbols we are going to use. Nest this element in the Symbols section.
  • Step 2: Create a Menu element representing the toolbar menu. Put this item into the Menus section and set up the Parent pointing to the menu item itself.
  • Step 3: Create a Group element representing the logical group holding the four toolbar command items. Set the Parent of this group to the Menu created in the previous step.
  • Step 4: Create four Button elements representing the commands and set their Parent to the Group created previously.

After these steps the .vsct file should look like in Listing 11.

Listing 11: Creating a toolbar

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <CommandTable xmlns="...">
  3.   <!-- Extern section unchanged -->
  4.   <Commands package="guidAdvancedVSCTSamplePkg">
  5.     <Menus>
  6.       <Menu guid="guidAdvancedVSCTSampleCmdSet" id="VSCTToolbar" priority="0x0000"
  7.         type="Toolbar">
  8.         <Parent guid="guidAdvancedVSCTSampleCmdSet" id="VSCTToolbar" />
  9.         <CommandFlag>DefaultDocked</CommandFlag>
  10.         <Strings>
  11.           <CommandName>VSCTSampleToolbar</CommandName>
  12.           <ButtonText>VSCT Sample Toolbar</ButtonText>
  13.         </Strings>
  14.       </Menu>
  15.     </Menus>
  16.  
  17.     <Groups>
  18.       <Group guid="guidAdvancedVSCTSampleCmdSet" id="VSCTToolbarGroup"
  19.         priority="0x0600">
  20.         <Parent guid="guidAdvancedVSCTSampleCmdSet" id="VSCTToolbar"/>
  21.       </Group>
  22.     </Groups>
  23.  
  24.     <Buttons>
  25.       <Button guid="guidAdvancedVSCTSampleCmdSet" id="FirstCommand" priority="0x0100"
  26.         type="Button">
  27.         <Parent guid="guidAdvancedVSCTSampleCmdSet" id="VSCTToolbarGroup" />
  28.         <Strings>
  29.           <CommandName>FirstCommand</CommandName>
  30.           <ButtonText>First Command</ButtonText>
  31.         </Strings>
  32.       </Button>
  33.       <Button guid="guidAdvancedVSCTSampleCmdSet" id="SecondCommand"
  34.         priority="0x0101" type="Button">
  35.         <Parent guid="guidAdvancedVSCTSampleCmdSet" id="VSCTToolbarGroup" />
  36.         <Strings>
  37.           <CommandName>SecondCommand</CommandName>
  38.           <ButtonText>Second Command</ButtonText>
  39.         </Strings>
  40.       </Button>
  41.       <Button guid="guidAdvancedVSCTSampleCmdSet" id="ThirdCommand" priority="0x0102"
  42.         type="Button">
  43.         <Parent guid="guidAdvancedVSCTSampleCmdSet" id="VSCTToolbarGroup" />
  44.         <Strings>
  45.           <CommandName>ThirdCommand</CommandName>
  46.           <ButtonText>Third Command</ButtonText>
  47.         </Strings>
  48.       </Button>
  49.       <Button guid="guidAdvancedVSCTSampleCmdSet" id="FourthCommand"
  50.         priority="0x0103" type="Button">
  51.         <Parent guid="guidAdvancedVSCTSampleCmdSet" id="VSCTToolbarGroup" />
  52.         <Strings>
  53.           <CommandName>FourthCommand</CommandName>
  54.           <ButtonText>Fourth Command</ButtonText>
  55.         </Strings>
  56.       </Button>
  57.     </Buttons>
  58.  
  59.   </Commands>
  60.  
  61.   <Symbols>
  62.     <GuidSymbol name="guidAdvancedVSCTSamplePkg" value="..." />
  63.     <GuidSymbol name="guidAdvancedVSCTSampleCmdSet" value="...">
  64.       <IDSymbol name="VSCTToolbar" value="0x0100" />
  65.       <IDSymbol name="VSCTToolbarGroup" value="0x0200" />
  66.       <IDSymbol name="FirstCommand" value="0x0300" />
  67.       <IDSymbol name="SecondCommand" value="0x0301" />
  68.       <IDSymbol name="ThirdCommand" value="0x0302" />
  69.       <IDSymbol name="FourthCommand" value="0x0303" />
  70.     </GuidSymbol>
  71.   </Symbols>
  72. </CommandTable>

The key in this scenario is the Menu element that uses the Toolbar value in its type attribute. Standard menus have parent groups defined by Visual Studio. However, for toolbars the concept of parent menu or menu group has no meaning just like as a nested toolbars does not. As a convention, you set the Parent of a toolbar to the same GUID and ID pair as used for the toolbar identification. The first time the toolbar is shown up you want it to be docked so you set a CommandFlag with the value of DefaultDocked. The Button elements now are parented into the Group element that points to the Menu instance.

Now, you can build the project and see how the toolbar works. Start debugging and make the toolbar visible by enabling our toolbar with View ð Toolbars ð VSCTSampleToolbar menu item. The toolbar will be shown docked right under the standard toolbar as shown in Figure 14.

f0314

Figure 14: Toolbar with button placeholders

Note: In previous Visual Studio versions this .vcst file would have resulted in a toolbar with four button placeholders without any text. To achieve the save visual properties you can see in Figure 3-14 the TextOnly command flag should have been assigned to the Button elements. Visual Studio 2010 changes this behavior. By default, when a toolbar button does not have an icon, its text is displayed even if the TextOnly command flag is not used.

Well, this toolbar resembles to a standard menu, the original concept of a toolbar seems lost. So, let’s move on using icons. To set up button icons you are going to use exactly the same code changes as used in Listing 3-6. Now the result shown in Figure 15 rather resembles to a toolbar than the first one.

f0315

Figure 15: Toolbar buttons with text

With toolbars you can use some other controls. Let’s see a few examples!

Menu Controllers on Toolbars

By now you have seen examples for two kinds of menus: standard menus and submenus. In toolbars you can use a third kind of menu called menu controller: this is a split-button drop-down menu. You can change the toolbar above so that the last three buttons go into a menu controller just like in Figure 16.

f0316

Figure 16: Menu controller with three items

Creating a menu controller is very similar to creating a submenu. To setup the items as in Error! Reference source not found. the following steps should be carried out:

  • Step 1: Create IDs for a new Menu element and a new Group.
  • Step 2: Create a Menu item with type of MenuController.
  • Step 3: Create a Group item for encapsulate the three Button controls that will appear in the menu controller.
  • Step 4: Attach the button items to the Group created in the third step.

Listing 12 summarizes the changes to establish the menu controller.

Listing 12: Using a menu controller

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <CommandTable xmlns="...">
  3.   <!-- Extern section unchanged -->
  4.   <Commands package="guidAdvancedVSCTSamplePkg">
  5.     <Menus>
  6.       <!-- New item representing the menu controller -->
  7.       <Menu guid="guidAdvancedVSCTSampleCmdSet" id="VSCTMenuController"
  8.       priority="0x0200" type="MenuController">
  9.         <Parent guid="guidAdvancedVSCTSampleCmdSet" id="VSCTToolbarGroup"/>
  10.         <CommandFlag>IconAndText</CommandFlag>
  11.         <Strings>
  12.           <ButtonText>VSCT Menu Controller</ButtonText>
  13.           <CommandName>Menu Controller</CommandName>
  14.         </Strings>
  15.       </Menu>
  16.     </Menus>
  17.  
  18.     <Groups>
  19.       <!-- New group for the items in the menu controller -->
  20.       <Group guid="guidAdvancedVSCTSampleCmdSet" id="VSCTControllerGroup"
  21.         priority="0x0600">
  22.         <Parent guid="guidAdvancedVSCTSampleCmdSet" id="VSCTMenuController"/>
  23.       </Group>
  24.     </Groups>
  25.  
  26.     <Buttons>
  27.       <!-- We leave the first button as it is -->
  28.       <!-- We change the 2nd 3rd and 4th button as indicated -->
  29.       <Button guid="guidHowToPackageCmdSet" id="cmdSecondCommand"
  30.         priority="0x0101" type="Button">
  31.         <Parent guid="guidAdvancedVSCTSampleCmdSet" id="VSCTControllerGroup" />
  32.         <CommandFlag>IconAndText</CommandFlag>
  33.         <Icon guid="guidImages" id="bmpPic2" />
  34.         <Strings>
  35.           <CommandName>cmdidSecondCommand</CommandName>
  36.           <ButtonText>Second Command</ButtonText>
  37.         </Strings>
  38.       </Button>
  39.       <!-- Code for the 3rd and 4th button is similar -->
  40.     </Buttons>
  41.     <!-- Bitmaps section unchanged -->
  42.   </Commands>
  43.  
  44.   <Symbols>
  45.     <GuidSymbol name="guidHowToPackageCmdSet" value="..." >
  46.       <!-- We use these two new IDs -->
  47.       <IDSymbol name="VSCTMenuController" value="0x0101" />
  48.       <IDSymbol name="VSCTControllerGroup" value="0x0201" />
  49.     </GuidSymbol>
  50.     <!-- Other Guids of the package -->
  51.   </Symbols>
  52. </CommandTable>

The Menu item for a menu controller can be defined so that the type attribute is set to MenuController. The Parent of the menu is the command group representing the four command buttons as it was used in the previous example.

You attach the newly created group to the Menu item above and then connect the last three Button elements to this Group. You also change the CommandFlag elements for the new menu controller item and for the tree involved button to IconAndText in order to make it dropdown-menu-like.

IconAndText is the default value for the buttons attached to a menu controller, so if you leave them for buttons, the toolbar appearance will not change. However, for the menu controller itself the default mode displays only the icons. If you had omitted the IconAndText flag from the controller definition we could see the toolbar like in Figure 17.

f0317

Figure 17: Menu controller with icon only

Combo Boxes

Combo boxes provide you an enhanced form of input by allowing a combination of hand-typed text selection with picking items from a list. Visual Studio also allows combo boxes on the user interface. The command table file (.vsct) has Combo elements to represent UI of commands with combo boxes instead of standard menu items or buttons. There are four stereotypes of combos; each of them is identified by a type name used in the .vsct file.

  • DropDownCombo: This type does not let the user type into the combo box; they can only pick an item from a list. After selecting an item the string value of the selected element is returned.
  • IndexCombo: This type is the same as a DropDownCombo in that it allows only picking up items from a list. However, an IndexCombo returns the zero-based index of the selected value on the list and not the value itself.
  • MRUCombo: This combo type allows the user to type into the edit box. The history of strings entered is automatically persisted by the IDE on a per-user or per-machine basis for the last 16 items.
  • DynamicCombo: This combo allows the user to type into the edit box or pick from the list. The list of choices is managed dynamically by a command event handler method.

Handling combo boxes is more complicated than working with simple menu items. A part of the complexity comes from the fact that combo boxes use two commands behind. The first command is executed as you select an item from the dropdown list. The second command is used to fill up the list of items.

Working with Visibility Contexts

In the beginning of this chapter you have already learnt about the concept of visibility contexts, and in Table 4 you can see the most frequently used ones. In this section you are going to create a small sample package demonstrating concepts treated there.

You can examine a package named VisibilityContextSample and similarly to the previous samples this package is only a container for the menu and does not provide any real function. The package adds a menu that looks like as shown in Figure 18.

f0318

Figure 18: Menus defined by the VisibilityContextSample package

The command table producing this menu can be found in Listing 13 and uses exactly the same approach that can be found in Listing 4.

Listing 13: VisibilityContextSample initial command table

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  3.   <Extern href="stdidcmd.h"/>
  4.   <Extern href="vsshlids.h"/>
  5.   <Extern href="msobtnid.h"/>
  6.   <Commands package="guidVisibilityContextSamplePkg">
  7.     <Menus>
  8.       <Menu guid="guidVisibilityContextSampleCmdSet" id="TopLevelMenu" priority="0x100"
  9.         type="Menu">
  10.         <Parent guid="guidSHLMainMenu" id="IDG_VS_MM_BUILDDEBUGRUN" />
  11.         <Strings>
  12.           <ButtonText>Visibility Sample</ButtonText>
  13.           <CommandName>Visibility Sample</CommandName>
  14.         </Strings>
  15.       </Menu>
  16.     </Menus>
  17.  
  18.     <Groups>
  19.       <Group guid="guidVisibilityContextSampleCmdSet" id="TopLevelMenuGroup"
  20.         priority="0x0600">
  21.         <Parent guid="guidVisibilityContextSampleCmdSet" id="TopLevelMenu"/>
  22.       </Group>
  23.     </Groups>
  24.  
  25.     <Buttons>
  26.       <Button guid="guidVisibilityContextSampleCmdSet" id="NoSolution" priority="0x0100"
  27.         type="Button">
  28.         <Parent guid="guidVisibilityContextSampleCmdSet" id="TopLevelMenuGroup" />
  29.         <Strings>
  30.           <CommandName>cmdNoSolution</CommandName>
  31.           <ButtonText>No Open Solution</ButtonText>
  32.         </Strings>
  33.       </Button>
  34.  
  35.       <Button guid="guidVisibilityContextSampleCmdSet" id="EmptySolution"
  36.         priority="0x0101" type="Button">
  37.         <Parent guid="guidVisibilityContextSampleCmdSet" id="TopLevelMenuGroup" />
  38.         <Strings>
  39.           <CommandName>cmdEmptySolution</CommandName>
  40.           <ButtonText>Empty Solution</ButtonText>
  41.         </Strings>
  42.       </Button>
  43.  
  44.       <Button guid="guidVisibilityContextSampleCmdSet" id="SingleProject"
  45.         priority="0x0102" type="Button">
  46.         <Parent guid="guidVisibilityContextSampleCmdSet" id="TopLevelMenuGroup" />
  47.         <Strings>
  48.           <CommandName>cmdSingleProject</CommandName>
  49.           <ButtonText>Single Project</ButtonText>
  50.         </Strings>
  51.       </Button>
  52.  
  53.       <Button guid="guidVisibilityContextSampleCmdSet" id="MultipleProject"
  54.         priority="0x0103" type="Button">
  55.         <Parent guid="guidVisibilityContextSampleCmdSet" id="TopLevelMenuGroup" />
  56.         <Strings>
  57.           <CommandName>cmdMultipleProject</CommandName>
  58.           <ButtonText>Multiple Project</ButtonText>
  59.         </Strings>
  60.       </Button>
  61.  
  62.       <Button guid="guidVisibilityContextSampleCmdSet" id="NotBound" priority="0x0104"
  63.         type="Button">
  64.         <Parent guid="guidVisibilityContextSampleCmdSet" id="TopLevelMenuGroup" />
  65.         <Strings>
  66.           <CommandName>cmdNotBound</CommandName>
  67.           <ButtonText>Not bound to context</ButtonText>
  68.         </Strings>
  69.       </Button>
  70.  
  71.       <Button guid="guidVisibilityContextSampleCmdSet" id="SingleOrMultiple"
  72.         priority="0x0105" type="Button">
  73.         <Parent guid="guidVisibilityContextSampleCmdSet" id="TopLevelMenuGroup" />
  74.         <Strings>
  75.           <CommandName>cmdSingleOrMultiple</CommandName>
  76.           <ButtonText>Single or Multiple</ButtonText>
  77.         </Strings>
  78.       </Button>
  79.     </Buttons>
  80.  
  81.   </Commands>
  82.  
  83.   <Symbols>
  84.     <GuidSymbol name="guidVisibilityContextSamplePkg" value="..." />
  85.     <GuidSymbol name="guidVisibilityContextSampleCmdSet" value="...">
  86.       <IDSymbol name="TopLevelMenu" value="0x1000" />
  87.       <IDSymbol name="TopLevelMenuGroup" value="0x1020" />
  88.       <IDSymbol name="NoSolution" value="0x0100" />
  89.       <IDSymbol name="EmptySolution" value="0x0101" />
  90.       <IDSymbol name="SingleProject" value="0x0102" />
  91.       <IDSymbol name="MultipleProject" value="0x0103" />
  92.       <IDSymbol name="NotBound" value="0x0104" />
  93.       <IDSymbol name="SingleOrMultiple" value="0x0105" />
  94.     </GuidSymbol>
  95.   </Symbols>
  96.  
  97. </CommandTable>

If you apply this .vsct file for a package you will get the menu in the Figure 18, but nothing extra. Bind the commands to specific visibility contexts! First, add DynamicVisibility command flag to all buttons. Without this flag the sample would not work. Second, add the VisibilityConstraints section as in Listing 14 to the command table.

Listing 14: VisibilityContextSample initial command table

  1. <VisibilityConstraints>
  2.   <VisibilityItem guid="guidVisibilityContextSampleCmdSet" id="NoSolution"
  3.     context="UICONTEXT_NoSolution"/>
  4.   <VisibilityItem guid="guidVisibilityContextSampleCmdSet" id="EmptySolution"
  5.     context="UICONTEXT_EmptySolution"/>
  6.   <VisibilityItem guid="guidVisibilityContextSampleCmdSet" id="SingleProject"
  7.     context="UICONTEXT_SolutionHasSingleProject"/>
  8.   <VisibilityItem guid="guidVisibilityContextSampleCmdSet" id="MultipleProject"
  9.     context="UICONTEXT_SolutionHasMultipleProjects"/>
  10.   <VisibilityItem guid="guidVisibilityContextSampleCmdSet" id="SingleOrMultiple"
  11.     context="UICONTEXT_SolutionHasSingleProject"/>
  12.   <VisibilityItem guid="guidVisibilityContextSampleCmdSet" id="SingleOrMultiple"
  13.     context="UICONTEXT_SolutionHasMultipleProjects"/>
  14. </VisibilityConstraints>

Each VisibilityItem node binds the specified command to a visibility context defined by the context attribute. Symbols starting with the UICONTEXT_ prefix define the contexts as summarized in Table 3-4. The NotBound button is not assigned with any contexts, so it is always visible (it is displayed in every context). The SingleOrMultiple command is bound to two contexts (represented by the last two VisibilityItem nodes), so it is displayed when there is a solution with one or more projects open.

We can check in a few steps how these constraints work. First, when you open Visual Studio you can see the initial state of the menu as shown in Figure 19.

f0319

Figure 19: Initial menu state after starting Visual Studio

Now, carry out the following steps and see the results in the corresponding figures:

  • Create an empty solution (Figure 20)
  • Add a C# class library (or any other kind of project) to the solution (Figure 21)
  • Add another project to the existing solution (Figure 22)

f0320

Figure 20: Empty solution created

f0321

Figure 21: A new project is added to the solution

 

f0322

Figure 22: Another project is added to the solution

As you see, visibility contexts work as expected.

Command Routing and Nested Contexts

The Visual Studio IDE, its registered packages, and objects owned by packages (editors, tool windows, and so on) define a huge set of executable commands. A command can be executed by a number of entities depending on the current context, for instance by the active editor window, the currently selected project, active tool window, and so on.

There are many objects at the same time in the IDE which are command targets and so are able to receive and execute commands. Visual Studio has a well-defined routing architecture that lays down the rules how commands are executed in a certain context — in which order command targets are offered to process commands. This algorithm starts form the innermost context and goes to the outermost context while it reaches the global context that is the core Visual Studio IDE.

From this point of view contexts are nested into each other. Here is a very simple example how contexts are nested. The real life is a bit more complex, but this helps you imagine nesting.

Assume you create a package with a tool window and register it with Visual Studio IDE. Because both the package itself and the tool window owned by the package are command targets, you have the following contexts from the top to the bottom:

  • The top level (global context) is Visual Studio IDE.
  • Your package is sited in the IDE. Before siting it has its own logical context. After you site the package its context logically will be a context nested into the IDE.
  • The tool window of the package has its own contexts. When an instance is created its context becomes a nested context within the package.

Visual Studio IDE, packages and package-owned objects form a tree of nested command contexts. From this perspective the routing algorithm starts from a leaf of that tree and bubbles up while it reaches the root of this tree that is called the global context.

The Routing Algorithm

The algorithm that routes commands has many fine details which add small twists. Here you will not learn all those subtle details; instead you’ll be given a high level overview.

In the route the current level is called the active command context. This context has the chance to handle the command or to say “I do not know what to do with this command” and the bubble goes on its way.

The routing algorithm defines the following levels from the leaves to the root:

  • Present Add-ins and special packages. Commands first are offered to the registered and loaded Add-ins or specially registered packages.
  • Context (shortcut) menus. If the user initiates a command from a context menu, the command target object belonging to this menu has the first chance to handle the command. If it does not, then the normal route (starting from Present Add-Ins) is applied.
  • Focus window. The window having the focus is the next entity that can undertake command handling. This can be either a tool window or a document window, for instance, a window related to an editor. The management of the command is different depending on what kind of window is focused.
    • Document window. There are windows in Visual Studio like editors and designers that have physical or virtual documents behind them. Document windows are composed logically from two separate parts: a document view that is responsible to display the UI representing the document and a document data object that is responsible to handle the information set behind the document. Both the document view and the document data can be command targets. The command first goes to the document view and goes on to the document data if the view does not support the command.
    • Tool window. The tool window can handle the command by its own logic. There are tool windows that route the commands within themselves to nested command targets. The Solution Explorer window is an example of them. Within Solution Explorer a command is routed according to the hierarchy composed from the elements of the Solution Explorer where each node type (file, folder, project, solution, etc.) has the ability to handle the command. This internal route also goes from the lower hierarchy levels to the upper ones.
  • Current project. The current project gets the opportunity to process the command. If it does not handle, the command goes up in the hierarchy of projects till the level of solution. All nodes on this route can manage the command just like other command target objects. (Visual Studio allows creating nested projects. When subprojects are used, the upper level of a project is not necessary the solution.)
  • Environment. Each package should be able to handle commands owned (defined) by it. Theoretically it is not mandatory, but why to define a custom command which is not handled by the only entity that knows it?
  • Global level. If a command is not handled in the previous levels, the environment attempts to route it to the appropriate package (the package defining the command). If necessary, Visual Studio loads the appropriate package into the memory.

Any command target objects along the route can decide how to process a command. They must answer status queries and execute requested commands.

Summary

Visual Studio clearly separates the concept of menu items and commands.

A command is responsible for determining its state (name, visibility, enabled, disabled, etc.) and executing the command triggered. A menu item is responsible for presenting the visual properties of a command and providing a way the user can trigger the execution of the command. A command object can be bound to zero, one or more menu items.

A command itself it a logical entity that can be forwarded to command targets which know how to handle the semantics of a specific command. There is a command routing model in the IDE that forwards a command to a command target. The target either can do something with the request related to a command (for example, set the command state disabled, execute the command, etc.) or can pass back the command as not supported (the target does not know what to do with that). The target even can pass the command to other command targets.

Each command has a state in the current Visual Studio context. This state is built up from two orthogonal factors: availability (the command is enabled or disabled) and visuals (the command is visible or hidden, the label it has, and so on).

The Visual Studio Command Table describes the commands and related UI elements that should be merged with Visual Studio menus. The command table is compiled to a binary resource which is embedded into the package infrastructure resources during the build process. When deploying the package the command table is merged into the menus and toolbars of the IDE.

Visual Studio 2010 uses an XML format with .vsct extension to describe the command table. Older versions used a simple textual format called .ctc that has been deprecated.

In this chapter you have seen the structure of the .vsct file and solved simple tasks like building a new main menu item or a toolbar, putting commands visually into command groups or submenus, working with menu controllers, etc. You have also examined a sample illustrating how visibility constraints work.


Posted May 23 2010, 03:54 PM by inovak
Filed under: ,

Comments

sujit wrote re: VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars
on Thu, May 27 2010 7:43

Thanks,Nice and Helpful

Hariharan wrote re: VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars
on Mon, Jun 28 2010 19:21

Really good article !!!!! Thanks a LOT

Chao wrote re: VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars
on Tue, Jul 27 2010 8:07

Really really really a excellent article! Could we expect for next chapter:"Chapter 3: Window Management and Tool Windows"? I really can not wait to see it.

inovak wrote re: VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars
on Tue, Jul 27 2010 8:13

Hey Chao,

Right now it seems that I'll be able to publish the book either with the help of Microsoft or without it...

DiveDeeper

Chao wrote re: VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars
on Tue, Jul 27 2010 11:22

Thanks for the quick respone and really look forward for your book. So you will not post Chapter3 here, right? Could I ask about when are your current plan time for publish the book? Thanks.

GeoVah wrote re: VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars
on Thu, Aug 19 2010 2:15

Do you have any sample regarding localization ?

The example is missing within MSDN (msdn.microsoft.com/.../ee943168.aspx) and I cannot find any sample other the web.

CKSDev Adding keyboard shortcuts wrote CKSDev Adding keyboard shortcuts
on Sun, Jan 9 2011 20:00

Pingback from  CKSDev Adding keyboard shortcuts

Karthik Reddy Chintaparthi wrote re: VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars
on Mon, Jun 4 2012 14:51

Hi, I have a question on multi template VS Package. I have added 4 C# project templates of which two of them are one solution type & the other two are different solution type. I am unable to see two templates when I run the solution in expermental instance. Could you please let me know how to control the order ?

buy stendra online wrote re: VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars
on Sun, Feb 24 2013 13:11

dscFAK Major thanks for the post.Really looking forward to read more. Want more.

clomiphene 25 mg wrote re: VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars
on Mon, Feb 25 2013 16:13

Hey, thanks for the article.Thanks Again.

http://cmystat.com/ wrote re: VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars
on Wed, Feb 27 2013 23:20

Very informative article post. Really Cool.

How to create Visual Studio add-ins with VSPackages | ApexSQL Blog wrote How to create Visual Studio add-ins with VSPackages | ApexSQL Blog
on Fri, Aug 15 2014 10:50

Pingback from  How to create Visual Studio add-ins with VSPackages | ApexSQL Blog

FIFA 15 Ultimate Team Hack Tool wrote FIFA 15 Ultimate Team Hack Tool
on Wed, Oct 15 2014 2:42

VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars - DiveDeeper's blog - Dotneteers.net

Subway Surfers Cheats wrote Subway Surfers Cheats
on Wed, Oct 15 2014 19:30

VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars - DiveDeeper's blog - Dotneteers.net

AVABEL Online Hack Tool wrote AVABEL Online Hack Tool
on Thu, Oct 16 2014 14:28

VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars - DiveDeeper's blog - Dotneteers.net

Plunder Pirates Hack Tool wrote Plunder Pirates Hack Tool
on Sun, Oct 19 2014 0:55

VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars - DiveDeeper's blog - Dotneteers.net

Pac-Man Friends Hack Tool wrote Pac-Man Friends Hack Tool
on Mon, Oct 20 2014 16:33

VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars - DiveDeeper's blog - Dotneteers.net

Running Shadow Hack wrote Running Shadow Hack
on Tue, Oct 21 2014 20:22

VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars - DiveDeeper's blog - Dotneteers.net

Battle Beach Hack Tool wrote Battle Beach Hack Tool
on Sat, Oct 25 2014 9:15

VS 2010 Package Development – Chapter 2: Commands, Menus and Toolbars - DiveDeeper's blog - Dotneteers.net