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 http://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.