In Part #18 I gave you a very short overview about combo boxes in the context of VSCT files. In that article I promised you to give a detailed overview about declaring and using combo boxes. Nowadays beside LearnVSXNow I’m working on my other project called VSXtra, and I have just finished the first implementation of a concept about working with combo boxes in a new way.
In this article I treat in details how to use combo boxes with Visual Studio Packages and also would like to present you an improved way with VSXtra.
The Visual Studio SDK comes with a sample called “C# Combobox Reference Sample”. I use this sample, because it gives you a great overview about how to use different stereotypes of combo boxes with menu and toolbar definitions. So, if you want to try how combos work, please look after this VS SDK sample and open it with Visual Studio.
Combo box stereotypes
Combo boxes provide you an enhanced form of input by allowing you a combination of hand-typed text selection with picking items from a list. Visual Studio also provides comboboxes on its user interface. The command table file (.vsct) has Combo elements to represent UI of commands with combo boxes instead of 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.
Analyzing the .VSCT file
Open the VS SDK “C# Combo box Reference” sample solution in Visual Studio and have a look at the PkgCmd.vsct file. This file contains the definition of the Combo Box Sample toolbar.

In this part I show you what the structure of the .vsct file is and how the different combo box stereotypes are defined. Here is the full code of the .vsct file (I have omitted the comments):
<?xml version="1.0" encoding="utf-8"?>
<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<Extern href="stdidcmd.h" mce_href="stdidcmd.h"/>
<Extern href="vsshlids.h" mce_href="vsshlids.h"/>
<Extern href="msobtnid.h" mce_href="msobtnid.h"/>
<Commands package="guidComboBoxPkg">
<Menus>
<Menu guid="guidComboBoxCmdSet" id="MyToolbar" priority="0x0000" type="Toolbar">
<Parent guid="guidComboBoxCmdSet" id="0"/>
<Strings>
<ButtonText>ComboBoxSample</ButtonText>
<CommandName>Combo Box Sample</CommandName>
</Strings>
</Menu>
</Menus>
<Groups>
<Group guid="guidComboBoxCmdSet" id="MyToolbarGroup" priority="0xFF45">
<Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
</Group>
</Groups>
<Combos>
<Combo guid="guidComboBoxCmdSet" id="cmdidMyDropDownCombo" priority="0x0010" type="DropDownCombo"
defaultWidth="130" idCommandList="cmdidMyDropDownComboGetList">
<Parent guid="guidComboBoxCmdSet" id="MyToolbarGroup"/>
<CommandFlag>IconAndText</CommandFlag>
<CommandFlag>CommandWellOnly</CommandFlag>
<Strings>
<MenuText>DropDown Combo: </MenuText>
<ButtonText>DropDown Combo</ButtonText>
<ToolTipText>Select String</ToolTipText>
<CanonicalName>DropDown Combo</CanonicalName>
<CommandName>DropDown Combo</CommandName>
</Strings>
</Combo>
<Combo guid="guidComboBoxCmdSet" id="cmdidMyIndexCombo" priority="0x0010" type="IndexCombo"
defaultWidth="100" idCommandList="cmdidMyIndexComboGetList">
<Parent guid="guidComboBoxCmdSet" id="MyToolbarGroup"/>
<CommandFlag>IconAndText</CommandFlag>
<CommandFlag>CommandWellOnly</CommandFlag>
<Strings>
<ButtonText>Index: </ButtonText>
<MenuText>Index Combo: </MenuText>
<ToolTipText>Select Choice</ToolTipText>
<CommandName>Index Combo</CommandName>
<CanonicalName>Index Combo</CanonicalName>
</Strings>
</Combo>
<Combo guid="guidComboBoxCmdSet" id="cmdidMyMRUCombo" priority="0x0040" type="MRUCombo"
defaultWidth="300" idCommandList="0">
<Parent guid="guidComboBoxCmdSet" id="MyToolbarGroup"/>
<CommandFlag>IconAndText</CommandFlag>
<CommandFlag>CommandWellOnly</CommandFlag>
<CommandFlag>NoAutoComplete</CommandFlag>
<CommandFlag>CaseSensitive</CommandFlag>
<Strings>
<ButtonText>MRU: </ButtonText>
<MenuText>MRU Combo: </MenuText>
<ToolTipText>Enter String</ToolTipText>
<CommandName>MRU Combo</CommandName>
<CanonicalName>MRU Combo</CanonicalName>
</Strings>
</Combo>
<Combo guid="guidComboBoxCmdSet" id="cmdidMyDynamicCombo" priority="0x0050" type="DynamicCombo"
defaultWidth="135" idCommandList="cmdidMyDynamicComboGetList">
<Parent guid="guidComboBoxCmdSet" id="MyToolbarGroup"/>
<CommandFlag>IconAndText</CommandFlag>
<CommandFlag>CommandWellOnly</CommandFlag>
<Strings>
<ButtonText>Dynamic: </ButtonText>
<MenuText>Dynamic Combo: </MenuText>
<ToolTipText>Enter Zoom Level</ToolTipText>
<CommandName>Dynamic Combo</CommandName>
<CanonicalName>Dynamic Combo</CanonicalName>
</Strings>
</Combo>
</Combos>
</Commands>
<CommandPlacements>
<CommandPlacement guid="guidComboBoxCmdSet" id="MyToolbarGroup" priority="0x0100">
<Parent guid="guidComboBoxCmdSet" id="MyToolbar"/>
</CommandPlacement>
</CommandPlacements>
<Symbols>
<GuidSymbol name="guidComboBoxPkg" value="{40d9f297-25fb-4264-99ed-7785f8331c94}" />
<GuidSymbol name="guidComboBoxCmdSet" value="{04151d39-35b6-4e53-beee-48df3bb8cfe5}">
<IDSymbol name="MyToolbar" value="0x1000"/>
<IDSymbol name="MyToolbarGroup" value="0x1030"/>
<IDSymbol name="cmdidMyDropDownCombo" value="0x101"/>
<IDSymbol name="cmdidMyDropDownComboGetList" value="0x102"/>
<IDSymbol name="cmdidMyIndexCombo" value="0x103"/>
<IDSymbol name="cmdidMyIndexComboGetList" value="0x104"/>
<IDSymbol name="cmdidMyMRUCombo" value="0x105"/>
<IDSymbol name="cmdidMyDynamicCombo" value="0x107"/>
<IDSymbol name="cmdidMyDynamicComboGetList" value="0x108"/>
</GuidSymbol>
</Symbols>
</CommandTable>
Now, let’s see the details of the file. I assume that you are familiar with the basic concepts of the .vsct file. If not, please read the LearnVSXNow! #14 - Basics of the .vsct file article.
Placing the combo box controls onto the toolbar
The Menus element declares only one Menu item representing our toolbar:
<Menus>
<Menu guid="guidComboBoxCmdSet" id="MyToolbar" priority="0x0000" type="Toolbar">
<Parent guid="guidComboBoxCmdSet" id="0"/>
<Strings>
<ButtonText>ComboBoxSample</ButtonText>
<CommandName>Combo Box Sample</CommandName>
</Strings>
</Menu>
</Menus>
The value “Toolbar” in the type attribute signs that we are declaring a new toolbar. Because the Parent child element under Menu is mandatory we must use it. However, a toolbar does not have a real parent, so as a convention the same guid as for the Menu and the id of 0 is used here.
In the example we user four combo boxes that are logically organized into one group defined by a new Group element:
<Groups>
<Group guid="guidComboBoxCmdSet" id="MyToolbarGroup" priority="0xFF45">
<Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
</Group>
</Groups>
This Group uses actually the Tools menu as its parent! How this group is associated with the toolbar? A CommandPlacement definition use used to build up this assignment:
<CommandPlacements>
<CommandPlacement guid="guidComboBoxCmdSet" id="MyToolbarGroup" priority="0x0100">
<Parent guid="guidComboBoxCmdSet" id="MyToolbar"/>
</CommandPlacement>
</CommandPlacements>
But wait! Actually we added the Group holding combo box definitions both to the Tools menu and to the toolbar frame! Instead, we could have created a simple Group definition like this:
<Groups>
<Group guid="guidComboBoxCmdSet" id="MyToolbarGroup" priority="0xFF45">
<Parent guid="guidComboBoxCmdSet" id="MyToolbar"/>
</Group>
</Groups>
You are right, this simple definition works! The reason why the sample uses the pattern above is customization. By adding any command to the main menu allows command and toolbar customization (the user can organize the commands among menus and toolbars).
Although the combo boxes are added to the main menu with the Parent element of the Group definition and to the toolbar with the CommandPlacement declaration, they are invisible in the main menu due to the CommandWellOnly flag associated with each combo. This command flag instructs the Visual Studio Shell to hide the corresponding items from the main menu but enable their customization.
To see what CommandWellOnly flag does, just comment out one of them. The combo without this flag will be displayed in the main menu as you see in the next figure:

Combo box declarations
The .vsct file contains Combos node to declare combo boxes in its Combo child nodes. Combo declarations are very similar to Button declarations, but those use more attributes and child elements to define combo appearance and behavior. The following extract contains the declaration for a DropDownCombo:
<Combo guid="guidComboBoxCmdSet" id="cmdidMyDropDownCombo"
priority="0x0010"
type="DropDownCombo"
defaultWidth="130"
idCommandList="cmdidMyDropDownComboGetList">
<Parent guid="guidComboBoxCmdSet" id="MyToolbarGroup"/>
<CommandFlag>IconAndText</CommandFlag>
<CommandFlag>CommandWellOnly</CommandFlag>
<Strings>
<MenuText>DropDown Combo: </MenuText>
<ButtonText>DropDown Combo</ButtonText>
<ToolTipText>Select String</ToolTipText>
<CanonicalName>DropDown Combo</CanonicalName>
<CommandName>DropDown Combo</CommandName>
</Strings>
</Combo>
Just as for Button elements, guid and id attributes identify the command represented by the combo. The type attribute names the stereotype of the combo box with the values of DropDownCombo, IndexCombo, MRUCombo and DynamicCombo. Except of MRUCombo the other combo box types have lists filled up by package owning the combo box. It is done so that these three types of combos actually have two commands:
The “main” command represented by the guid and id attributes is called when the shell is about to get the current selection of the combo or set it to a value.
There is a secondary command represented by the guid and idCommandList attributes. This is called when the shell is about to fill up the list belonging to the combo box. Actually, any time when you drop down the list of a combo this command is used.
Unlike buttons, combos can have a suggested width set by the defaultWidth attribute. It is specified in pixels and includes the width of the entire combo box including the text area, icon and label.
You can see in the .vsct file that two CommandFlag elements are used for each combo. The IconAndText flag specifies that both the label and icon for the combo should be displayed. The CommandWellOnly is used to hide the combos from the main menu but to allow user customization.
The Strings element is a container for various strings for the Combo elements. Their semantics is similar to the ones used for buttons.
Using combo boxes in code
In order combo boxes can be used, command handlers should be bound to them. For buttons only a simple command handler is required to respond to the event when the user clicks on the button. However, for combo boxes the situation is a bit more complicated:
The shell must be able to obtain the current value of the combo box and also to set it. So, besides executing an action, the standard command handler is responsible for getting and setting the combo box value. The combo box has to be able to publish the collection of items in the drop-down list part of the combo. This is done by a separate command.
The behavior of combo boxes require special command handler binding and command handler methods have more responsibilities. To understand how these concepts work, let us see some code extracts from the Combo box reference sample.
The command handler binding is done in the overridden Initialize method of the package:
protected override void Initialize()
{
base.Initialize();
OleMenuCommandService mcs = GetService(typeof(IMenuCommandService))
as OleMenuCommandService;
if (null != mcs)
{
// --- Initialize the DropDownCombo
CommandID menuMyDropDownComboCommandID =
new CommandID(GuidList.guidComboBoxCmdSet,
(int)PkgCmdIDList.cmdidMyDropDownCombo);
OleMenuCommand menuMyDropDownComboCommand =
new OleMenuCommand(new EventHandler(OnMenuMyDropDownCombo),
menuMyDropDownComboCommandID);
menuMyDropDownComboCommand.ParametersDescription = "$";
mcs.AddCommand(menuMyDropDownComboCommand);
// --- Initialize the “GetList” command for DropDownCombo
CommandID menuMyDropDownComboGetListCommandID =
new CommandID(GuidList.guidComboBoxCmdSet,
(int)PkgCmdIDList.cmdidMyDropDownComboGetList);
MenuCommand menuMyDropDownComboGetListCommand =
new OleMenuCommand(new EventHandler(OnMenuMyDropDownComboGetList),
menuMyDropDownComboGetListCommandID);
mcs.AddCommand(menuMyDropDownComboGetListCommand);
// --- Initialize the IndexCombo
CommandID menuMyIndexComboCommandID =
new CommandID(GuidList.guidComboBoxCmdSet,
(int)PkgCmdIDList.cmdidMyIndexCombo);
OleMenuCommand menuMyIndexComboCommand =
new OleMenuCommand(new EventHandler(OnMenuMyIndexCombo),
menuMyIndexComboCommandID);
menuMyIndexComboCommand.ParametersDescription = "$";
mcs.AddCommand(menuMyIndexComboCommand);
// --- Initialize the “GetList” command for IndexCombo
CommandID menuMyIndexComboGetListCommandID =
new CommandID(GuidList.guidComboBoxCmdSet,
(int)PkgCmdIDList.cmdidMyIndexComboGetList);
MenuCommand menuMyIndexComboGetListCommand =
new OleMenuCommand(new EventHandler(OnMenuMyIndexComboGetList),
menuMyIndexComboGetListCommandID);
mcs.AddCommand(menuMyIndexComboGetListCommand);
// --- Initialize the MRUCombo
CommandID menuMyMRUComboCommandID =
new CommandID(GuidList.guidComboBoxCmdSet,
(int)PkgCmdIDList.cmdidMyMRUCombo);
OleMenuCommand menuMyMRUComboCommand =
new OleMenuCommand(new EventHandler(OnMenuMyMRUCombo),
menuMyMRUComboCommandID);
menuMyMRUComboCommand.ParametersDescription = "$";
mcs.AddCommand(menuMyMRUComboCommand);
// --- Initialize the DynamicCombo
CommandID menuMyDynamicComboCommandID =
new CommandID(GuidList.guidComboBoxCmdSet,
(int)PkgCmdIDList.cmdidMyDynamicCombo);
OleMenuCommand menuMyDynamicComboCommand =
new OleMenuCommand(new EventHandler(OnMenuMyDynamicCombo),
menuMyDynamicComboCommandID);
menuMyDynamicComboCommand.ParametersDescription = "$";
mcs.AddCommand(menuMyDynamicComboCommand);
// --- Initialize the “GetList” command for IndexCombo
CommandID menuMyDynamicComboGetListCommandID =
new CommandID(GuidList.guidComboBoxCmdSet,
(int)PkgCmdIDList.cmdidMyDynamicComboGetList);
MenuCommand menuMyDynamicComboGetListCommand =
new OleMenuCommand(new EventHandler(OnMenuMyDynamicComboGetList),
menuMyDynamicComboGetListCommandID);
mcs.AddCommand(menuMyDynamicComboGetListCommand);
}
}
As you see from the code, we use the same steps for binding a handler to combo box command as for button commands. First we create a CommandID instance then an OleMenuCommand instance and set it up with the corresponding command identifier and event handler method(s). As the last step, we add the command to the OleMenuService instance we obtained at the beginning of the method. You can follow in the code how we bind two commands for each combo box stereotypes except MRUCombo.
There is one more interesting piece of code: we set the ParametersDescription property of the OleMenuCommand instances to “$”. This looks a bit esoteric, due to the fact that ParametersDescription is not a well-documented property.
Here I do not explain you all the details, but try to give you a few hints about what this parameter does. Actually, each command available in Visual Studio can be used from command line through the Command window. When using commands from command line, parameters can be passed to the command. The ParametersDescription property is used to tell the shell what kind of parameters are accepted by a command when starting from the command line. The “$” used in this sample tells the shell that the rest of the command line following the command name is the parameter of that command.
You could leave the ParametersDescription property unset, the example would still work.
Creating the standard command handler
The combo box command handler methods have the same signature as any other standard event handler methods:
void EventHandlerMethod(object sender, EventArgs e);
However, for combo boxes the shell passes an OleMenuCmdEventArgs instance for e. This is important: an OleMenuCmdEventArgs has an InValue property (type of System.Object) and an OutValue property (type of System.IntPtr). When the handler method executes and OutValue is not empty (not null), the shell expects the method to retrieve the current value of the combo box in OutValue. Should InValue be not empty, the shell signs the current value of the combo box should be set according to the string provided by InValue. It is not legal that both InValue and OutValue are empty or both are filled with values.
The standard event handler methods for combo boxes should follow this pattern:
private void OnMyComboExec(object sender, EventArgs e)
{
if (e == EventArgs.Empty)
{
// --- We should never get here; EventArgs are required.
throw (new ArgumentException(Resources.EventArgsRequired));
}
OleMenuCmdEventArgs eventArgs = e as OleMenuCmdEventArgs;
if (eventArgs != null)
{
string newChoice = eventArgs.InValue as string;
IntPtr vOut = eventArgs.OutValue;
if (vOut != IntPtr.Zero && newChoice != null)
{
throw (new ArgumentException(Resources.BothInOutParamsIllegal));
}
else if (vOut != IntPtr.Zero)
{
// --- The IDE is requesting the current value for the combo
Marshal.GetNativeVariantForObject(currentValue, vOut);
}
else if (newChoice != null)
{
// --- New value was selected or typed in, check if it is a valid input
bool validInput = ... // --- the input here
if (validInput)
{
// --- Store the new value
// --- Execute the command action
}
else
{
// --- An invalid input has been specified
throw (new ArgumentException(Resources.ParamNotValidStringInList));
}
}
else
{
// --- We should never get here
throw (new ArgumentException(Resources.InOutParamCantBeNULL));
}
}
else
{
// --- We should never get here; EventArgs are required.
throw (new ArgumentException(Resources.EventArgsRequired));
}
}
The majority of this code deals with parameter checks. When we retrieve the current value of the combo box, we must use the Marshal.GetNativeVariantForObject method to pass back the output value.
When the IDE is about to set the current value, we must check if the value is valid (it can be accepted by our combo). It always depends on the stereotype and semantics of our combo box if we take a value into account as valid or as invalid. The DropDownCombo implements the pattern above in this way:
private string[] dropDownComboChoices = { Resources.Apples, Resources.Oranges,
Resources.Pears, Resources.Bananas };
private string currentDropDownComboChoice = Resources.Apples;
private void OnMenuMyDropDownCombo(object sender, EventArgs e)
{
if (e == EventArgs.Empty)
{
throw (new ArgumentException(Resources.EventArgsRequired)); }
OleMenuCmdEventArgs eventArgs = e as OleMenuCmdEventArgs;
if (eventArgs != null)
{
string newChoice = eventArgs.InValue as string;
IntPtr vOut = eventArgs.OutValue;
if (vOut != IntPtr.Zero && newChoice != null)
{
throw (new ArgumentException(Resources.BothInOutParamsIllegal));
}
else if (vOut != IntPtr.Zero)
{
Marshal.GetNativeVariantForObject(this.currentDropDownComboChoice, vOut);
}
else if (newChoice != null)
{
bool validInput = false;
int indexInput = -1;
for (indexInput = 0; indexInput < dropDownComboChoices.Length; indexInput++)
{
if (String.Compare(dropDownComboChoices[indexInput], newChoice,
StringComparison.CurrentCultureIgnoreCase) == 0)
{
validInput = true;
break;
}
}
if (validInput)
{
this.currentDropDownComboChoice = dropDownComboChoices[indexInput];
ShowMessage(Resources.MyDropDownCombo, this.currentDropDownComboChoice);
}
else
{
throw (new ArgumentException(Resources.ParamNotValidStringInList));
}
}
else
{
throw (new ArgumentException(Resources.InOutParamCantBeNULL));
}
}
else
{
throw (new ArgumentException(Resources.EventArgsRequired));
}
}
We store the available selection values in the dropDownComboChoices array (we look later how the content of this array is passed to the shell). The current selection is stored in the currentDropDownComboChoice string variable. The highlighted code simply checks if the new selection is one of the available values (using case-insensitive comparison). If the new value is acceptable we store it and execute the command.
The IndexCombo uses a bit different approach to set the value, as the following code extract indicates it: