Add a button to Solution Explorer toolbar

One of the extensibility features of Visual Studio is the ability to add new commands. These commands could be visible in toolbar, menu, context menu, or the toolbar of the other wondows. In this post, I’ll create a Visual Studio extenstion which adds a button to Solutuion explorer.

The step by step guide shows you how to:

  • Create a Visual Studio extension
  • Add a new command and show it in Solution explorer
  • Set the visibility rule for the command, so it should be visible only when there’s a “.cs” file is selected.
  • Open the file in code editor when you click on the button.

Add a button to solution explorer to open .cs files in code editor

This extension adds a toolbar button to the Solution Explore which will be visible, only if you choose a C# file, and when you click on it, it opens the file in code editor:

You can download or clone the solution here:

Here is the step by step guide on creating this extension:

  1. Add new project and choose ‘VSIX Project‘ and leave the default name.
    Please note: To have this project type, you need to install ‘Visual Studio extension development‘ when installing/modifying VS installation.

  2. Add new item and choose ‘Command‘ which is under ‘Extensibility‘ group.

  3. Open ‘VSIXProject1Package.cs’ file and add the following line of code to the class:
    public const string UIContextGuid = "8B40D5E2-5626-42AE-99EF-3DD1EFF46E7B";
    

    It could be any GUID.

  4. Open ‘VSIXProject1Package.vsct’ file and add the following as child of <Symbols> node:
    <GuidSymbol name="UIContextGuid" value="{8B40D5E2-5626-42AE-99EF-3DD1EFF46E7B}" />
    

    The GUID here should be same as the GUID in previous step.

  5. Modify the attributes of the class to be like this:
    [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
    [Guid(VSIXProject1Package.PackageGuidString)]
    [ProvideMenuResource("Menus.ctmenu", 1)]
    [ProvideUIContextRule(VSIXProject1Package.UIContextGuid,
    name: "Supported Files",
    expression: "CSharp",
    termNames: new[] { "CSharp" },
    termValues: new[] { "HierSingleSelectionName:.cs$" })]
    public sealed class VSIXProject1Package : AsyncPackage
    
  6. Open ‘Command1.cs’ and modify the ServiceProvider property to this:
    private IServiceProvider ServiceProvider
    {
       get
       {
           return this.package;
       }
    }
    
  7. Replace the ExecuteCommand with this:
    private void Execute(object sender, EventArgs e)
    {
       ThreadHelper.ThrowIfNotOnUIThread();
       var dte =  ServiceProvider.GetService(typeof(DTE)) as DTE; 
       ProjectItem item = dte.SelectedItems.Item(1)?.ProjectItem;
       if (item != null)
       {
           VsShellUtilities.OpenDocumentWithSpecificEditor(package,
               item.FileNames[1],
               Microsoft.VisualStudio.VSConstants
                   .VsEditorFactoryGuid.TextEditor_guid,
               Microsoft.VisualStudio.VSConstants.LOGVIEWID.Code_guid);
       }
    }
    
  8. Add the following method:
    private void MyQueryStatus(object sender, EventArgs e)
    {
       ThreadHelper.ThrowIfNotOnUIThread();
       var button = (MenuCommand)sender;
       button.Visible = false;
       var dte = ServiceProvider.GetService(typeof(DTE)) as DTE;
       ProjectItem item = dte.SelectedItems.Item(1)?.ProjectItem;
       if (item != null)
       {
           string fileExtension = 
               Path.GetExtension(item.Name).ToLowerInvariant();
           string[] supportedFiles = new[] { ".cs"};
           button.Visible = supportedFiles.Contains(fileExtension);
       }
    }
    
  9. Open the file and replace the content of the Groups node with the following:

    <Group guid="guidVSIXProject1PackageCmdSet" 
    id="SolutionToolbarGroup" priority="0xF000">
      <Parent guid="guidSHLMainMenu" id="IDM_VS_TOOL_PROJWIN"/>
    </Group>
    
  10. Replace the child of the GuidSymbol which has name = guidVSIXProject1PackageCmdSet with the following:
    <IDSymbol name="Command1Id" value="0x0100" />
    <IDSymbol name="SolutionToolbarGroup" value="0x0190"/>
    
  11. Add the visibility constraint, right after the end of </Commands> tag:
    <VisibilityConstraints>
      <VisibilityItem guid="guidVSIXProject1PackageCmdSet"
      id="Command1Id"
      context="UIContextGuid" />
    </VisibilityConstraints>
    
  12. Modify the generated button tag to:
    <Button guid="guidVSIXProject1PackageCmdSet" id="Command1Id" priority="0x0100" type="Button">
      <Parent guid="guidVSIXProject1PackageCmdSet" id="SolutionToolbarGroup" />
      <Icon guid="guidImages" id="bmpPic1" />
      <CommandFlag>DefaultInvisible</CommandFlag>
      <CommandFlag>DynamicVisibility</CommandFlag>
      <Strings>
         <ButtonText>Invoke Command1</ButtonText>
      </Strings>
    </Button>
    
  13. Run the project and see the result.

More information

You May Also Like

About the Author: Reza Aghaei

I’ve been a .NET developer since 2004. During these years, as a developer, technical lead and architect, I’ve helped organizations and development teams in design and development of different kind of applications including LOB applications, Web and Windows application frameworks and RAD tools. As a teacher and mentor, I’ve trained tens of developers in C#, ASP.NET MVC and Windows Forms. As an interviewer I’ve helped organizations to assess and hire tens of qualified developers. I really enjoy learning new things, problem solving, knowledge sharing and helping other developers. I'm usually active in .NET related tags in stackoverflow to answer community questions. I also share technical blog posts in my blog as well as sharing sample codes in GitHub.

Leave a Reply

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