I'm trying to create dynamic menu items in a VS 2015 extension. The best I can do is to leave the placeholder command visible and active. Here's the most minimal example I could create starting with the VS wizards (sorry for the length, but it really is the most minimal example I could create):
Command1Package.cs:
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using System;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace minimal {
[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] // Info on this package for Help/About
[ProvideMenuResource("Menus.ctmenu", 1)]
[Guid(Command1Package.PackageGuidString)]
[ProvideAutoLoad(VSConstants.UICONTEXT.NoSolution_string)]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")]
public sealed class Command1Package : Package {
public const string PackageGuidString = "3e88287b-7b79-403d-ae8d-3329af218869";
public Command1Package() {
}
#region Package Members
protected override void Initialize() {
Debug.WriteLine("minimal package instantiated");
Command1.Initialize(this, GetService(typeof(IMenuCommandService)) as OleMenuCommandService);
base.Initialize();
}
#endregion
}
}
Command1.cs:
using Microsoft.VisualStudio.Shell;
using System;
using System.ComponentModel.Design;
using System.Diagnostics;
namespace minimal {
internal sealed class Command1 {
public static readonly Guid CommandSet = new Guid("c1388361-6429-452c-8ba0-580d292ef0ca");
private Command1() {
}
public static void Initialize(Package package, OleMenuCommandService mcs) {
AddOneCommand(mcs, 0x0100, "Renamed placeholder command"); // the placeholder
AddOneCommand(mcs, 0x0101, "Dynamic command 1"); // a dynamic command
AddOneCommand(mcs, 0x0102, "Dynamic command 2");
}
internal static void AddOneCommand(OleMenuCommandService mcs, uint cmdid, string cmdname) {
Debug.WriteLine("AddOneCommand" + cmdid.ToString("X4"));
CommandID id = new CommandID(CommandSet, (int) cmdid);
OleMenuCommand mc = new OleMenuCommand(new EventHandler(MenuItemCallback), id, cmdname);
mc.BeforeQueryStatus += OnBeforeQueryStatus;
mcs.AddCommand(mc);
}
private static void OnBeforeQueryStatus(object sender, EventArgs e) {
OleMenuCommand mc = sender as OleMenuCommand;
Debug.WriteLine("OnBeforeQueryStatus called for " + mc.CommandID.ToString());
}
private static void MenuItemCallback(object sender, EventArgs e) {
OleMenuCommand mc = sender as OleMenuCommand;
System.Windows.Forms.MessageBox.Show("MenuItemCallback called for " + mc.CommandID.ToString());
}
}
}
Command1Package.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="guidCommand1Package">
<Groups>
<Group guid="guidCommand1PackageCmdSet" id="MyMenuGroup" priority="0x0600">
<Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
</Group>
<Group guid="guidCommand1PackageCmdSet" id="MyMenuSubgroup" priority="0x0100">
<Parent guid="guidCommand1PackageCmdSet" id="SubMenu"/>
</Group>
</Groups>
<Menus>
<Menu guid="guidCommand1PackageCmdSet" id="SubMenu" priority="0x0100" type="Menu">
<Parent guid="guidCommand1PackageCmdSet" id="MyMenuGroup"/>
<Strings>
<ButtonText>Minimal commands</ButtonText>
<CommandName>MinimalCommands</CommandName>
</Strings>
</Menu>
</Menus>
<Buttons>
<Button guid="guidCommand1PackageCmdSet" id="Command1Id" priority="0x0100" type="Button">
<Parent guid="guidCommand1PackageCmdSet" id="MyMenuSubgroup" />
<CommandFlag>DynamicItemStart</CommandFlag>
<CommandFlag>TextChanges</CommandFlag>
<CommandFlag>DynamicVisibility</CommandFlag>
<Strings>
<ButtonText>Invoke Command1</ButtonText>
<CommandName>Command1</CommandName>
</Strings>
</Button>
</Buttons>
</Commands>
<Symbols>
<GuidSymbol name="guidCommand1Package" value="{3e88287b-7b79-403d-ae8d-3329af218869}" />
<GuidSymbol name="guidCommand1PackageCmdSet" value="{c1388361-6429-452c-8ba0-580d292ef0ca}">
<IDSymbol name="MyMenuGroup" value="0x1020" />
<IDSymbol name="MyMenuSubgroup" value="0x1021"/>
<IDSymbol name="SubMenu" value="0x200"/>
<IDSymbol name="Command1Id" value="0x0100" />
</GuidSymbol>
</Symbols>
</CommandTable>
Some observations:
[ProvideAutoLoad]
attribute on the package, the framework doesn't instantiate the package until after you execute the placeholder command, which is the only item on the submenu at that point. It seems like the code generated by the VS wizard should do this for you, since this attribute (among many others) isn't mentioned in the help entry for the Package class.OnBeforeQueryStatus
) made the entire submenu vanish. The best I could do was to change the name of this command. The help entry for "How to: Dynamically Add Menu Items" doesn't mention this. (Don't bother reading the entry for VS 2015, because the example code is needlessly complex and doesn't show how to do the one simple thing of adding a menu command dynamically. The VS 2012 example is much easier to follow.)GetMenu()
, GetSubMenu()
, etc. I thought .net was supposed to be easier than traditional Win32 programming.</rant>Yes, it is much harder than in MFC.
[ProvideAutoLoad] is indeed required.
You should not add the placeholder command. Start from
AddOneCommand(mcs, 0x0100, "Dynamic command 1");
Usually you add <CommandFlag>DefaultInvisible</CommandFlag> to the DynamicItemStart command and set menuCommand.Text and menuCommand.Visible in the OnBeforeQueryStatus handler.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With