Building your own Visual Studio Source Code Outliner extension
I’ll be giving my first Visual Studio Extensibility talk at the Seattle Code Camp on January 27, 2008.
My introduction to Visual Studio talk will be a 15 to 20 minute demo on how to build a real VS Tool Window. I plan on creating a smaller version of the Source Code Outliner that’s available on https://codeplex. The Source Code Outliner version I’m creating will show a tree view of the functions and classes in a .NET source file. When you double click on a node in the tree view, it’ll highlight the function or class name and take you to that line of code. A status message will also appear in the output window.
The VB code for this demo was provided by Aaron Marten, one of the developers on the Visual Studio Extensibility team. He gave the same demo at DevTeach in Vancouver late last year. I’ve ported the VB code to C# and will include the C# snippets in this blog. However, the demo itself (and this blog) will be VB focused.
This blog post is going to walk you through step by step on how to build the add-in. I plan on using this as a reference for anyone attending the Seattle Code Camp that wants to try and build this themselves.
Prerequisites:
1) Install Visual Studio 2008. If you don’t have a copy, you can download the trial edition from here
2) Install the Visual Studio 2008 SDK from here
Stage 1 – Create the framework for the tool window
This is pretty easy as you just need to walk through the wizard.
1) Launch Visual Studio 2008
2) In the file menu, select File -> New -> Project
3) Navigate to the Other Project Types -> Extensibility project
4) Select the Visual Studio Integration Package
5) Type in the Name, Location, and Solution name you want to use (or leave the defaults)
6) Click OK
7) The Visual Studio Integration Package Wizard will launch. Click Next
8) Select the language. For this walkthrough (and my demo), I’ll be using Visual Basic. Click on the Visual Basic radio button. (Select C# if you plan on using the C# sample code below)
9) Click Next
10) Edit the company name, package name, and version information to anything you want.
11) Leave the Minimal version as Standard (although you can also just use Pro) and add some detail information.
12) Click Next
13) In the VSPackage Options page, check Tool window
14) Click Next
15) Enter a window name. I used “SouceCodeOutliner”
16) Feel free to update the Command ID or leave it as the default
17) Click Next
18) Uncheck the test projects. You can leave them but for the demo, I left them unchecked as they were beyond the scope of what I wanted to show.
19) Click Finish
At this point, you’ve created a basic Tool window. If you hit F5 at this point, you’ll launch a second instance of Visual Studio which uses the experimental hive. The experimental hive is a copy of the registry keys that allows you to use Visual Studio in an environment that doesn’t affect or interfere with your current Visual Studio installation. You can always reset your experimental hive by going to the short cut for the Visual Studio 2008 SDK Tools in your start menu.
You’ll also notice several files have been added to the project. Here’s a quick description of the four important files (assuming you left the default project name):
MyControl.vb |
This is the design view and code view for your control |
MyToolWindow.vb |
This is the code for creating the tool window itself |
VSPackage#.vsct |
This is the XML file containing the definition on where the command shows up in the file menu |
VSPackage#Package.vb |
This is the file that contains the implementation for the IVSPackage class to make it a real VS Package. |
For those using C#, replace .vb above with .cs in the above table and throughout this blog.
To view your tool window, in the second instance of Visual Studio, go to the View Menu -> Other Windows. You’ll see your Source Code Outliner command there. Selecting that will bring up a tool window which you can float around and dock within Visual Studio.
Stage 2 – Add some UI
1) Open MyControl.vb form view
2) Delete the default button
3) Add a new button and set the Anchor to Top, Left, Right. This can be done in the properties window.
4) Add a new TreeView control and set the Anchor to Top, Left, Right, Bottom.
Stage 3 – Add a reference to the DTE (Designer Tools Environment) Object. This is the automation object within Visual Studio.
1) Open the code view for MyControl.vb
2) In the file menu, select Project -> Add Reference
3) Select EnvDTE
a. There’s a bug that two copies of it shows up.
4) At the top of the form, add the following import statements
VB Code |
Imports EnvDTE Imports Microsoft.VisualStudio.Shell Imports Microsoft.VisualStudio Imports Microsoft.VisualStudio.Shell.Interop |
C# Code |
using EnvDTE; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Shell.Interop;
|
5) In the MyControl class, below the “Inherits UserControl” statement add the following code:
VB Code |
Public dte As DTE Public toolWindow As ToolWindowPane |
C# Code |
public DTE dte; public ToolWindowPane toolWindow;
|
Stage 4 – Get the DTE Service
1) Open the MyToolWindow.vb file
2) In the New() function, add the following lines of code after the line “control = New MyControl()”
VB Code |
control.dte = CType(Microsoft.VisualStudio.Shell.Package.GetGlobalService(GetType(Microsoft.VisualStudio.Shell.Interop.SDTE)), EnvDTE.DTE) control.toolWindow = Me |
C# Code |
control.dte = (EnvDTE.DTE)Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(EnvDTE.DTE)); control.toolWindow = this;
|
Stage 5 – Add an event handler for the button
1) Go back to the form view for MyControl.vb and double click on the button
2) In the event handler that gets generated, add the following code to clear the tree and get access to the document that’s loaded
VB Code |
TreeView1.Nodes.Clear() Dim fcm As FileCodeModel = dte.ActiveDocument.ProjectItem.FileCodeModel
|
C# Code |
treeView1.Nodes.Clear(); FileCodeModel fcm = dte.ActiveDocument.ProjectItem.FileCodeModel;
|
Stage 6 – Add a class definition for the CodeElementNode
1) Go to the code view for MyControl.vb
2) We need to extend the Treenode class to get access to its properties. Add the following class below the MyControl class
VB Code |
Public Class CodeElementNode Inherits TreeNode Public CodeElement As EnvDTE.CodeElement End Class |
C# Code |
public class CodeElementNode : TreeNode { public CodeElementNode() { } public EnvDTE.CodeElement CodeElement; }
|
Stage 7 – Add methods to recurse over the CodeMembers
1) Go to the code view for MyControl.vb
2) In the MyControl class, add the following methods to recurse over the Code Members. I’ve added comments above the methods to describe more details what the method is doing. At a high level, the code is iterating through the code model to find a class or function, then adding it to the tree view.
VB Code |
' Recurse through the code model ' Get elements and add to the child of the current node. ' We only care about classes and functions ' Once we find the class or function, insert a node, ' then recurse its children Private Sub RecurseCM(ByVal elements As CodeElements, ByRef nodes As TreeNodeCollection) If elements IsNot Nothing Then For Each element As CodeElement In elements If (element.Kind = vsCMElement.vsCMElementNamespace _ Or element.Kind = vsCMElement.vsCMElementClass _ Or element.Kind = vsCMElement.vsCMElementFunction) Then Dim node As New CodeElementNode() node.CodeElement = element node.Text = element.Name nodes.Add(node) RecurseCM(GetMembers(element), node.Nodes) End If Next End If End Sub ' We can't call element.members since members is not defined in the code elements class ' We need to cast to a specific type and then refer to the member on that derived type Private Function GetMembers(ByRef element As CodeElement) As CodeElements If Not element Is Nothing Then Select Case element.Kind Case vsCMElement.vsCMElementNamespace Dim ns As CodeNamespace = CType(element, CodeNamespace) Return ns.Members Case vsCMElement.vsCMElementClass Dim cls As CodeClass = CType(element, CodeClass) Return cls.Members End Select End If Return Nothing End Function
|
C# Code |
private void RecurseCM(CodeElements elements, TreeNodeCollection nodes) { if (elements != null) { foreach (CodeElement element in elements) { if (element.Kind == vsCMElement.vsCMElementClass || element.Kind == vsCMElement.vsCMElementNamespace || element.Kind == vsCMElement.vsCMElementFunction) { CodeElementNode node = new CodeElementNode(); node.CodeElement = element; node.Text = element.Name; nodes.Add(node); RecurseCM(GetMembers(element), node.Nodes); } } } } private CodeElements GetMembers(CodeElement element) { CodeElements members = null; if (element != null) { switch (element.Kind) { case vsCMElement.vsCMElementNamespace: members = ((CodeNamespace)element).Members; break; case vsCMElement.vsCMElementClass: members = ((CodeClass)element).Members; break; } }
return members; }
|
Stage 8 – Add a function call to RecurseCM
1) We need to now call the code to recurse through the code model. In the button event handler, add the following code:
VB Code |
RecurseCM(fcm.CodeElements, TreeView1.Nodes) TreeView1.ExpandAll()
|
C# Code |
RecurseCM(fcm.CodeElements, treeView1.Nodes); treeView1.ExpandAll(); |
Stage 9 – Add functionality to go to the line of code when a user double clicks on it in the tree view
1) In the design view for MyControl.vb, add an event handler for NodeMouseDoubleClick
2) Add the following code in the event handler:
VB Code |
Dim node As CodeElementNode = CType(e.Node, CodeElementNode) Dim doc As TextDocument = node.CodeElement.StartPoint.Parent doc.Selection.MoveToPoint(node.CodeElement.StartPoint) doc.Selection.SelectLine() |
C# Code |
CodeElementNode node = (CodeElementNode)e.Node; TextDocument doc = node.CodeElement.StartPoint.Parent; doc.Selection.MoveToPoint(node.CodeElement.StartPoint,false); doc.Selection.SelectLine();
|
Stage 10 – Add text to the Output Window
1) Every time someone double clicks on a node, we want to also write a message to the output window. In the NodeMouseDoubleClick event handler, add the following code to get a reference to the Output Window pane and write to it.
VB Code |
Dim generalPane As IVsOutputWindowPane = VsShellUtilities.GetOutputWindowPane(CType(Me.toolWindow.Package, System.IServiceProvider), VSConstants.GUID_OutWindowGeneralPane) generalPane.OutputString("Navigating to " + node.Name + node.Text + System.Environment.NewLine)
|
C# Code |
IVsOutputWindowPane generalPane; generalPane = VsShellUtilities.GetOutputWindowPane((System.IServiceProvider)this.toolWindow.Package, VSConstants.GUID_OutWindowGeneralPane); generalPane.OutputString("Navigating to " + node.Name + node.Text + System.Environment.NewLine); |
At this point, your tool window is complete! You can press F5 to start debugging through your new tool window. In the View -> Other Windows menu, you can see your Source Code Outliner command. Select that to see your tool window. You can dock the window, drag it around, and do anything to it that all tool windows support.
To simplify this demo, I didn’t add any error handling. Make sure you have a VB or C# code file loaded in the IDE of your experimental version of Visual Studio before clicking the button on your tool window.
If it’s your first time launching VS in experimental mode, you will be prompted and asked what type of developer are you? This happens every time you reset the experimental hive.
To see the full version of the Source Code outliner, go here.
To see more “How To” Videos to extend Visual Studio, go here.
To learn more about Visual Studio Extensibility, go here.
Comments
Anonymous
January 27, 2008
The comment has been removedAnonymous
January 28, 2008
My last blog post was a step by step walkthrough on how to build a simple version of the source codeAnonymous
January 28, 2008
My last blog post was a step by step walkthrough on how to build a simple version of the source codeAnonymous
February 07, 2008
Quan is a new (and, I should say, very productive) PM on the VS Ecosystem Team. He's now started bloggingAnonymous
February 07, 2008
Quan is a new (and, I should say, very productive) PM on the VS Ecosystem Team. He's now startedAnonymous
March 11, 2008
Last night, Ken Levy and I presented another VSX talk at the .NET Developer Association weekly meetingAnonymous
March 11, 2008
Last night, Ken Levy and I presented another VSX talk at the .NET Developer Association weekly meetingAnonymous
April 02, 2008
LinqToCodeModel is now on available on Code Gallery . LinqToCodeModel, created by Pablo Galiano of Clarius,Anonymous
November 21, 2011
Can this be updated for visual Studio 2010?