Build Better Extensions with VisualStudio.Extensibility Preview 3!

Maia Kelner

Today, we’re very excited to announce the third public preview of VisualStudio.Extensibility, our new SDK for creating extensions for Visual Studio!

The VisualStudio.Extensibility SDK focuses on extensions that run out-of-process from the IDE for improved performance and reliability, and it features a modern API that has been streamlined and carefully engineered to maximize developer productivity.

This release builds on our first and second public previews, and it brings some big features like an API to query and modify project information and the ability to create incredible debugger visualizers. Please visit the VisualStudio.Extensibility repo to find docs and installation instructions. You can also use our issue tracker to report bugs and provide feedback.


What’s new for Preview 3 of VisualStudio.Extensibility?

The VisualStudio.Extensibility Preview 3 release expands your ability to create powerful productivity extensions, with features including:


Create custom data visualizers for better debugging

Debugger visualizers, such as the popular IEnumerable visualizer, add tremendous power to the debugging experience in Visual Studio by enabling custom views of complex data types. With this release, we’ve leveraged our Remote UI features to enable you to create debugger visualizers with VisualStudio.Extensibility!

A screenshot of a debugger visualizer for regex matches
A debugger visualizer for regex matches

We can’t wait to see the debugger visualizers you create with this feature!


Interact with users through custom dialogs

With the last release of VisualStudio.Extensibility, you could interact with users through prompts with simple, configurable buttons. Now, you can go beyond buttons and interact with your users through dialogs featuring custom UI.

A screenshot of a custom dialog
A dialog with custom UI

Dialogs are backed by our Remote UI features, so you can use WPF to create rich, interactive visuals, and then you can show your dialog to the user with a simple call:

public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
    using IRemoteUserControl content = new MyDialogControl();
    await this.Extensibility.Shell().ShowDialogAsync(content, "My Dialog", DialogOption.Close, cancellationToken);

Sample code for showing a custom dialog


Query the project system for information about projects and solutions

With the latest preview, we’ve added the ability to query the project system for projects in a solution that match a set of conditions or for information about those projects. For example, you could use this query API to detect that web projects are present in the solution before enabling web-related commands in your extension:

IQueryResults<IProjectSnapshot> webProjects = await Extensibility.Workspaces().QueryProjectsAsync(
    project => project.Where(p => p.Capabilities.Contains("DotNetCoreWeb"))
                      .With(p => new { p.Path, p.Guid }),

Sample code for finding all the projects in a solution with a given set of capabilities.

You could also modify projects, for example by adding a project reference or creating a new file:

await Extensibility.Workspaces().UpdateProjectsAsync(
    project => project.Where(p => p.Name == "MyProject"),
    project => project.CreateFile("NewFile.txt", newFileContents),

Sample code for adding a new text file to a project

With these features, your extension can deliver experiences to your users that feel directly relevant to the code they’re working on!


Add valuable context with editor margin extensions

We know developers spend most of their time in Visual Studio working with files and documents in the editor. With editor margin extensions, you can get your work in front of many users by leveraging Remote UI to create great front-and-center experiences that boost developer productivity. You could use this feature to offer simple at-a-glance features like the word count or the encoding of the open document, but you could also create custom navigation bars or headers!

A screenshot of code to insert an editor margin extension that adds a word count to the bottom editor margin
An editor margin extension that adds a word count to the bottom margin


Configure your extension easily with configuration properties

Many components defined in your extensions require you to configure how or when they appear in the IDE. For example, if you define a command, you could configure it to be a menu item in the Tools menu or an icon on the toolbar. If you define a tool window, you could configure it to be either docked or floating.

Based on feedback from user testing, we’ve completely overhauled extension configuration for better discoverability and usability. Now, you’ll be able to configure your extension with strongly-typed classes and properties, and you’ll have IntelliSense to help you discover and browse predefined options. These changes will help you get your extensions to work like you intend them to.

With the changes, configuration has gone from looking like this with attributes:

	"Run Linter on open file",
	containerType: typeof(MarkdownLinterExtension),
	placement: CommandPlacement.ToolsMenu)]
	new string[] { "FileSelected" },
	new string[] { "ClientContext:Shell.ActiveSelectionFileName=.+" })]
[CommandIcon("MarkdownIcon", IconSettings.IconAndText)]
internal class RunLinterOnCurrentFileCommand : Command

Command configuration using the old configuration attributes

to looking like this with configuration properties:

internal class RunLinterOnCurrentFileCommand : Command
    public override CommandConfiguration CommandConfiguration => new("%RunLinterOnCurrentFileCommand.DisplayName%")
        Placements = new[] { CommandPlacement.KnownPlacements.ToolsMenu },
        Icon = new(ImageMoniker.Custom("MarkdownIcon"), IconSettings.IconAndText),
        EnabledWhen = ActivationConstraint.ClientContext(ClientContextKey.Shell.ActiveSelectionFileName, ".+"),


Command configuration using the new configuration properties


Control the placement of your commands

As seen in the configuration properties above, you can easily place your commands inside known menus, but you can also place them inside new menus and toolbars, which you can create and control:

public static ToolbarConfiguration MyToolbar => new ToolbarConfiguration("%MyToolbar.DisplayName")
    Children = new[]

Sample code for creating a toolbar with a command


Add keyboard shortcuts to your commands

Now you can set keyboard shortcuts for your commands using simple sets of keys and modifiers:

public override CommandConfiguration CommandConfiguration => new("%MyCommand.DisplayName%")
    Shortcuts = new CommandShortcutConfiguration[]
        new(ModifierKey.ControlShift, Key.G),

Sample code for setting a keyboard shortcut for a command


We can’t do this without you!

As we continue to develop VisualStudio.Extensibility, we need your help! Please give Preview 3 a try and share your thoughts with us. You can give feedback, report bugs, make suggestions, and ask questions through our issue tracker. You can also sign up here to be contacted about future user studies, which are invaluable as we work to give you the best possible experience with our SDK.

Please go to the VisualStudio.Extensibility repo for docs, code samples, and setup instructions so you can get started today!