Sdílet prostřednictvím


Dynamické přidávání položek nabídky

Položky nabídky můžete přidat za běhu zadáním příznaku DynamicItemStart příkazu v definici zástupného tlačítka v souboru příkazové tabulky sady Visual Studio (.vsct) a následným definováním (v kódu) počtu položek nabídky, které se mají zobrazit a zpracovat. Při načtení balíčku VSPackage se zástupný symbol nahradí položkami dynamické nabídky.

Visual Studio používá dynamické seznamy v seznamu Naposledy použité (MRU), který zobrazuje názvy dokumentů, které byly nedávno otevřeny, a seznam Windows , který zobrazuje názvy oken, které jsou aktuálně otevřené. Příznak DynamicItemStart definice příkazu určuje, že příkaz je zástupný symbol, dokud se neotevře balíček VSPackage. Po otevření balíčku VSPackage se zástupný symbol nahradí 0 nebo více příkazy vytvořenými za běhu a přidají se do dynamického seznamu. V nabídce, kde se dynamický seznam zobrazí, se nemusí zobrazit, dokud se balíček VSPackage neotevře. K naplnění dynamického seznamu sada Visual Studio požádá balíček VSPackage, aby vyhledal příkaz s ID, jehož první znaky jsou stejné jako ID zástupného symbolu. Když Visual Studio najde odpovídající příkaz, přidá název příkazu do dynamického seznamu. Potom zvýší ID a vyhledá další odpovídající příkaz, který se přidá do dynamického seznamu, dokud nebudou k dispozici žádné další dynamické příkazy.

Tento návod ukazuje, jak nastavit spouštěný projekt v řešení sady Visual Studio pomocí příkazu na panelu nástrojů Průzkumník řešení. Používá kontroler nabídek, který má dynamický rozevírací seznam projektů v aktivním řešení. Aby se tento příkaz nezopravoval, když není otevřené řešení nebo když má otevřené řešení jenom jeden projekt, sada VSPackage se načte pouze v případě, že řešení obsahuje více projektů.

Další informace o souborech .vsct naleznete v tématu Soubory příkazové tabulky sady Visual Studio (.vsct).

Vytvoření rozšíření pomocí příkazu nabídky

  1. Vytvořte projekt VSIX s názvem DynamicMenuItems.

  2. Po otevření projektu přidejte vlastní šablonu položky příkazu a pojmenujte ji DynamicMenu. Další informace najdete v tématu Vytvoření rozšíření pomocí příkazu nabídky.

Nastavení prvků v souboru .vsct

Chcete-li vytvořit kontroler nabídek s dynamickými položkami nabídky na panelu nástrojů, zadejte následující prvky:

  • Dvě skupiny příkazů, jedna obsahující kontroler nabídek a druhá obsahující položky nabídky v rozevíracím seznamu

  • Jeden prvek nabídky typu MenuController

  • Dvě tlačítka, jedna, která slouží jako zástupný symbol pro položky nabídky a druhá, která obsahuje ikonu a popis na panelu nástrojů.

  1. V DynamicMenuPackage.vsct definujte ID příkazů. Přejděte do části Symbols a nahraďte elementy IDSymbol v guidDynamicMenuPackageCmdSet GuidSymbol bloku. Musíte definovat prvky IDSymbol pro dvě skupiny, řadič nabídky, zástupný příkaz a příkaz ukotvení.

    <GuidSymbol name="guidDynamicMenuPackageCmdSet" value="{ your GUID here }">
        <IDSymbol name="MyToolbarItemGroup" value="0x1020" />
        <IDSymbol name="MyMenuControllerGroup" value="0x1025" />
        <IDSymbol name="MyMenuController" value ="0x1030"/>
        <IDSymbol name="cmdidMyAnchorCommand" value="0x0103" />
        <!-- NOTE: The following command expands at run time to some number of ids.
         Try not to place command ids after it (e.g. 0x0105, 0x0106).
         If you must add a command id after it, make the gap very large (e.g. 0x200) -->
        <IDSymbol name="cmdidMyDynamicStartCommand" value="0x0104" />
    </GuidSymbol>
    
  2. V části Skupiny odstraňte existující skupiny a přidejte dvě skupiny, které jste právě definovali:

    <Groups>
        <!-- The group that adds the MenuController on the Solution Explorer toolbar.
             The 0x4000 priority adds this group after the group that contains the
             Preview Selected Items button, which is normally at the far right of the toolbar. -->
        <Group guid="guidDynamicMenuPackageCmdSet" id="MyToolbarItemGroup" priority="0x4000" >
            <Parent guid="guidSHLMainMenu" id="IDM_VS_TOOL_PROJWIN" />
        </Group>
        <!-- The group for the items on the MenuController drop-down. It is added to the MenuController submenu. -->
        <Group guid="guidDynamicMenuPackageCmdSet" id="MyMenuControllerGroup" priority="0x4000" >
            <Parent guid="guidDynamicMenuPackageCmdSet" id="MyMenuController" />
        </Group>
    </Groups>
    

    Přidejte MenuController. Nastavte příznak příkazu DynamicVisibility, protože není vždy viditelný. Text tlačítka se nezobrazuje.

    <Menus>
        <!-- The MenuController to display on the Solution Explorer toolbar.
             Place it in the ToolbarItemGroup.-->
        <Menu guid="guidDynamicMenuPackageCmdSet" id="MyMenuController" priority="0x1000" type="MenuController">
            <Parent guid="guidDynamicMenuPackageCmdSet" id="MyToolbarItemGroup" />
            <CommandFlag>DynamicVisibility</CommandFlag>
            <Strings>
               <ButtonText></ButtonText>
           </Strings>
        </Menu>
    </Menus>
    
  3. Přidejte dvě tlačítka, jedno jako zástupný symbol pro položky dynamické nabídky a jedno jako ukotvení pro MenuController.

    Nadřazený objekt zástupného tlačítka je MyMenuControllerGroup. Přidejte příznaky příkazu DynamicItemStart, DynamicVisibility a TextChanges na zástupné tlačítko. Text tlačítka se nezobrazuje.

    Tlačítko ukotvení obsahuje ikonu a text popisu. Nadřazený prvek tlačítka ukotvení je také MyMenuControllerGroup. Přidáte příznak příkazu NoShowOnMenuController, abyste se ujistili, že se tlačítko ve skutečnosti nezobrazuje v rozevíracím seznamu kontroleru nabídek, a příznak příkazu FixMenuController, aby byl trvalý ukotvení.

    <!-- The placeholder for the dynamic items that expand to N items at run time. -->
    <Buttons>
        <Button guid="guidDynamicMenuPackageCmdSet" id="cmdidMyDynamicStartCommand" priority="0x1000" >
          <Parent guid="guidDynamicMenuPackageCmdSet" id="MyMenuControllerGroup" />
          <CommandFlag>DynamicItemStart</CommandFlag>
          <CommandFlag>DynamicVisibility</CommandFlag>
          <CommandFlag>TextChanges</CommandFlag>
          <!-- This text does not appear. -->
          <Strings>
            <ButtonText>Project</ButtonText>
          </Strings>
        </Button>
    
        <!-- The anchor item to supply the icon/tooltip for the MenuController -->
        <Button guid="guidDynamicMenuPackageCmdSet" id="cmdidMyAnchorCommand" priority="0x0000" >
          <Parent guid="guidDynamicMenuPackageCmdSet" id="MyMenuControllerGroup" />
          <!-- This is the icon that appears on the Solution Explorer toolbar. -->
          <Icon guid="guidImages" id="bmpPicArrows"/>
          <!-- Do not show on the menu controller's drop down list-->
          <CommandFlag>NoShowOnMenuController</CommandFlag>
          <!-- Become the permanent anchor item for the menu controller -->
          <CommandFlag>FixMenuController</CommandFlag>
          <!-- The text that appears in the tooltip.-->
          <Strings>
            <ButtonText>Set Startup Project</ButtonText>
          </Strings>
        </Button>
    </Buttons>
    
  4. Přidejte do projektu ikonu (ve složce Zdroje ) a pak do souboru .vsct přidejte odkaz. V tomto názorném postupu používáme ikonu Šipky, která je součástí šablony projektu.

  5. Přidejte oddíl VisibilityConstraints mimo oddíl Příkazy těsně před oddíl Symboly. (Pokud ho přidáte za symboly, může se zobrazit upozornění.) Tato část zajišťuje, aby se kontroler nabídek zobrazil pouze v případě, že je načteno řešení s více projekty.

    <VisibilityConstraints>
         <!--Make the MenuController show up only when there is a solution with more than one project loaded-->
        <VisibilityItem guid="guidDynamicMenuPackageCmdSet" id="MyMenuController" context="UICONTEXT_SolutionHasMultipleProjects"/>
    </VisibilityConstraints>
    

Implementace příkazu dynamické nabídky

Vytvoříte dynamickou třídu příkazů nabídky, která dědí z OleMenuCommand. V této implementaci konstruktor určuje predikát, který se má použít pro odpovídající příkazy. Chcete-li nastavit MatchedCommandId vlastnost, která identifikuje příkaz, který má být vyvolán, je nutné přepsat DynamicItemMatch metodu použití tohoto predikátu.

  1. Vytvořte nový soubor třídy C# s názvem DynamicItemMenuCommand.cs a přidejte třídu s názvem DynamicItemMenuCommand , která dědí z OleMenuCommand:

    class DynamicItemMenuCommand : OleMenuCommand
    {
    
    }
    
    
  2. Přidejte následující direktivy using:

    using Microsoft.VisualStudio.Shell;
    using Microsoft.VisualStudio.Shell.Interop;
    using System.ComponentModel.Design;
    
  3. Přidejte soukromé pole pro uložení predikátu shody:

    private Predicate<int> matches;
    
    
  4. Přidejte konstruktor, který dědí z konstruktoru OleMenuCommand a určuje obslužnou rutinu příkazu a obslužnou rutinu BeforeQueryStatus . Přidejte predikát pro porovnávání příkazu:

    public DynamicItemMenuCommand(CommandID rootId, Predicate<int> matches, EventHandler invokeHandler, EventHandler beforeQueryStatusHandler)
        : base(invokeHandler, null /*changeHandler*/, beforeQueryStatusHandler, rootId)
    {
        if (matches == null)
        {
            throw new ArgumentNullException("matches");
        }
    
        this.matches = matches;
    }
    
  5. Přepište metodu DynamicItemMatch tak, aby volaly shody predikát a nastaví MatchedCommandId vlastnost:

    public override bool DynamicItemMatch(int cmdId)
    {
        // Call the supplied predicate to test whether the given cmdId is a match.
        // If it is, store the command id in MatchedCommandid
        // for use by any BeforeQueryStatus handlers, and then return that it is a match.
        // Otherwise clear any previously stored matched cmdId and return that it is not a match.
        if (this.matches(cmdId))
        {
            this.MatchedCommandId = cmdId;
            return true;
        }
    
        this.MatchedCommandId = 0;
        return false;
    }
    

Přidání příkazu

Konstruktor DynamicMenu je místo, kde nastavíte příkazy nabídky, včetně dynamických nabídek a položek nabídky.

  1. V souboru DynamicMenuPackage.cs přidejte identifikátor GUID sady příkazů a ID příkazu:

    public const string guidDynamicMenuPackageCmdSet = "00000000-0000-0000-0000-00000000";  // get the GUID from the .vsct file
    public const uint cmdidMyCommand = 0x104;
    
  2. Do souboru DynamicMenu.cs přidejte následující direktivy using:

    using EnvDTE;
    using EnvDTE80;
    using System.ComponentModel.Design;
    
  3. DynamicMenu Do třídy přidejte privátní pole dte2.

    private DTE2 dte2;
    
  4. Přidejte privátní pole rootItemId:

    private int rootItemId = 0;
    
  5. V konstruktoru DynamicMenu přidejte příkaz nabídky. V další části definujeme obslužnou rutinu příkazu, obslužnou rutinu BeforeQueryStatus události a predikát shody.

    private DynamicMenu(Package package)
    {
        if (package == null)
        {
            throw new ArgumentNullException(nameof(package));
        }
    
        this.package = package;
    
        OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
        if (commandService != null)
        {
            // Add the DynamicItemMenuCommand for the expansion of the root item into N items at run time.
            CommandID dynamicItemRootId = new CommandID(
                new Guid(DynamicMenuPackageGuids.guidDynamicMenuPackageCmdSet),
                (int)DynamicMenuPackageGuids.cmdidMyCommand);
            DynamicItemMenuCommand dynamicMenuCommand = new DynamicItemMenuCommand(
                dynamicItemRootId,
                IsValidDynamicItem,
                OnInvokedDynamicItem,
                OnBeforeQueryStatusDynamicItem);
                commandService.AddCommand(dynamicMenuCommand);
        }
    
        this.dte2 = (DTE2)this.ServiceProvider.GetService(typeof(DTE));
    }
    

Implementace obslužných rutin

Chcete-li implementovat dynamické položky nabídky v kontroleru nabídek, musíte zpracovat příkaz při kliknutí na dynamickou položku. Musíte také implementovat logiku, která nastaví stav položky nabídky. Přidejte obslužné rutiny do DynamicMenu třídy.

  1. Chcete-li implementovat příkaz Set Startup Project , přidejte OnInvokedDynamicItem obslužnou rutinu události. Vyhledá projekt, jehož název je stejný jako text příkazu, který byl vyvolán, a nastaví ho jako spouštěný projekt nastavením jeho absolutní cesty ve StartupProjects vlastnosti.

    private void OnInvokedDynamicItem(object sender, EventArgs args)
    {
        DynamicItemMenuCommand invokedCommand = (DynamicItemMenuCommand)sender;
        // If the command is already checked, we don't need to do anything
        if (invokedCommand.Checked)
            return;
    
        // Find the project that corresponds to the command text and set it as the startup project
        var projects = dte2.Solution.Projects;
        foreach (Project proj in projects)
        {
            if (invokedCommand.Text.Equals(proj.Name))
            {
                dte2.Solution.SolutionBuild.StartupProjects = proj.FullName;
                return;
            }
        }
    }
    
  2. Přidejte obslužnou rutinu OnBeforeQueryStatusDynamicItem události. Toto je obslužná rutina volaná před událostí QueryStatus . Určuje, zda je položka nabídky skutečnou položkou, tj. ne zástupnou položkou a zda je položka již zaškrtnutá (což znamená, že projekt je již nastaven jako spouštěný projekt).

    private void OnBeforeQueryStatusDynamicItem(object sender, EventArgs args)
    {
        DynamicItemMenuCommand matchedCommand = (DynamicItemMenuCommand)sender;
        matchedCommand.Enabled = true;
        matchedCommand.Visible = true;
    
        // Find out whether the command ID is 0, which is the ID of the root item.
        // If it is the root item, it matches the constructed DynamicItemMenuCommand,
         // and IsValidDynamicItem won't be called.
        bool isRootItem = (matchedCommand.MatchedCommandId == 0);
    
        // The index is set to 1 rather than 0 because the Solution.Projects collection is 1-based.
        int indexForDisplay = (isRootItem ? 1 : (matchedCommand.MatchedCommandId - (int) DynamicMenuPackageGuids.cmdidMyCommand) + 1);
    
        matchedCommand.Text = dte2.Solution.Projects.Item(indexForDisplay).Name;
    
        Array startupProjects = (Array)dte2.Solution.SolutionBuild.StartupProjects;
        string startupProject = System.IO.Path.GetFileNameWithoutExtension((string)startupProjects.GetValue(0));
    
        // Check the command if it isn't checked already selected
        matchedCommand.Checked = (matchedCommand.Text == startupProject);
    
        // Clear the ID because we are done with this item.
        matchedCommand.MatchedCommandId = 0;
    }
    

Implementace predikátu shody ID příkazu

Teď implementujte predikát shody. Nejprve musíme určit dvě věci: jestli je ID příkazu platné (je větší nebo rovno deklarovanému ID příkazu) a druhé, jestli určuje možný projekt (je menší než počet projektů v řešení).

private bool IsValidDynamicItem(int commandId)
{
    // The match is valid if the command ID is >= the id of our root dynamic start item
    // and the command ID minus the ID of our root dynamic start item
    // is less than or equal to the number of projects in the solution.
    return (commandId >= (int)DynamicMenuPackageGuids.cmdidMyCommand) && ((commandId - (int)DynamicMenuPackageGuids.cmdidMyCommand) < dte2.Solution.Projects.Count);
}

Nastavení balíčku VSPackage tak, aby se načítá pouze v případech, kdy řešení má více projektů

Protože příkaz Nastavit spouštěný projekt nemá smysl, pokud aktivní řešení nemá více než jeden projekt, můžete balíček VSPackage nastavit tak, aby se automaticky načítal jenom v takovém případě. Používáte ProvideAutoLoadAttribute společně s kontextem SolutionHasMultipleProjectsuživatelského rozhraní . Do souboru DynamicMenuPackage.cs přidejte do třídy DynamicMenuPackage následující atributy:

[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
[ProvideMenuResource("Menus.ctmenu", 1)]
[ProvideAutoLoad(UIContextGuids.SolutionHasMultipleProjects)]
[Guid(DynamicMenuPackage.PackageGuidString)]
public sealed class DynamicMenuItemsPackage : Package
{}

Otestování příkazu set startup project

Teď můžete svůj kód otestovat.

  1. Sestavte projekt a spusťte ladění. Měla by se zobrazit experimentální instance.

  2. V experimentální instanci otevřete řešení, které má více než jeden projekt.

    Na panelu nástrojů Průzkumník řešení by se měla zobrazit ikona šipky. Po rozbalení by se měly zobrazit položky nabídky, které představují různé projekty v řešení.

  3. Když zkontrolujete jeden z projektů, stane se projektem po spuštění.

  4. Když řešení zavřete nebo otevřete řešení, které má jenom jeden projekt, ikona panelu nástrojů by měla zmizet.