Walkthrough: Adding a Most Recently Used List to a Submenu

This walkthrough builds on the demonstrations in Walkthrough: Adding a Submenu to a Menu, and shows how to add a dynamic list to a submenu. The dynamic list forms the basis for creating a Most Recently Used (MRU) list.

A dynamic menu list starts with a placeholder on a menu. Every time the menu is shown, the Visual Studio integrated development environment (IDE) asks the VSPackage for all commands that should be shown at the placeholder. A dynamic list can occur anywhere on a menu. However, dynamic lists are typically stored and displayed by themselves on submenus or at the bottoms of menus. By using these design patterns, you enable the dynamic list of commands to expand and contract without affecting the position of other commands on the menu. In this walkthrough, the dynamic MRU list is displayed at the bottom of an existing submenu, separated from the rest of the submenu by a line.

Technically, a dynamic list can also be applied to a toolbar. However, we discourage that usage because a toolbar should remain unchanged unless the user takes specific steps to change it.

This walkthrough creates an MRU list of four items that change their order every time that one of them is selected (the selected item moves to the top of the list).

For more information about menus and .vsct files, see Commands, Menus, and Toolbars.

Prerequisites

To follow this walkthrough, you must install the Visual Studio 2013 SDK. For more information, see Visual Studio Software Development Kit (SDK).

Locations for the Visual Studio Package Project Template

The Visual Studio Package project template is available in three locations in the New Project dialog box:

  • Under Visual Basic Extensibility. The default language of the project is Visual Basic.

  • Under C# Extensibility. The default language of the project is C#.

  • Under Other Project Types Extensibility. The default language of the project is C++.

Creating a VSPackage

To create the TopLevelMenu VSPackage

The procedures in this walkthrough assume that the name of the VSPackage is TopLevelMenu, which is the name that is used in Walkthrough: Adding a Menu to the Visual Studio Menu Bar (C#).

Creating a Dynamic Item List Command

To create a dynamic item list command

  1. Open TopLevelMenu.vsct.

  2. In the Symbols section, in the GuidSymbol node named guidTopLevelMenuCmdSet, add the symbol for the MRUListGroup group and the cmdidMRUList command, as follows.

    <IDSymbol name="MRUListGroup" value="0x1200"/>
    <IDSymbol name="cmdidMRUList" value="0x0200"/>
    
  3. In the Groups section, add the declared group after the existing group entries.

    <Group guid="guidTopLevelMenuCmdSet" id="MRUListGroup" 
           priority="0x0100">
        <Parent guid="guidTopLevelMenuCmdSet" id="SubMenu"/>
    </Group>
    
  4. In the Buttons section, add a node to represent the newly declared command, after the existing button entries.

    <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>
    

    The DynamicItemStart flag enables the command to be generated dynamically.

  5. On the Build menu,click Build Solution.

    Doing this rebuilds the .vsct file with the changes. Correct any errors that may occur during building.

  6. Press F5 to test the display of the new command.

  7. On the TestMenu menu, click the new submenu, Sub Menu, to display the new command, MRU Placeholder. After a dynamic MRU list of commands is implemented in the next procedure, this command label will be replaced by that list every time that the submenu is opened.

    Note

    You must close the experimental Visual Studio before you continue to the next section.

Filling the MRU List

To fill the MRU list by using managed code

  1. Open PkgCmdID.cs or PkgCmdID.vb in the code editor.

  2. Add the following command ID after the existing command IDs in the PkgCmdIDList class definition.

    public const uint cmdidMRUList = 0x200;
    
  3. Open TopLevelMenuPackage.cs or TopLevelMenuPackage.vb in the code editor.

  4. At the top of the file, at the end of the list of using statements, add the following statement.

    using System.Collections;
    
  5. Find the Initialize method. You may have to find the hidden region labeled "Package Members" and expand it. The Initialize method is in this hidden region.

  6. To the Initialize method, add the following line just after the last call to the AddCommand method. The InitMRUMenu will be undefined until after step 8.

    this.InitMRUMenu(mcs);
    
  7. At the end of the MyTopLevelMenuPackage class, add the following code just after the SubItemCallback method. This code initializes the list of strings that represent the items to be shown on the MRU list.

    private int numMRUItems = 4;
    private int baseMRUID = (int)PkgCmdIDList.cmdidMRUList;
    private ArrayList mruList;
    
    private void InitializeMRUList()
    {
        if (null == this.mruList)
        {
            this.mruList = new ArrayList();
            if (null != this.mruList)
            {
                for (int i = 0; i < this.numMRUItems; i++)
                {
                    this.mruList.Add(string.Format(CultureInfo.CurrentCulture,
                                                   "Item {0}", i + 1));
                }
            }
        }
    }
    
  8. After the InitializeMRUList method, add the following InitMRUMenu method. This initializes the MRU list menu commands.

    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);
        }
    }
    

    In managed code, you must create a menu command object for every possible item in the MRU list. The IDE calls the OnMRUQueryStatus method for each item in the MRU list until there are no more items. In managed code, the only way for the IDE to know that there are no more items is to create all possible items first. If you want, you can mark additional items as not visible at first by using mc.Visible = false; after the menu command is created. These items can then be made visible later by using mc.Visible = true; in the OnMRUQueryStatus method.

  9. After the InitMRUMenu method, add the following OnMRUQueryStatus method. This is the handler that sets the text for each MRU item.

    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;
            }
        }
    }
    
  10. After the OnMRUQueryStatus method, add the following OnMRUExec method. This is the handler for selecting an MRU item. This method moves the selected item to the top of the list and then displays the selected item in a message box.

    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));
            }
        }
    }
    
  11. On the Build menu, click Build Solution to build the solution. Correct any errors that may occur.

Testing the MRU List

To test the MRU menu list

  1. Press F5 to test the display of the new command.

  2. On the TestMenu menu, click Test Command. Doing this displays a message box that indicates that the command was selected.

    Note

    This step is required to force the VSPackage to load and correctly display the MRU list. If you skip this step, the MRU list is not displayed.

  3. On the Test Menu menu, click Sub Menu. A list of four items is displayed at the end of the submenu, below a separator. When you click Item 3, a message box should appear and display the text, "Selected Item 3". (If the list of four items is not displayed, ensure that you have followed the instructions in the earlier step.)

  4. Open the submenu again. Notice that Item 3 is now at the top of the list and the other items have been pushed down one position. Click Item 3 again and notice that the message box still displays "Selected Item 3", which indicates that the text has correctly moved to the new position together with the command label.

See Also

Tasks

Walkthrough: Dynamically Adding Menu Items

Other Resources

Walkthroughs for User Interface Elements

Commands, Menus, and Toolbars