Visual Studio 菜单项出现多次

Visual Studio menu item appearing multiple times

我已经为我正在开发的扩展程序创建了一个菜单项;然而,它在“工具”菜单中出现了 4 次而不是一次。以下是我所拥有的,但我一直无法弄清楚为什么菜单项出现不止一次。

VSCT 文件

<?xml version="1.0" encoding="utf-8"?>
<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <Extern href="stdidcmd.h"/>
  <Extern href="vsshlids.h"/>

  <Commands package="guidTemplatePackPkg">
    <Groups>
      <Group guid="guidTemplatePackCmdSet" id="MyMenuGroup" priority="0x0600">
        <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
      </Group>
    </Groups>

    <Buttons>

      <Button guid="guidTemplatePackCmdSet" id="cmdidMyCommand" priority="0x2000" type="Button">
        <Parent guid="guidSHLMainMenu" id="IDG_VS_CTXT_PROJECT_ADD_REFERENCES" />
        <CommandFlag>DynamicVisibility</CommandFlag>
        <CommandFlag>DefaultInvisible</CommandFlag>
        <Strings>
          <CommandName>AddSideWaffleProject</CommandName>
          <ButtonText>Add Template Reference (SideWaffle project)</ButtonText>
        </Strings>
      </Button>
    </Buttons> 
  </Commands>

  <!-- SideWaffle Menu Options -->
  <Commands package="guidMenuOptionsPkg">
    <Groups>
      <Group guid="guidMenuOptionsCmdSet" id="SWMenuGroup" priority="0x0600">
        <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
      </Group>
    </Groups>

    <Buttons>
      <Button guid="guidMenuOptionsCmdSet" id="cmdidOpenSWMenu" priority="0x0100" type="Button">
        <Parent guid="guidMenuOptionsCmdSet" id="SWMenuGroup" />
        <Icon guid="guidImages" id="bmpPic1" />
        <Strings>
          <ButtonText>SideWaffle Settings</ButtonText>
        </Strings>
      </Button>
    </Buttons>

    <Bitmaps>
      <Bitmap guid="guidImages" href="Resources\Images.png" usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows"/>
    </Bitmaps>
  </Commands>
  <!-- End SideWaffle Menu Options -->

  <Symbols>
    <GuidSymbol name="guidTemplatePackPkg" value="{e6e2a48e-387d-4af2-9072-86a5276da6d4}" />

    <GuidSymbol name="guidTemplatePackCmdSet" value="{a94bef1a-053e-4066-a851-16e5f6c915f1}">
      <IDSymbol name="MyMenuGroup" value="0x1020" />
      <IDSymbol name="cmdidMyCommand" value="0x0100" />
    </GuidSymbol>

    <!-- SideWaffle Menu Options -->
    <GuidSymbol name="guidMenuOptionsPkg" value="{ee0cf212-810b-45a1-8c62-e10041913c94}" />
    <GuidSymbol name="guidMenuOptionsCmdSet" value="{c75eac28-63cd-4766-adb1-e655471525ea}">
      <IDSymbol name="SWMenuGroup" value="0x1020" />
      <IDSymbol name="cmdidOpenSWMenu" value="0x0100" />
    </GuidSymbol>

    <GuidSymbol name="guidImages" value="{e2bf6a31-afea-46fb-9397-0c2add3a59d8}" >
      <IDSymbol name="bmpPic1" value="1" />
      <IDSymbol name="bmpPic2" value="2" />
      <IDSymbol name="bmpPicSearch" value="3" />
      <IDSymbol name="bmpPicX" value="4" />
      <IDSymbol name="bmpPicArrows" value="5" />
      <IDSymbol name="bmpPicStrikethrough" value="6" />
    </GuidSymbol>
    <!-- End SideWaffle Menu Options -->
  </Symbols>

</CommandTable>

TemplatePackPackage.cs

using System;
using System.Linq;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.ComponentModel.Design;
using Microsoft.Win32;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using System.Collections.Generic;
using EnvDTE;
using EnvDTE80;
using LigerShark.Templates.DynamicBuilder;
using TemplatePack.Tooling;

namespace TemplatePack
{
    [PackageRegistration(UseManagedResourcesOnly = true)]
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
    [ProvideMenuResource("Menus.ctmenu", 1)]
    [Guid(GuidList.guidTemplatePackPkgString)]
    [ProvideAutoLoad(UIContextGuids80.SolutionExists)]
    public sealed class TemplatePackPackage : Package
    {
        private DTE2 _dte;

        protected override void Initialize()
        {
            base.Initialize();
            _dte = GetService(typeof(DTE)) as DTE2;

            OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
            if (null != mcs)
            {
                CommandID cmdId = new CommandID(GuidList.guidTemplatePackCmdSet, (int)PkgCmdIDList.cmdidMyCommand);
                OleMenuCommand button = new OleMenuCommand(ButtonClicked, cmdId);
                button.BeforeQueryStatus += button_BeforeQueryStatus;
                mcs.AddCommand(button);
            }

            /*if(Environment.GetEnvironmentVariable("SideWaffleEnableDynamicTemplates") != null)*/{
                try {
                    new DynamicTemplateBuilder().ProcessTemplates();
                }
                catch (Exception ex) {
                    // todo: replace with logging or something
                    System.Windows.MessageBox.Show(ex.ToString());
                }
            }
        }

        void button_BeforeQueryStatus(object sender, EventArgs e)
        {
            var button = (OleMenuCommand)sender;
            var project = GetSelectedProjects().ElementAt(0);

            // TODO: We should only show this if the target project has the TemplateBuilder NuGet pkg installed
            //       or something similar to that.
            button.Visible = true;
            // button.Visible = project.IsWebProject();
        }

        private void ButtonClicked(object sender, EventArgs e)
        {
            Project currentProject = GetSelectedProjects().ElementAt(0);
            var projects = _dte.Solution.GetAllProjects();
            var names = from p in projects
                        where p != currentProject
                        select p.Name;

            ProjectSelector selector = new ProjectSelector(names);
            bool? isSelected = selector.ShowDialog();

            if (isSelected.HasValue && isSelected.Value)
            {
                // need to save everything because we will directly write to the project file in the creator
                _dte.ExecuteCommand("File.SaveAll");

                TemplateReferenceCreator creator = new TemplateReferenceCreator();
                var selectedProject = projects.First(p => p.Name == selector.SelectedProjectName);
                creator.AddTemplateReference(currentProject, selectedProject);
            }
        }

        public IEnumerable<Project> GetSelectedProjects()
        {
            var items = (Array)_dte.ToolWindows.SolutionExplorer.SelectedItems;
            foreach (UIHierarchyItem selItem in items)
            {
                var item = selItem.Object as Project;
                if (item != null)
                {
                    yield return item;
                }
            }
        }
    }

    [PackageRegistration(UseManagedResourcesOnly = true)]
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
    [ProvideMenuResource("Menus.ctmenu", 1)]
    [Guid(GuidList.guidMenuOptionsPkgString)]
    public sealed class MenuOptionsPackage : Package
    {     
        // Overridden Package Implementation
        #region Package Members

        protected override void Initialize()
        {
            base.Initialize();

            // Add our command handlers for menu (commands must exist in the .vsct file)
            OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
            if ( null != mcs )
            {
                // Create the command for the menu item.
                CommandID menuCommandID = new CommandID(GuidList.guidMenuOptionsCmdSet, (int)PkgCmdIDList.cmdidMyCommand);
                MenuCommand menuItem = new MenuCommand(MenuItemCallback, menuCommandID );
                mcs.AddCommand( menuItem );
            }
        }
        #endregion

        private void MenuItemCallback(object sender, EventArgs e)
        {
            // Here is where our UI (i.e. user control) will go to do all the settings.
            var window = new SettingsForm();
            window.Show();
        }
    }
}

PackageConstants.cs

using System;

namespace TemplatePack
{
    static class GuidList
    {
        public const string guidTemplatePackPkgString = "e6e2a48e-387d-4af2-9072-86a5276da6d4";
        public const string guidTemplatePackCmdSetString = "a94bef1a-053e-4066-a851-16e5f6c915f1";

        public static readonly Guid guidTemplatePackCmdSet = new Guid(guidTemplatePackCmdSetString);

        // SideWaffle Remote Source Settings
        public const string guidMenuOptionsPkgString = "ee0cf212-810b-45a1-8c62-e10041913c94";
        public const string guidMenuOptionsCmdSetString = "c75eac28-63cd-4766-adb1-e655471525ea";

        public static readonly Guid guidMenuOptionsCmdSet = new Guid(guidMenuOptionsCmdSetString);
    }

    static class PkgCmdIDList
    {
        public const uint cmdidMyCommand = 0x100;
        public const uint SWMenuGroup = 0x100;
    };
}

我似乎无法弄清楚我可能做错了什么。有什么建议吗?

...what I might be doing wrong.

您实际上是在尝试将两个包 (TemplatePackPackageMenuOptionsPackage) 捆绑到一个包中,这会弄乱您的 .pkgdef 文件,我认为这是你的问题。

如果您注释掉 MenuOptionsPackage class,那么您应该只会看到一个菜单项 - 正如预期的那样。

Any suggestions?

  1. 通过单个 Package class.
  2. 公开您的所有功能
  3. 创建两个不同的扩展项目,每个项目包含相应的 Package。如果您需要通过另一个访问一个,您可以 provide and consume 服务。