Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic display custom Visual Studio VSPackage command on toolbar

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.

like image 235
Shakaron Avatar asked Jul 17 '14 09:07

Shakaron


1 Answers

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;
}
like image 102
Vit Avatar answered Oct 22 '22 17:10

Vit