0

Just imagine you are in Solution Explorer and you expand a few folders to find a file with an extension "xyz". When you right-click this file, you get a context menu. What I want to do be able to do is add a new/custom button to that context menu BUT ONLY for the "xyz" file type, for example.

After a lot of hunting, I am stumped. I have buttons in the main menu easy enough, but I am trying to make it conditionally present in the context of a file. If you can help, thank you in advance.

Jerry Nixon
  • 31,313
  • 14
  • 117
  • 233
  • You can add a general context menu command with dynamic visibility and before showing it check what file type is currently selected. – Sergey Vlasov Apr 22 '23 at 04:06
  • Sounds like a good plan. Can you give me a small pointer to where I can check for the file type like that? – Jerry Nixon Apr 22 '23 at 16:06
  • See https://stackoverflow.com/questions/34677290/visual-studio-extension-get-the-path-of-the-current-selected-file-in-the-soluti or https://stackoverflow.com/questions/5501777/getting-the-selected-file-and-using-ivssinglefilegenerator – Sergey Vlasov Apr 23 '23 at 03:02
  • Hi, have you check my steps below to create VSIX project to add context menu for file type? You can use `ProvideUXContextRule` attribute to control when a button should be visible. – Dou Xu-MSFT Apr 28 '23 at 06:23

1 Answers1

0

Based on Sergey Vlasov's comment, i created a VSIX project using Dynamically add menu items for test in VS2022.It can be able to add a new/custom button to that context menu BUT ONLY for the "xyz" file type,for example. Please check the following steps.

This is the final run result: enter image description here Steps

1 Create a VSIX project named DynamicMenuItems.

2 When the project opens, add a custom command item template and name it FirstCommand.

After completing these two steps and you will see the project directory structure: enter image description here

3 Specify a rule for when a button should be visible. In this example, the rule is that the button should be visible when the user right-clicks a .xyz file in Solution Explorer. We can express that in an attribute on the Package or AsyncPackage class like so:

[ProvideUIContextRule(PackageGuidString,
        name: "Supported Files",
        expression: "xyz",
        termNames: new[] { "xyz" },
        termValues: new[] { "HierSingleSelectionName:.xyz$" })]

See sample DynamicMenuItemsPackage.cs file about using the ProvideUXContextRule attribute.

using System;
using System.ComponentModel.Design;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Task = System.Threading.Tasks.Task;

namespace DynamicMenuItems
{
    /// <summary>
    /// This is the class that implements the package exposed by this assembly.
    /// </summary>
    /// <remarks>
    /// <para>
    /// The minimum requirement for a class to be considered a valid package for Visual Studio
    /// is to implement the IVsPackage interface and register itself with the shell.
    /// This package uses the helper classes defined inside the Managed Package Framework (MPF)
    /// to do it: it derives from the Package class that provides the implementation of the
    /// IVsPackage interface and uses the registration attributes defined in the framework to
    /// register itself and its components with the shell. These attributes tell the pkgdef creation
    /// utility what data to put into .pkgdef file.
    /// </para>
    /// <para>
    /// To get loaded into VS, the package must be referred by &lt;Asset Type="Microsoft.VisualStudio.VsPackage" ...&gt; in .vsixmanifest file.
    /// </para>
    /// </remarks>
    [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
    [Guid(DynamicMenuItemsPackage.PackageGuidString)]
    [ProvideMenuResource("Menus.ctmenu", 1)]
    [ProvideUIContextRule(PackageGuidString,
        name: "Supported Files",
        expression: "xyz",
        termNames: new[] { "xyz" },
        termValues: new[] { "HierSingleSelectionName:.xyz$" })]
    public sealed class DynamicMenuItemsPackage : AsyncPackage
    {
        /// <summary>
        /// DynamicMenuItemsPackage GUID string.
        /// </summary>
        public const string PackageGuidString = "f4dc9564-aebc-4d7a-826a-9d8c13e1542e";

        #region Package Members

        /// <summary>
        /// Initialization of the package; this method is called right after the package is sited, so this is the place
        /// where you can put all the initialization code that rely on services provided by VisualStudio.
        /// </summary>
        /// <param name="cancellationToken">A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down.</param>
        /// <param name="progress">A provider for progress updates.</param>
        /// <returns>A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method.</returns>
        protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
        {
            // When initialized asynchronously, the current thread may be a background thread at this point.
            // Do any initialization that requires the UI thread after switching to the UI thread.
            // Request any services while on the background thread
            await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
            await FirstCommand.InitializeAsync(this);

        }

        #endregion
       
    }
}

4 Modify the menu group created for your action to make it an “Item node menu” command instead of a “Visual Studio Menu” command. Please refer to IDM_VS_CTXT_ITEMNODE

<Group guid="guidDynamicMenuItemsPackageCmdSet" id="MyMenuGroup" priority="0x0600">
    <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_ITEMNODE"/>
</Group>

5 Register a based on that rule in the .vsct.

<VisibilityConstraints>
    <VisibilityItem guid="guidDynamicMenuItemsPackageCmdSet" id="FirstCommandId" context="PackageGuidString" />
</VisibilityConstraints>

6 Mark the button itself as dynamic visible.

 <CommandFlag>DynamicVisibility</CommandFlag>

7 Customize your context menu name.

<Strings>
       <ButtonText>My frist context menu</ButtonText>
 </Strings>

8 Add UI Context guid that must match the one in DynamicMenuItemsPackage.cs.

 <GuidSymbol name="PackageGuidString" value="{f4dc9564-aebc-4d7a-826a-9d8c13e1542e}" />

see sample DynamicMenuItemsPackage.vsct file:

<?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="guidDynamicMenuItemsPackage">
    <Groups>
      <Group guid="guidDynamicMenuItemsPackageCmdSet" id="MyMenuGroup" priority="0x0600">
          <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_ITEMNODE"/>
      </Group>
    </Groups>
    
    <Buttons>
      <Button guid="guidDynamicMenuItemsPackageCmdSet" id="FirstCommandId" priority="0x0100" type="Button">
        <Parent guid="guidDynamicMenuItemsPackageCmdSet" id="MyMenuGroup" />
        <Icon guid="guidImages" id="bmpPic1" />
          <CommandFlag>DynamicVisibility</CommandFlag>
        <Strings>
          <ButtonText>My frist context menu</ButtonText>
        </Strings>
      </Button>
    </Buttons>

    <Bitmaps>
      <Bitmap guid="guidImages" href="Resources\FirstCommand.png" usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows, bmpPicStrikethrough"/>
    </Bitmaps>
  </Commands>
    <VisibilityConstraints>
        <VisibilityItem guid="guidDynamicMenuItemsPackageCmdSet" id="FirstCommandId" context="PackageGuidString" />
    </VisibilityConstraints>

  <Symbols>
    <!-- This is the package guid. -->
    <GuidSymbol name="guidDynamicMenuItemsPackage" value="{f4dc9564-aebc-4d7a-826a-9d8c13e1542e}" />
      
      <!-- This is the UI Context guid that must match the one in MyPackage.cs -->
      <GuidSymbol name="PackageGuidString" value="{f4dc9564-aebc-4d7a-826a-9d8c13e1542e}" />

    <!-- This is the guid used to group the menu commands together -->
    <GuidSymbol name="guidDynamicMenuItemsPackageCmdSet" value="{7ce2b30e-f5dc-4d0b-ac54-d731b2555fe7}">
      <IDSymbol name="MyMenuGroup" value="0x1020" />
      <IDSymbol name="FirstCommandId" value="0x0100" />
    </GuidSymbol>

    <GuidSymbol name="guidImages" value="{cf09af20-801f-4be3-b5d0-fabb0acb4e9d}" >
      <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>
  </Symbols>
</CommandTable>

Note:You can refer to this guide:

https://github.com/microsoft/VSSDK-Extensibility-Samples/tree/master/VisibilityConstraints#lets-get-started

Dou Xu-MSFT
  • 880
  • 1
  • 1
  • 4