Freigeben über


Dynamisches Hinzufügen von Menüelementen

Sie können Menüelemente zur Laufzeit hinzufügen, indem Sie die DynamicItemStart Befehlskennzeichnung für eine Platzhalterschaltflächendefinition in der Visual Studio-Befehlstabellendatei (VSCT) angeben und dann die Anzahl der Menüelemente definieren, die angezeigt und die Befehle behandelt werden sollen. Wenn das VSPackage geladen wird, wird der Platzhalter durch die dynamischen Menüelemente ersetzt.

Visual Studio verwendet dynamische Listen in der Liste "Zuletzt verwendet" (MRU), in der die Namen von Dokumenten angezeigt werden, die zuletzt geöffnet wurden, und die Windows-Liste, in der die Namen der derzeit geöffneten Fenster angezeigt werden. Das DynamicItemStart Flag in einer Befehlsdefinition gibt an, dass der Befehl ein Platzhalter ist, bis das VSPackage-Element geöffnet wird. Wenn das VSPackage geöffnet wird, wird der Platzhalter durch 0 oder mehr Befehle ersetzt, die zur Laufzeit erstellt und der dynamischen Liste hinzugefügt werden. Möglicherweise können Sie die Position im Menü nicht sehen, an der die dynamische Liste angezeigt wird, bis das VSPackage geöffnet wird. Zum Auffüllen der dynamischen Liste fordert Visual Studio das VSPackage auf, nach einem Befehl mit einer ID zu suchen, deren erste Zeichen mit der ID des Platzhalters identisch sind. Wenn Visual Studio einen übereinstimmenden Befehl findet, wird der dynamischen Liste der Name des Befehls hinzugefügt. Anschließend erhöht sie die ID und sucht nach einem anderen übereinstimmenden Befehl, der der dynamischen Liste hinzugefügt werden soll, bis keine dynamischen Befehle vorhanden sind.

In dieser exemplarischen Vorgehensweise wird gezeigt, wie Sie das Startprojekt in einer Visual Studio-Projektmappe mit einem Befehl auf der Symbolleiste Projektmappen-Explorer festlegen. Sie verwendet einen Menücontroller mit einer dynamischen Dropdownliste der Projekte in der aktiven Lösung. Damit dieser Befehl nicht angezeigt wird, wenn keine Lösung geöffnet ist oder wenn die geöffnete Projektmappe nur ein Projekt aufweist, wird das VSPackage nur geladen, wenn eine Lösung mehrere Projekte hat.

Weitere Informationen zu VSCT-Dateien finden Sie in Visual Studio-Befehlstabellendateien (VSCT).

Erstellen einer Erweiterung mit einem Menübefehl

  1. Erstellen Sie ein VSIX-Projekt mit dem Namen DynamicMenuItems.

  2. Wenn das Projekt geöffnet wird, fügen Sie eine benutzerdefinierte Befehlselementvorlage hinzu, und nennen Sie sie "DynamicMenu". Weitere Informationen finden Sie unter Erstellen einer Erweiterung mit einem Menübefehl.

Einrichten der Elemente in der VSCT-Datei

Zum Erstellen eines Menücontrollers mit dynamischen Menüelementen auf einer Symbolleiste geben Sie die folgenden Elemente an:

  • Zwei Befehlsgruppen, eine, die den Menücontroller und eine andere enthält, die die Menüelemente im Dropdown enthält

  • Ein Menüelement vom Typ MenuController

  • Zwei Schaltflächen, eine, die als Platzhalter für die Menüelemente fungiert, und eine andere, die das Symbol und die QuickInfo auf der Symbolleiste bereitstellt.

  1. Definieren Sie in DynamicMenuPackage.vsct die Befehls-IDs. Wechseln Sie zum Abschnitt "Symbole", und ersetzen Sie die IDSymbol-Elemente im guidDynamicMenuPackageCmdSet GuidSymbol-Block. Sie müssen IDSymbol-Elemente für die beiden Gruppen definieren, den Menücontroller, den Platzhalterbefehl und den Ankerbefehl.

    <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. Löschen Sie im Abschnitt "Gruppen" die vorhandenen Gruppen, und fügen Sie die beiden soeben definierten Gruppen hinzu:

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

    Fügen Sie den MenuController hinzu. Legen Sie die DynamicVisibility-Befehlskennzeichnung fest, da sie nicht immer sichtbar ist. Der ButtonText wird nicht angezeigt.

    <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. Fügen Sie zwei Schaltflächen hinzu, eine als Platzhalter für die dynamischen Menüelemente und eine als Anker für den MenuController.

    Das übergeordnete Element der Platzhalterschaltfläche ist die MyMenuControllerGroup. Fügen Sie die Befehlskennzeichnungen "DynamicItemStart", "DynamicVisibility" und "TextChanges" zur Schaltfläche "Platzhalter" hinzu. Der ButtonText wird nicht angezeigt.

    Die Ankerschaltfläche enthält das Symbol und den QuickInfo-Text. Das übergeordnete Element der Ankerschaltfläche ist auch die MyMenuControllerGroup. Sie fügen das NoShowOnMenuController-Befehlsflagge hinzu, um sicherzustellen, dass die Schaltfläche nicht tatsächlich im Dropdownmenü des Menücontrollers angezeigt wird, und das FixMenuController-Befehlsflagge, um sie zum dauerhaften Anker zu machen.

    <!-- 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. Fügen Sie dem Projekt (im Ordner "Ressourcen ") ein Symbol hinzu, und fügen Sie dann den Verweis darauf in der VSCT-Datei hinzu. In dieser exemplarischen Vorgehensweise verwenden wir das Symbol "Pfeile", das in der Projektvorlage enthalten ist.

  5. Fügen Sie einen VisibilityConstraints-Abschnitt außerhalb des Befehlsabschnitts direkt vor dem Abschnitt "Symbole" hinzu. (Möglicherweise wird eine Warnung angezeigt, wenn Sie sie nach Symbolen hinzufügen.) In diesem Abschnitt wird sichergestellt, dass der Menücontroller nur angezeigt wird, wenn eine Projektmappe mit mehreren Projekten geladen wird.

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

Implementieren des Dynamischen Menübefehls

Sie erstellen eine Befehlsklasse für dynamische Menüs, die von OleMenuCommand. In dieser Implementierung gibt der Konstruktor ein Prädikat an, das für übereinstimmende Befehle verwendet werden soll. Sie müssen die DynamicItemMatch Methode überschreiben, um dieses Prädikat zum Festlegen der MatchedCommandId Eigenschaft zu verwenden, wodurch der zu aufrufende Befehl identifiziert wird.

  1. Erstellen Sie eine neue C#-Klassendatei namens "DynamicItemMenuCommand.cs", und fügen Sie eine Klasse namens "DynamicItemMenuCommand" hinzu, die von OleMenuCommand:

    class DynamicItemMenuCommand : OleMenuCommand
    {
    
    }
    
    
  2. Fügen Sie die folgenden using-Direktiven hinzu:

    using Microsoft.VisualStudio.Shell;
    using Microsoft.VisualStudio.Shell.Interop;
    using System.ComponentModel.Design;
    
  3. Fügen Sie ein privates Feld hinzu, um das Übereinstimmungsdrädikat zu speichern:

    private Predicate<int> matches;
    
    
  4. Fügen Sie einen Konstruktor hinzu, der OleMenuCommand vom Konstruktor erbt, und gibt einen Befehlshandler und einen BeforeQueryStatus Handler an. Fügen Sie ein Prädikat zum Abgleichen des Befehls hinzu:

    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. Überschreiben Sie die DynamicItemMatch Methode so, dass sie das Übereinstimmungsdrädikat aufruft und die MatchedCommandId Eigenschaft festlegt:

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

Hinzufügen des Befehls

Der DynamicMenu-Konstruktor richtet Menübefehle ein, einschließlich dynamischer Menüs und Menüelemente.

  1. Fügen Sie in DynamicMenuPackage.cs die GUID des Befehlssatzes und der Befehls-ID hinzu:

    public const string guidDynamicMenuPackageCmdSet = "00000000-0000-0000-0000-00000000";  // get the GUID from the .vsct file
    public const uint cmdidMyCommand = 0x104;
    
  2. Fügen Sie in der Datei "DynamicMenu.cs " die folgenden Direktiven hinzu:

    using EnvDTE;
    using EnvDTE80;
    using System.ComponentModel.Design;
    
  3. Fügen Sie in der DynamicMenu Klasse ein privates Feld dte2 hinzu.

    private DTE2 dte2;
    
  4. Hinzufügen eines privaten RootItemId-Felds:

    private int rootItemId = 0;
    
  5. Fügen Sie im DynamicMenu-Konstruktor den Menübefehl hinzu. Im nächsten Abschnitt definieren wir den Befehlshandler, den BeforeQueryStatus Ereignishandler und das Übereinstimmungs-Prädikat.

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

Implementieren der Handler

Um dynamische Menüelemente in einem Menücontroller zu implementieren, müssen Sie den Befehl behandeln, wenn auf ein dynamisches Element geklickt wird. Sie müssen auch die Logik implementieren, die den Status des Menüelements festlegt. Fügen Sie der Klasse die Handler hinzu DynamicMenu .

  1. Um den Befehl "Startprojekt festlegen" zu implementieren, fügen Sie den OnInvokedDynamicItem-Ereignishandler hinzu. Es sucht nach dem Projekt, dessen Name dem Text des aufgerufenen Befehls entspricht, und legt es als Startprojekt fest, indem er seinen absoluten Pfad in der StartupProjects Eigenschaft festlegt.

    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. Fügen Sie den OnBeforeQueryStatusDynamicItem Ereignishandler hinzu. Dies ist der Handler, der vor einem QueryStatus Ereignis aufgerufen wird. Es bestimmt, ob es sich bei dem Menüelement um ein "echtes" Element handelt, d. h. nicht um das Platzhalterelement, und ob das Element bereits überprüft wird (d. h., das Projekt ist bereits als Startprojekt festgelegt).

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

Implementieren des Befehls-ID-Übereinstimmungs-Prädikats

Implementieren Sie nun das Übereinstimmungs-Prädikat. Wir müssen zwei Dinge bestimmen: zuerst, ob die Befehls-ID gültig ist (sie ist größer oder gleich der deklarierten Befehls-ID) und zweitens, ob ein mögliches Projekt angegeben wird (es ist kleiner als die Anzahl der Projekte in der Lösung).

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

Festlegen, dass vsPackage nur geladen wird, wenn eine Lösung mehrere Projekte hat

Da der Befehl "Startprojekt festlegen" nicht sinnvoll ist, es sei denn, die aktive Projektmappe verfügt über mehrere Projekte, können Sie das VSPackage so festlegen, dass es nur in diesem Fall automatisch geladen wird. Sie verwenden ProvideAutoLoadAttribute zusammen mit dem Ui-Kontext SolutionHasMultipleProjects. Fügen Sie in der Datei "DynamicMenuPackage.cs " der DynamicMenuPackage-Klasse die folgenden Attribute hinzu:

[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
{}

Testen des Befehls zum Festlegen des Startprojekts

Jetzt können Sie Ihren Code testen.

  1. Erstellen Sie das Projekt, und starten Sie das Debugging. Die experimentelle Instanz sollte angezeigt werden.

  2. Öffnen Sie in der experimentellen Instanz eine Lösung mit mehreren Projekten.

    Auf der Symbolleiste Projektmappen-Explorer sollte das Pfeilsymbol angezeigt werden. Wenn Sie sie erweitern, sollten Menüelemente, die die verschiedenen Projekte in der Projektmappe darstellen, angezeigt werden.

  3. Wenn Sie eines der Projekte überprüfen, wird es zum Startprojekt.

  4. Wenn Sie die Projektmappe schließen oder eine Projektmappe öffnen, die nur ein Projekt enthält, sollte das Symbolleistensymbol ausgeblendet werden.