Add custom buttons to the Visual Studio toolbar.
External Tools are easy to create, but they don't let you specify custom icons for some reason.
- Visual Studio 2022, 2026, or later for Windows
- Bitbucket
- Browse current repository's Bitbucket web page
- Git Extensions
- Opens current repository in Git Extensions
- GitHub
- Browse current repository's GitHub web page
- Sublime Text
- Edit current file in Sublime Text
- Total Commander
- Opens current project directory in a new Total Commander tab
- From Visual Studio Marketplace
- From the VSIX file from this repository's latest release
- Buttons are available in the Tools › Ben's Buttons submenu
- Add a button to a toolbar
- On the toolbar, click … › Add or Remove Buttons › Customize…
- Click Add Command…
- Select the Tools category
- Select one of the buttons provided by this extension, then click OK
- Arrange the new button using Move Up, Move Down, and Modify Selection to make it appear as desired
- Add a keyboard shortcut to a button
- Go to Tools › Options › More Settings (VS ≥ 2026) › Keyboard
- Search for the command with the
Tools.BensButtons.prefix, such asTools.BensButtons.GitExtensions - Type the desired key combination in the Press shortcut keys text box
- Click Assign
Do you want to integrate with a different program, or change which pane Total Commander opens your project directory in? To add new buttons beyond what this extension already provides, or edit the built-in buttons, you can fork this repository and edit its source.
In general, just search this project for all 5 other instances of Add new buttons here and make a new one that looks like the existing buttons. It's easier than the piles of XML make it seem.
- Add new button to
menus.vsct.- New buttons go in
/CommandTable/Commands/Buttons - Add a new button definition that looks like
<Button guid="commandSet" id="myProgram" priority="106" type="Button"> <Parent guid="commandSet" id="submenuGroup" /> <Icon guid="imageManifest" id="myProgram" /> <CommandFlag>IconIsMoniker</CommandFlag> <Strings> <ButtonText>My Program</ButtonText> <ToolTipText>Open file in My Program</ToolTipText> <CanonicalName>BensButtons.MyProgram</CanonicalName> <LocCanonicalName>BensButtons.MyProgram</LocCanonicalName> </Strings> </Button>
- Replace the
myProgramcommand ID symbol with your own arbitrary command ID symbol - Replace
ButtonText,ToolTipText,CanonicalName, andLocCanonicalNamewith the friendly name of the program, a description of the button action, and two versions without spaces, respectively - Replace
prioritywith the sort index of this button, which should be unique among buttons in thesubmenuGroupcommand group - Add two new command IDs to the
/CommandTable/Symbols/GuidSymbolelements- The
commandSetshould contain a command ID likewhere the<IDSymbol name="myProgram" value="106" />
namewas used above in theButton/@id, and thevalueis not used by any other commands and is therefore unique in the command set - The
imageManifestshould contain the icon ID likewhere the<IDSymbol name="myProgram" value="106" />
namewas used above in theButton/Icon/@id, and thevalueis unique in the image manifest; it doesn't have to be the same as thevaluefrom thecommandSet
- The
- New buttons go in
- Import images into the project.
- Image files go in the
Resourcesdirectory - Accepted formats are PNG (raster, 32bpp including transparency) and XAML (vector)
- Native raster dimensions are 16×16px for 100% scaling, and vector images are scaled correctly regardless of the viewbox size
- Different formats can be mixed in the same image, for example a 16px raster image for 100% scaling, 32px for 200%, and a vector fallback for all other scaled sizes such as 48px (300%)
- Images can have variations for light and dark mode
- All PNG files must have their Build Action changed from Content to Resource, which should be handled automatically by the project
- Image files go in the
- Add new images to
images.imagemanifest- New
Imageelements go in/ImageManifest/Images - Add a new image definition that looks like
<Image Guid="$(imageManifest)" ID="106" AllowColorInversion="false"> <Source Uri="$(Resources)/myProgram-16.png"> <Size Value="16" /> </Source> <Source Uri="$(Resources)/myProgram-24.png"> <Size Value="24" /> </Source> <Source Uri="$(Resources)/myProgram-32.png"> <Size Value="32" /> </Source> <Source Uri="$(Resources)/myProgram-48.png"> <Size Value="48" /> </Source> </Image>
- Replace the ID with the number used in
menus.vsctin thevaluefor theIDSymbolforimageManifest - Replace the filename with the PNG or XAML filename, prefixed with
$(Resources)/to load it from the correct library and folder - The size is the edge length of the image in physical pixels when this source is allowed to be used (16px at 100% scaling, 24px at 150%, etc)
- To specify different images for light and dark mode, set the
Backgroundattribute on theSourcetoLightorDark, respectively; otherwise you can setImage/@AllowColorInversiontofalseto prevent Visual Studio from inverting the black and white pixels of the image in dark mode<Image Guid="$(imageManifest)" ID="106"> <Source Uri="$(Resources)/myprogram-light.xaml" Background="Light" /> <Source Uri="$(Resources)/myprogram-dark.xaml" Background="Dark" /> </Image>
- Vector images can be converted using SvgToXaml, although the XAML it generates needs to be reorganized to work in a standalone XAML file: the
DrawingGroupmust be removed from theDrawingImageand put in aDrawingBrush'sDrawingproperty, which goes inside aRectangleinside aViewbox; seegithub-dark.xamlandbitbucket.xamlfor examples<Viewbox xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <Rectangle Width="16" Height="16"> <Rectangle.Fill> <DrawingBrush> <DrawingBrush.Drawing> <!-- DrawingGroup element and descendants from SvgToXaml --> </DrawingBrush.Drawing> </DrawingBrush> </Rectangle.Fill> </Rectangle> </Viewbox>
- New
- Add a new button command class.
- Subclass
AbstractButtonCommandnamespace BensButtons.Commands; internal class MyProgram: AbstractButtonCommand { public override int commandId => 106; protected override async Task onClick() { // TODO: button was clicked } }
- Replace the
commandIdproperty value with thevalueof your newIDSymbolforcommandSetinmenus.vsct - Handle the button click event in the
onClickmethod
- Subclass
- Register the new button class in the
MyPackageclass- Add a new constructor call to your button class in the
registerButtonsmethod, and callregisteron the new instancenew MyProgram { extensionPackage = this, visualStudio = visualStudio }.register(commandService);
- Add a new constructor call to your button class in the
- Test your changes in another instance of Visual Studio
- Clean and Rebuild your extension (not just Build) to make sure code and image changes aren't ignored by the VSPackage compiler's faulty incremental build logic
- Click Start without Debugging to install the extension into a separate Visual Studio instance and launch it
- Look in the Tools › Ben's Buttons menu
- On build, this extension will be compiled into
bin\$(Configuration)\BensButtons.vsix, which you can double-click to install in Visual Studio
