Dynamic display custom Visual Studio VSPackage command on toolbar
Asked Answered
M

1

8

Let's assume we have a VSPackage with a toolbar and a couple of commands on the toolbar. How can you programmatically show/hide one of the commands on the toolbar? If you are a user you can do that by customizing the toolbar. Therefore I have a strong feeling that there must be a way to do this from the code as well.

Since we are not developing an AddIn, we can't use DTE.Commands.AddNamedCommand(AddInInstance, Name, ButtonText, ToolTip, MSOButton).

That's all right, because we can still define our commands using the Visual Studio Command Table (.vsct) format, and that is the proposed way for VSPackages:

<?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="vsshlids.h" />

  <Commands package="testPackage">

    <Menus>  <!-- Define the menus, toolbars, etc. -->
      <Menu guid="commands" id="toolbar" type="Toolbar">
        <Parent guid="guidSHLMainMenu" id="IDG_VS_BUILD_SOLUTION" />
        <CommandFlag>DefaultDocked</CommandFlag>
        <Strings>
          <ButtonText>TestToolbar</ButtonText>
        </Strings>
      </Menu>
    </Menus>

    <Groups>  <!-- Define the groups for commands -->
      <Group guid="commands" id="toolbarGroup" priority="0x0001">
        <Parent guid="commands" id="toolbar" />
      </Group>
    </Groups>

    <Buttons>  <!-- Define the commands as buttons -->
      <Button guid="commands" id="button0" type="Button">
        <Parent guid="commands" id="toolbarGroup" />
        <CommandFlag>DynamicVisibility</CommandFlag>
        <Strings>
          <ButtonText>TestButton0</ButtonText>
        </Strings>
      </Button>
      <Button guid="commands" id="button1" type="Button">
        <Parent guid="commands" id="toolbarGroup" />
        <CommandFlag>DynamicVisibility</CommandFlag>
        <Strings>
          <ButtonText>TestButton1</ButtonText>
        </Strings>
      </Button>
    </Buttons>

  </Commands>

  <Symbols>
    <GuidSymbol name="testPackage" value="{FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF}" />

    <GuidSymbol name="commands" value="{EEEEEEEE-EEEE-EEEE-EEEE-EEEEEEEEEEEE}">
      <IDSymbol name="toolbar" value="0x0100" />
      <IDSymbol name="toolbarGroup" value="0x0010" />
      <IDSymbol name="button0" value="0x0000" />
      <IDSymbol name="button1" value="0x0001" />
    </GuidSymbol>
  </Symbols>

</CommandTable>

Later on the C# code you can set different properties of a MenuCommand we have just defined above:

System.ComponentModel.Design.MenuCommand menuCommand = <Acquire your menu command>;
menuCommand.Enabled = <enabled>;
menuCommand.Visible = <visible>;
menuCommand.Supported = <supported>;

The problem is, that if the menu command is placed on a toolbar, then making the Visible property false would not hide the button, just makes it grayed out. (It is hidden on any other menus all right.) This is a feature of Visual Studio, not a bug.

Yet, what I need is exactly this: hiding Button0. (Let's assume Button0 is only shown when some special condition applies, e.g.: you have less space on your hard drive than X MB or when you have some other tool installed, or just make up your own condition here.)

One could use the following technique from the AddIn times to delete the button, if not needed:

EnvDTE.Command button0 = DTE.Commands.Item(commandsGuid, button0CommandId); // Both are the same as in the .vsct file
if (button0 != null)
    button0.Delete();

Command0 is found, but when trying to delete, I've got an exception: Unspecified error (Exception from HRESULT: 0x80004005 (E_FAIL)) After all, it kinda makes sense, since it was created through the .vsct mechanism, not programmatically.

I'm running out of ideas. Please, help me find out how to hide/show or add/remove toolbar buttons programmatically at run time. Is there any other way to define the VSPackage commands but the .vsct file?

Any help appreciated.

Menhir answered 17/7, 2014 at 9:12 Comment(3)
I hit the same problem. I also tried to use <VisibilityConstraints> section of vsct file, but without success. Did you make any progress with this?Logogram
@Logogram we stuck with the disabled (grayed out) icon. Never figured out how to make it disappear as there had been tasks with higher priority than this.Menhir
#14572726Hooper
H
8

First of all you have to set DynamicVisibility flag on your button in vsct file:

  <Button guid="commands" id="button0" priority="0x1001" type="Button">
    <Parent guid="commands" id="toolbarGroup" />
    <CommandFlag>DefaultInvisible</CommandFlag>
    <CommandFlag>DynamicVisibility</CommandFlag>
    <Strings>
      <ButtonText>TestButton1</ButtonText>
    </Strings>
  </Button>

Next, in overriden Package.Initialize create command handler using OleMenuCommand class instead of MenuCommand and subscribe to BeforeQueryStatus event like this:

OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
if (null != mcs)
{
   // Create the command for the menu item.
   CommandID menuCommandID = new CommandID(GuidList.guidToolbarCmdSet, (int)PkgCmdIDList.cmdidButton0);
   var menuItem = new OleMenuCommand(MenuItemCallback, menuCommandID);
   menuItem.BeforeQueryStatus += BeforeQueryStatusCallback;
   mcs.AddCommand(menuItem);
}

Now in BeforeQueryStatusCallback you can show or hide you button

private void BeforeQueryStatusCallback(object sender, EventArgs e)
{
    var cmd = (OleMenuCommand)sender
    cmd.Visible = true;
}
Hypocrisy answered 17/7, 2014 at 10:2 Comment(2)
This is perfectly true, if the button is not located on a toolbar. But in my case it is, therefore it is not hidden, but grayed out as I pointed out in my original post.You are right, I should have included the DynamicVisiblility flag on the items. I've fixed it.Menhir
Typo - missing semi-colon at end of line: var cmd = (OleMenuCommand)senderPseudohemophilia

© 2022 - 2024 — McMap. All rights reserved.