Udostępnij za pośrednictwem


Dynamiczne dodawanie elementów menu

Elementy menu można dodawać w czasie wykonywania, określając flagę polecenia w definicji przycisku zastępczego w pliku tabeli poleceń programu Visual Studio (vsct), a następnie definiując DynamicItemStart (w kodzie) liczbę elementów menu do wyświetlania i obsługi poleceń. Po załadowaniu pakietu VSPackage symbol zastępczy zostanie zastąpiony elementami menu dynamicznego.

Program Visual Studio używa list dynamicznych na liście ostatnio używanych (MRU), która wyświetla nazwy dokumentów, które zostały ostatnio otwarte, oraz listę systemu Windows , która wyświetla nazwy okien, które są obecnie otwarte. Flaga DynamicItemStart w definicji polecenia określa, że polecenie jest symbolem zastępczym do momentu otwarcia pakietu VSPackage. Po otwarciu pakietu VSPackage symbol zastępczy zostanie zastąpiony co najmniej 0 poleceniami utworzonymi w czasie wykonywania i dodanymi do listy dynamicznej. Pozycja w menu, w którym jest wyświetlana lista dynamiczna, może nie być widoczna, dopóki pakiet VSPackage nie zostanie otwarty. Aby wypełnić listę dynamiczną, program Visual Studio prosi program VSPackage o wyszukanie polecenia o identyfikatorze, którego pierwsze znaki są takie same jak identyfikator symbolu zastępczego. Gdy program Visual Studio znajdzie pasujące polecenie, dodaje nazwę polecenia do listy dynamicznej. Następnie zwiększa identyfikator i szuka innego pasującego polecenia, aby dodać je do listy dynamicznej, dopóki nie będzie więcej dynamicznych poleceń.

W tym przewodniku pokazano, jak ustawić projekt startowy w rozwiązaniu programu Visual Studio za pomocą polecenia na pasku narzędzi Eksplorator rozwiązań. Używa kontrolera menu, który ma dynamiczną listę rozwijaną projektów w aktywnym rozwiązaniu. Aby nie wyświetlać tego polecenia, gdy żadne rozwiązanie nie jest otwarte lub gdy otwarte rozwiązanie ma tylko jeden projekt, pakiet VSPackage jest ładowany tylko wtedy, gdy rozwiązanie ma wiele projektów.

Aby uzyskać więcej informacji na temat plików vsct , zobacz pliki tabeli poleceń programu Visual Studio (vsct).

Tworzenie rozszerzenia za pomocą polecenia menu

  1. Utwórz projekt VSIX o nazwie DynamicMenuItems.

  2. Po otwarciu projektu dodaj niestandardowy szablon elementu polecenia i nadaj mu nazwę DynamicMenu. Aby uzyskać więcej informacji, zobacz Create an extension with a menu command (Tworzenie rozszerzenia za pomocą polecenia menu).

Konfigurowanie elementów w pliku vsct

Aby utworzyć kontroler menu z dynamicznymi elementami menu na pasku narzędzi, należy określić następujące elementy:

  • Dwie grupy poleceń, jedna zawierająca kontroler menu, a druga zawierająca elementy menu na liście rozwijanej

  • Jeden element menu typu MenuController

  • Dwa przyciski, jeden, który działa jako symbol zastępczy dla elementów menu, a drugi, który dostarcza ikonę i etykietkę narzędzia na pasku narzędzi.

  1. W pliku DynamicMenuPackage.vsct zdefiniuj identyfikatory poleceń. Przejdź do sekcji Symbole i zastąp elementy IDSymbol w bloku guidDynamicMenuPackageCmdSet GuidSymbol. Należy zdefiniować elementy IDSymbol dla dwóch grup, kontrolera menu, polecenia symbolu zastępczego i polecenia kotwicy.

    <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. W sekcji Grupy usuń istniejące grupy i dodaj dwie zdefiniowane właśnie grupy:

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

    Dodaj kontrolkę MenuController. Ustaw flagę polecenia DynamicVisibility, ponieważ nie zawsze jest widoczna. Tekst przycisku nie jest wyświetlany.

    <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. Dodaj dwa przyciski, jeden jako symbol zastępczy dla dynamicznych elementów menu i jeden jako kotwicę menu MenuController.

    Elementem nadrzędnym przycisku symbolu zastępczego jest MyMenuControllerGroup. Dodaj flagi poleceń DynamicItemStart, DynamicVisibility i TextChanges do przycisku symbolu zastępczego. Tekst przycisku nie jest wyświetlany.

    Przycisk kotwicy zawiera ikonę i tekst etykietki narzędzia. Element nadrzędny przycisku kotwicy jest również elementem MyMenuControllerGroup. Należy dodać flagę polecenia NoShowOnMenuController, aby upewnić się, że przycisk nie jest wyświetlany na liście rozwijanej kontrolera menu, oraz flagę polecenia FixMenuController, aby uczynić ją stałą kotwicą.

    <!-- 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. Dodaj ikonę do projektu (w folderze Resources ), a następnie dodaj do niego odwołanie w pliku vsct . W tym przewodniku użyjemy ikony Strzałki zawartej w szablonie projektu.

  5. Dodaj sekcję Ograniczenia widoczności poza sekcją Polecenia tuż przed sekcją Symbole. (Jeśli dodasz je po symbolach, może pojawić się ostrzeżenie). Ta sekcja zapewnia, że kontroler menu jest wyświetlany tylko w przypadku załadowania rozwiązania z wieloma projektami.

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

Implementowanie dynamicznego polecenia menu

Utworzysz dynamiczną klasę poleceń menu, która dziedziczy z OleMenuCommandklasy . W tej implementacji konstruktor określa predykat, który ma być używany do dopasowywania poleceń. Należy zastąpić metodę DynamicItemMatch , aby użyć tego predykatu, aby ustawić MatchedCommandId właściwość , która identyfikuje polecenie do wywołania.

  1. Utwórz nowy plik klasy C# o nazwie DynamicItemMenuCommand.cs i dodaj klasę o nazwie DynamicItemMenuCommand dziedziczą z OleMenuCommand:

    class DynamicItemMenuCommand : OleMenuCommand
    {
    
    }
    
    
  2. Dodaj następujące dyrektywy using:

    using Microsoft.VisualStudio.Shell;
    using Microsoft.VisualStudio.Shell.Interop;
    using System.ComponentModel.Design;
    
  3. Dodaj pole prywatne do przechowywania predykatu dopasowania:

    private Predicate<int> matches;
    
    
  4. Dodaj konstruktor, który dziedziczy z konstruktora OleMenuCommand i określa program obsługi poleceń i BeforeQueryStatus program obsługi. Dodaj predykat do dopasowania polecenia:

    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. Zastąpuj metodę DynamicItemMatch tak, aby wywołała predykat dopasowania i ustawia MatchedCommandId właściwość :

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

Dodaj polecenie

Konstruktor DynamicMenu służy do konfigurowania poleceń menu, w tym dynamicznych menu i elementów menu.

  1. W pliku DynamicMenuPackage.cs dodaj identyfikator GUID zestawu poleceń i identyfikator polecenia:

    public const string guidDynamicMenuPackageCmdSet = "00000000-0000-0000-0000-00000000";  // get the GUID from the .vsct file
    public const uint cmdidMyCommand = 0x104;
    
  2. W pliku DynamicMenu.cs dodaj następujące dyrektywy using:

    using EnvDTE;
    using EnvDTE80;
    using System.ComponentModel.Design;
    
  3. DynamicMenu W klasie dodaj pole prywatne dte2.

    private DTE2 dte2;
    
  4. Dodaj prywatne pole rootItemId:

    private int rootItemId = 0;
    
  5. W konstruktorze DynamicMenu dodaj polecenie menu. W następnej sekcji zdefiniujemy procedurę obsługi poleceń, BeforeQueryStatus procedurę obsługi zdarzeń i predykat dopasowania.

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

Implementowanie procedur obsługi

Aby zaimplementować dynamiczne elementy menu na kontrolerze menu, należy obsłużyć polecenie po kliknięciu elementu dynamicznego. Należy również zaimplementować logikę ustawiającą stan elementu menu. Dodaj programy obsługi do DynamicMenu klasy .

  1. Aby zaimplementować polecenie Ustaw projekt startowy, dodaj procedurę obsługi zdarzeń OnInvokedDynamicItem. Wyszukuje projekt, którego nazwa jest taka sama jak tekst polecenia, który został wywołany, i ustawia go jako projekt startowy, ustawiając jego bezwzględną ścieżkę we StartupProjects właściwości .

    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. Dodaj procedurę obsługi zdarzeń OnBeforeQueryStatusDynamicItem . Jest to procedura obsługi wywoływana przed zdarzeniem QueryStatus . Określa, czy element menu jest elementem "rzeczywistym", czyli elementem zastępczym, a nie elementem zastępczym i czy element jest już sprawdzany (co oznacza, że projekt jest już ustawiony jako projekt startowy).

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

Implementowanie predykatu dopasowania identyfikatora polecenia

Teraz zaimplementuj predykat dopasowania. Musimy określić dwie elementy: najpierw, czy identyfikator polecenia jest prawidłowy (jest większy lub równy zadeklarowanym identyfikatorowi polecenia), a po drugie, czy określa możliwy projekt (jest to mniejsza niż liczba projektów w rozwiązaniu).

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

Ustaw pakiet VSPackage tak, aby był ładowany tylko wtedy, gdy rozwiązanie ma wiele projektów

Ponieważ polecenie Ustaw projekt startowy nie ma sensu, chyba że aktywne rozwiązanie ma więcej niż jeden projekt, możesz ustawić pakiet VSPackage na automatyczne ładowanie tylko w tym przypadku. ProvideAutoLoadAttribute Używasz razem z kontekstem SolutionHasMultipleProjectsinterfejsu użytkownika . W pliku DynamicMenuPackage.cs dodaj następujące atrybuty do klasy DynamicMenuPackage:

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

Testowanie polecenia set startup project

Teraz możesz przetestować kod.

  1. Skompiluj projekt i rozpocznij debugowanie. Powinno zostać wyświetlone wystąpienie eksperymentalne.

  2. W wystąpieniu eksperymentalnym otwórz rozwiązanie, które ma więcej niż jeden projekt.

    Na pasku narzędzi Eksplorator rozwiązań powinien zostać wyświetlona ikona strzałki. Po rozwinięciu menu powinny zostać wyświetlone elementy menu reprezentujące różne projekty w rozwiązaniu.

  3. Po sprawdzeniu jednego z projektów staje się projektem startowym.

  4. Po zamknięciu rozwiązania lub otwarciu rozwiązania, które ma tylko jeden projekt, ikona paska narzędzi powinna zniknąć.