Visual Studio is a very capable and mature IDE, plus there are 5,942 items in Visual Studio Gallery from 3rd parties, extending functionality even further in all possible directions. Still, if it is not written by you it can’t be perfect š
The hardest part when writing a Visual Studio extension is to find the right interface for the task. In this article I will show most “popular” interfaces to help you get started with Visual Studio customization using code. When there is no public API to perform your task, I’ll show possible workarounds.
All samples below are available as commands and extensions for Visual Commander. You can use this code as part of your own extension or you can run it directly with Visual Commander. Generally, for relatively simple functionality Visual Commander helps you write, run, customize and share VS extension code faster than with a full VSIX package. Most of the code will also run unmodified for all Visual Studio versions from VS 2010 to VS 2015 (when creating a full VSIX package you should carefully package it to provide compatibility with different versions of Visual Studio). Visual Commander also provides the common VCmd menu to run and organize multiple commands and extensions.
The most basic task for an extension is to modify selected text in the code editor. Block comment and Insert date and time commands demonstrate access to the current selection as string:
EnvDTE.TextSelection ts = DTE.ActiveDocument.Selection as EnvDTE.TextSelection; ts.Text = "/* " + ts.Text + " */";
You can run existing Visual Studio commands from your code. They are easier to discover than APIs and easier to invoke. See for example Close the current document tab and activate next:
if (IsCommandAvailable("Window.NextTab")) { EnvDTE.Window w = DTE.ActiveWindow; DTE.ExecuteCommand("Window.NextTab"); w.Close(); } else if (IsCommandAvailable("File.Close")) DTE.ExecuteCommand("File.Close");
Another very common requirement is to perform some task automatically on a Visual Studio event. See the list of Common Visual Studio environment events and see Open a file from the solution directory on opening a solution, Run Cppcheck on the saved file samples:
public void SetSite(EnvDTE80.DTE2 DTE_, Microsoft.VisualStudio.Shell.Package package) { events = DTE.Events; documentEvents = events.DocumentEvents; documentEvents.DocumentSaved += OnDocumentSaved; } private void OnDocumentSaved(EnvDTE.Document doc) { if(doc.Language == "C/C++") RunCppcheck(doc.FullName); }
You can create custom commands to programmatically setup different Visual Studio options. For example, Toggle line numbers for C# files and Set font size:
dim v = DTE.Properties("TextEditor", "CSharp").Item("ShowLineNumbers").Value DTE.Properties("TextEditor", "CSharp").Item("ShowLineNumbers").Value = Not v
To analyze code, you can use Visual Studio code model, for example Move the caret to the beginning of the containing function, Copy to the clipboard properties of the selected class in Visual Studio text editor and Copy current file, line, method. Alternatively, you can use Roslyn, for example Create a typed variable from the current method invocation:
Microsoft.CodeAnalysis.CSharp.Syntax.InvocationExpressionSyntax invocationExpressionNode = document.GetSyntaxRootAsync().Result.FindToken(caretPosition).Parent.AncestorsAndSelf(). OfType<Microsoft.CodeAnalysis.CSharp.Syntax.InvocationExpressionSyntax>().FirstOrDefault(); if (invocationExpressionNode != null) { Microsoft.CodeAnalysis.SemanticModel semanticModel = document.GetSemanticModelAsync().Result; Microsoft.CodeAnalysis.IMethodSymbol methodSymbol = semanticModel.GetSymbolInfo(invocationExpressionNode).Symbol as Microsoft.CodeAnalysis.IMethodSymbol; textView.TextBuffer.Insert(invocationExpressionNode.SpanStart, methodSymbol.ReturnType.ToString() + " v = "); }
Visual Studio is a .NET, Windows, WPF application and your code runs as a part of it. If there is no public API for your needs, you can directly enumerate and modify WPF UI controls (Hide Sign in and Feedback smiley buttons in Visual Studio 2013/2015), subscribe to Application events (AutoSave files when Visual Studio loses focus), modify the MainWindow (Add version overlay to the Visual Studio 2015 taskbar icon) and simulate keyboard input with SendKeys (Toggle CodeLens on/off). As a last resort you can use .NET reflection to access internal Visual Studio assemblies.
I hope you are now feeling empowered to spend some time and add a feature to Visual Studio that you always wanted. Don’t hesitate to share your code and ask questions in comments.
Hi Sergey. Thanks for your great tool. My question – is it possible to define some public function that can be used in several commands? I want to write a method that returns the current task description(and that will be often changing) as string and use this text in different commands
Denis,
Yes, in Visual Commander Professional you can create common code modules that can be called from different commands and extensions.
Many thx for fantastic tool ‘Visual Commander’!
Hi Sergey,
I want to get the “Create Function Header” macro from Samples.vsmacros and put it in Visual Commander. My problem is that Visual Studio won’t open the Macro IDE any more. Do you have a way I can cut/paste the function out of the samples file? Do you have an implementation of that function I could use as a sample?
Thanks,
Alan
Alan,
I’ve sent you sample macros from VS 2010 via email.