Dynamic Menu Commands in Visual Studio Packages – Part 1

As I get further into my DBML Fixup project, I’m learning more and more about Visual Studio Extensibility (VSX). Along the way, I have found multiple blogs which have helped me to learn a lot more than I would than just stepping through the documentation. (Those blogs are now listed on the sidebar.) The one I find especially helpful for beginners like me is DiveDeeper, whose LearnVSXNow tutorials are great stepping stones in learning the basics of VSX Packages. He currently has 14 tutorials, the first of which is located here. Although his blog is very helpful for learning about how to work with menu commands, he hasn’t (yet?) delved into making them dynamic. I needed to do this for my project, so I had to "dive deeper," as it were.

In this series of posts I’m going to focus on three ways to make menu commands dynamic—that is, how to make menu commands invisible or disabled in certain contexts, but visible and enabled in others. Two of these ways I found through some experimentation. Consequently, I’m not sure whether these represent VSX best practices, so if anyone has any more input, it would be greatly appreciated. With that said, let’s get started.

Using a Built-In UI Context

For each of these techniques, I’ll be using the same solution, and I’ll post the solution with the current progress at the end of each post. But…first things first: create a new project and select the Visual Studio Integration Package project type.

image

The Visual Studio Integration Package Wizard pops up. For this tutorial we’ll use Visual C# and generate a new key file to sign the assembly.

image

For the basic information page, feel free to type whatever you want for the company name, package name, and detailed information. I’ll leave the minimum Visual Studio edition at "Standard," as well. On the next page, check the Menu Command check box to indicate to the wizard that we want to, of course, make a menu command.

image

We’ll provide the following command name and ID for the command the wizard will generate:

image

On the last page, uncheck the Integration Test Project and Unit Test Project options, as we won’t need these for this demonstration. If you are interested in the Integration Test and Unit Test options, have a look at this blog entry. Click "Finish," and the wizard will create the majority of the solution for us. Press F5 and verify that a "Built In UI Context" menu item appears in the Tools menu:

image

The integration package wizard has gotten us this far, but we have to supply the finishing touches in order to make this menu item dynamic. In order to do this we need a little background in the .vsct file’s structure. There should be one in your project (DynamicMenuDevelopment.vsct in my case). Go ahead and open that file. The following will briefly touch on the different points of the .vsct file. If you don’t have any previous experience working with these, please read this excellent post before continuing.

In the VSCT file, our menu command is represented by the sole Button element:

<Button guid="guidDynamicMenuDevelopmentCmdSet" id="cmdidBuiltInUIContext" priority="0×0100" type="Button">

  <Parent guid="guidDynamicMenuDevelopmentCmdSet" id="MyMenuGroup" />

  <Icon guid="guidImages" id="bmpPic1" />

  <Strings>

    <CommandName>cmdidBuiltInUIContext</CommandName>

    <ButtonText>Built In UI Context</ButtonText>

  </Strings>

</Button>

That "button" is parented by the "MyMenuGroup" group element:

<Group guid="guidDynamicMenuDevelopmentCmdSet" id="MyMenuGroup" priority="0×0600">

  <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>

</Group>

And that group is parented by the Tools menu. If we look at the GuidSymbols element in the file, we can see the GUIDs for our package, guidDynamicMenuDevelopmentPkg, as well as our set of commands, guidDynamicMenuDevelopmentCmdSet. The latter has some IDs which identify the single menu command and the group: cmdidBuiltInUIContext and MyMenuGroup, respectively. But if you look at the Group’s parent in the previous XML snippet, you’ll see a GUID guidSHLMainMenu and an ID IDM_VS_MENU_TOOLS which are not defined here. Where do they come from? They are defined externally in the vsshlids.h, which we "imported" using one of the Extern elements at the beginning of the document.

From here, it’s very simple to add dynamic visibility. Navigate back to the Button element and add the CommandFlag element, as demonstrated below:

<Button guid="guidDynamicMenuDevelopmentCmdSet" id="cmdidBuiltInUIContext" priority="0×0100" type="Button">

  <Parent guid="guidDynamicMenuDevelopmentCmdSet" id="MyMenuGroup" />

  <Icon guid="guidImages" id="bmpPic1" />

  <CommandFlag>DynamicVisibility</CommandFlag>

  <Strings>

    <CommandName>cmdidBuiltInUIContext</CommandName>

    <ButtonText>Built In UI Context</ButtonText>

  </Strings>

</Button>

Simple, right? We’ve told Visual Studio that this command may be invisible at times and visible at other times, but we haven’t told it when. To do that, we must add a VisibilityConstraints element underneath the closing Commands tag.

<VisibilityConstraints>

  <VisibilityItem guid="guidDynamicMenuDevelopmentCmdSet" id="cmdidBuiltInUIContext" context="UICONTEXT_NoSolution" />

</VisibilityConstraints>

If you think about it, you can make some educated guesses about what this piece of the XML says. The VisibilityItem element specifies the same GUID and ID pair as our Button element above, so they must be related. Furthermore, the element specifies a context attribute which we have not seen before. This is the sole part of the VSCT file that tells Visual Studio when a particular command is visible or invisible. You can also infer that UICONTEXT_NoSolution means that the menu command will be visible only when there is no solution open. Press F5 and try it!

That leaves one last question before wrapping up this technique, and that is "Where is UICONTEXT_NoSolution defined?" It certainly isn’t in our GuidSymbols, but if you remember we have a couple of external header files that we reference. One of those is vsshlids.h. If you navigate to the %PROGRAMFILES%Microsoft Visual Studio 2008 SDKVisualStudioIntegrationCommonInc directory, you’ll find vsshlids.h among a multitude of other header files. If you open that file and search for "UICONTEXT_", you’ll see a couple of different contexts, including UICONTEXT_NoSolution. You can use any of the other UI Contexts in this file in order to control dynamic visibility for your menu command.

That was pretty good for writing only a few lines of XML, but if there’s no built-in UI context that fits your requirements, then this solution will not work for you. Also, this technique works only for hiding and show menu commands, not for disabling and enabling them. In short, while it’s fairly easy to do, it’s sometimes not flexible enough for the job.

Next time I’ll outline another technique, which is also fairly simple, but requires us to write some code. Hope this helps you in your VSX development!

Edit: I realize I forgot to post the solution for this example! It is a Visual Studio 2008 solution and it’s located here.

Other Articles in the Series

5 Comments

  1. Hi David,

    I am very glad that not I am the only beginner in VSX who wants to share “guessings”. In a few days (after finishing custom editor articles) I go on with dynamics in menus. Your post is really excelent and didactical. I have put you blog on my list…

    Bests, Istvan (alias DiveDeeper)

Leave a Reply

Your email address will not be published. Required fields are marked *