How to: Dynamically Add Menu Items

A command that is defined in the XML-Based Command Table Configuration (.vsct) file and has the DynamicItemStart flag specifies a position on a menu where dynamically created commands are inserted. Dynamic commands are typically created when the associated VSPackage is started and can be updated when the VSPackage is open.

Two common kinds of dynamic lists are as follows:

  • Most Recently Used (MRU) list, which typically displays the names of documents that have been opened recently.

  • Windows list, which typically displays the names of windows that are currently open.

The DynamicItemStart flag on a command definition specifies that the command is a placeholder until the VSPackage is opened. When the VSPackage is opened, the placeholder is replaced with 0 or more commands that are created at run time by the VSPackage and added to the dynamic list. You may not be able to see the position on the menu where the dynamic list appears until the VSPackage is opened.

To populate the dynamic list, the integrated development environment (IDE) first calls the VSPackage to look for a command that has an ID whose first characters are the same as the ID of the placeholder. When the IDE finds a matching command, it adds the name of the command to the dynamic list. Then the IDE increments the ID and looks for another matching command to add to the dynamic list. The IDE continues to increment the ID and add to the dynamic list until there are no more dynamic commands.

The first procedure describes how to create a dynamic MRU list in a submenu.

The second procedure describes how to populate a dynamic list by using the Managed Package Framework (MPF).

For more information about .vsct files, see Visual Studio Command Table (.Vsct) Files.

For information about how to create an MRU list, see Walkthrough: Adding a Most Recently Used List to a Submenu.

Creating a Dynamic Command List

To create a dynamic command list, you must first add a Button element in the .vsct file. This element becomes a placeholder for the list in the IDE. Then implement the list itself in code.

To create a placeholder for a dynamic command list

  1. In the .vsct file, create a submenu that contains a command group. For more information, see How to: Create Menus, SubMenus, and Shortcut Menus.

    This submenu will contain the dynamic list.

  2. In the Symbols section of the .vsct file, locate the GuidSymbol element that contains your other commands. Add an IDSymbol element for the MRUListGroup group and the cmdidMRUList command for the dynamic list placeholder, as follows.

  3. Define a group, as follows.

    <Group guid="guidTopLevelMenuCmdSet" id="MRUListGroup" priority="0x100">
      <Parent guid="guidTopLevelMenuCmdSet" id="SubMenu"/>
    </Group>
    
  4. Define the new command as a Button element in the Buttons section.

    1. Set the guid and id fields to the GUID:ID pair that represents the new element. The GUID:ID pair consists of the name values of the GuidSymbol and IDSymbol elements from the previous step.

      This GUID:ID pair is the first in a list of dynamic commands that are added at run time by the VSPackage. Each subsequent dynamic command has an ID that is one greater than the ID of the previous dynamic command.

    2. Set the type attribute to Button.

      A dynamic command has the type Button just like any other command.

    3. Do not set a priority attribute for the command.

      This submenu contains only one dynamic list. Therefore, the priority of the first item does not matter. Dynamic items that are added later have the same priority; the order in which each is added dictates its relative position.

    4. Add a Parent element that has guid and id attributes that match those of the group that was created for the submenu.

    5. Add a Command Flag element and set its value to DynamicItemStart.

      This marks the command as a placeholder.

      Note

      All other flags are ignored. If you want to control the visibility of a dynamic item, you must do so from the VSPackage at run time.

    6. Add a Strings element that contains a ButtonText element and a CommandName element and set their values to the name of the placeholder.

      Typically, this button text is not seen if the VSPackage can initialize the dynamic list before the menu that contains it is shown.

    7. Do not specify an Icon element.

      A dynamic command cannot have an icon associated with it.

    The following example shows a completed command definition for an MRU list placeholder.

    <Button guid="guidTopLevelMenuCmdSet" id="cmdidMRUList"
            type="Button" priority="0x0100">
      <Parent guid="guidTopLevelMenuCmdSet" id="MRUListGroup" />
      <CommandFlag>DynamicItemStart</CommandFlag>
      <Strings>
        <CommandName>cmdidMRUList</CommandName>
        <ButtonText>MRU Placeholder</ButtonText>
      </Strings>
    </Button>
    

Implementing the Dynamic List

The Managed Package Framework (MPF) uses event handlers to hide the details of the IOleCommandTarget interface and its QueryStatus method. For a VSPackage that is written by using the MPF to support a dynamic list of menu commands, create a list of OleMenuCommand objects, each of which represents an item in the dynamic list. Every OleMenuCommand object has the same event handler for executing the command and the same event handler for getting the status of the command.

Note

The following procedure uses code from Walkthrough: Adding a Most Recently Used List to a Submenu to illustrate how to implement a dynamic list. However, the code does not create a complete VSPackage. For a complete sample, see the walkthrough.

To implement a dynamic list

  1. Add the ID to the list of IDs in PkgCmdID.cs.

    public const uint cmdidMRUList = 0x200;
    
  2. In TopLevelMenuPackage.cs add this using statement.

    using System.Collections;
    
  3. Bind the placeholder to a OleMenuCommand instance and assign its event handlers. The method in the following example is called from the Initialize() method of the package.

    private void InitMRUMenu(OleMenuCommandService mcs)
    {
        InitializeMRUList();
        for (int i = 0; i < this.numMRUItems; i++)
        {
            var cmdID = new CommandID(
                GuidList.guidTopLevelMenuCmdSet, this.baseMRUID + i);
            var mc = new OleMenuCommand(
                new EventHandler(OnMRUExec), cmdID);
            mc.BeforeQueryStatus += new EventHandler(OnMRUQueryStatus);
            mcs.AddCommand(mc);
        }
    }
    

    The examples in this procedure assume that the following private variables are used.

    private int numMRUItems = 4;
    private int baseMRUID = (int)PkgCmdIDList.cmdidMRUList;
    private ArrayList mruList;
    
  4. Use the QueryStatus event handler to populate the dynamic list, as shown in the following example.

    private void OnMRUQueryStatus(object sender, EventArgs e)
    {
        OleMenuCommand menuCommand = sender as OleMenuCommand;
        if (null != menuCommand)
        {
            int MRUItemIndex = menuCommand.CommandID.ID - this.baseMRUID;
            if (MRUItemIndex >= 0 && MRUItemIndex < this.mruList.Count)
            {
                menuCommand.Text = this.mruList[MRUItemIndex] as string;
            }
        }
    }
    
  5. Implement the Exec handler to respond when an item on the list is clicked. The following example updates the position of list items so that the most recent appears at the top.

    private void OnMRUExec(object sender, EventArgs e)
    {
        var menuCommand = sender as OleMenuCommand;
        if (null != menuCommand)
        {
            int MRUItemIndex = menuCommand.CommandID.ID - this.baseMRUID;
            if (MRUItemIndex >= 0 && MRUItemIndex < this.mruList.Count)
            {
                string selection = this.mruList[MRUItemIndex] as string;
                for (int i = MRUItemIndex; i > 0; i--)
                {
                    this.mruList[i] = this.mruList[i - 1];
                }
                this.mruList[0] = selection;
                System.Windows.Forms.MessageBox.Show(
                    string.Format(CultureInfo.CurrentCulture,
                                  "Selected {0}", selection));
            }
        }
    }
    

    You could also use the Text property or the CommandID property to identify which item is selected and respond accordingly.

  6. Control the visibility of the menu or individual menu items by setting the Visible property. The following code renders a menu or menu item invisible.

    var menuCommand = sender as OleMenuCommand;
    menuCommand.Visible = false;
    

    Putting the preceding code in the QueryStatus handler applies it to the entire menu because it provides the sender argument to that handler. However, putting it in the Exec handler affects just the individual menu item that is being executed.

See Also

Concepts

How VSPackages Add User Interface Elements to the IDE

Other Resources

Commands, Menus, and Toolbars

Common Tasks with Commands, Menus, and Toolbars