version that the add-in should be accessible to. The preceding file is making the add-in available to
both Visual Studio 2010 and the Visual Studio Macros 2010 IDE. If you want to make your add-in
accessible to other versions of Visual Studio, simply add additional HostApplication nodes, with
the corresponding version number for that version of Visual Studio (Visual Studio 2008 = 9.0, Visual
Studio 2005 = 8.0). Of course you must make sure that you don’t use features specific to Visual Studio
2010, and remove references to the higher EnvDTE dlls than the lowest version you are supporting.
the connect class
This section looks at the structure of the core class that manages the add - in. The Connect.cs class
(or Connect.vb ) manages the life cycle of the add - in, and you can fi nd a number of methods that
handle the event notifi cations from the IDTExtensibility2 and IDTCommandTarget interfaces that
are implemented by the class.
The IDTExtensibility2 interface exposes handlers for the events raised by Visual Studio that
notifi es the add - in at each point in its life cycle. The following methods form the IDTExtensibility2
interface:
. OnConnection: Called when the add - in is being loaded by Visual Studio.
. OnStartupComplete: Called when Visual Studio has fi nished loading.
. OnAddInsUpdate: Called when the collection of add - ins in Visual Studio has changed.
. O nBeginShutdown: Called when Visual Studio is shutting down.
. O nDisconnection: Called when the add - in is being unloaded by Visual Studio.
The IDTCommandTarget interface exposes handlers for the events of named commands used by the
add - in. The following methods form the IDTCommandTarget interface:
. Exec: Called when a command used by the add - in is called from Visual Studio (such as
when the menu item created under the Tools menu is selected). Visual Studio will pass this
method the name of the command so you can respond accordingly.
. QueryStatus: Called when the status of a command (such as whether or not it is available) is
requested by Visual Studio.
creating a tool window
Now that you have looked at the structure and life cycle of an add - in, it ’ s time to add some
functionality to interact with the Visual Studio IDE and implement some useful behavior. The
sample you work through in this chapter creates a dockable tool window in Visual Studio that
will enable you to place some notes while working in Visual Studio. Unfortunately, the Add - in
Wizard doesn ’ t provide options to help in creating your own tool window (which is one of the more
common requirements when writing add - ins), so you will have to do this yourself. This section takes
you through the steps to do so.
If you are upgrading an add - in from a previous version of Visual Studio, you will
need to add another HostApplication node to the existing .AddIn fi le, with a
value of 10.0 in the Version node so that it will run under Visual Studio 2010.
Despite Visual Studio 2010 having a complete user interface overhaul to use WPF,
unfortunately you still have to use Windows Forms for your tool windows when
creating add - ins. To use WPF for your tool windows, you will have to use
VSPackages in the Visual Studio 2010 SDK when extending Visual Studio instead.
Developing an add - in . 1015
1016 .
chaPter 51 Add -inS
Add a new Windows Forms User Control item to your project, and call it NotesUserControl
(.cs or .vb). Add a RichTextBox control to the user control, name it rtbNotes, set the BorderStyle
property to None, and dock it to fi ll the area of the control.
Now return to the Connect.cs (or .vb) file, and add the following method to it to simplify the
process of creating the tool window:
Vb
Private toolWindow As Window2 = Nothing
Private Function CreateToolWindow(ByVal guid As String,
ByVal windowTitle As String,
ByVal classPath As String) As Object
Dim windowObject As Object = Nothing
Dim windows As Windows2 = DirectCast(_applicationObject.Windows, Windows2)
Dim assemblyLocation As String = Assembly.GetCallingAssembly().Location
toolWindow = DirectCast(windows.CreateToolWindow2(_addInInstance,
assemblyLocation, classPath,
windowTitle, guid,
windowObject), Window2)
Return windowObject
End Function
c#
Private Window2 toolWindow = null;
private object CreateToolWindow(string guid, string windowTitle, string classPath)
{
object windowObject = null;
Windows2 windows = (Windows2)_applicationObject.Windows;
string assemblyLocation = Assembly.GetCallingAssembly().Location;
toolWindow = (Window2)windows.CreateToolWindow2(_addInInstance,
assemblyLocation, classPath, windowTitle, guid, ref windowObject);
return windowObject;
}
A reference needs to be maintained to the user control at the class level because
windows of add-ins are not destroyed/cleaned up during the life cycle of the
add-in — instead they are merely hidden.
You will create the tool window when the menu item in the Tools menu is selected. You are notified
of this in the Exec method, and you’ll notice that the wizard already created the code to respond to
Developing an add-in .
1017
this (although it currently does nothing). Use the following code to create the tool window and have
it displayed in Visual Studio (the code to be added to the method has been bolded):
Vb
Private notesUserControl As NotesUserControl
Public Sub Exec(ByVal commandName As String,
ByVal executeOption As vsCommandExecOption,
ByRef varIn As Object, ByRef varOut As Object,
ByRef handled As Boolean) Implements IDTCommandTarget.Exec
handled = False
If executeOption = vsCommandExecOption.vsCommandExecOptionDoDefault Then
If commandName = "MyNotesTool.Connect.MyNotesTool" Then
’ An ID that uniquely identifies this tool window
Dim windowID As String = "{fb9e4681-681d-4216-9a28-0f09f3528360}"
’ Create the tool window if it hasn’t already been created
If toolWindow Is Nothing Then
notesUserControl = DirectCast(CreateToolWindow(windowID,
"My Notes" , "MyNotesTool.NotesUserControl" ), NotesUserControl )
End If
’ Make the tool window visible if it’s currently hidden
toolWindow.Visible = True
handled = True
Return
End If
End If
End Sub
c#
private NotesUserControl notesUserControl;
public void Exec(string commandName, vsCommandExecOption executeOption,
ref object varIn, ref object varOut, ref bool handled)
{
handled = false;
if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
{
if (commandName == "MyNotesTool.Connect.MyNotesTool")
{
// An ID that uniquely identifies this tool window
string windowID = "{fb9e4681-681d-4216-9a28-0f09f3528360}" ;
// Create the tool window if it hasn’t already been created
if (toolWindow == null)
{
notesUserControl = ( NotesUserControl)
CreateToolWindow(windowID, "My Notes" ,
"MyNotesTool.NotesUserControl" );
}
1018 .
chaPter 51 Add -inS
// Make the tool window visible if it ’ s currently hidden
toolWindow.Visible = true;
handled = true;
return;
}
}
}
As you can see from the code, it’s now a relatively easy process to create the window. You pass an
ID that uniquely identifies this tool window, a window
title, and the qualified name of the user control class to
the CreateToolWindow method you created earlier, and
it handles calling the extensibility model to create the
tool window in Visual Studio.
Now, run your project and select the menu item for the
add-in under the Tools menu. The user control will
display as a tool window (as shown in Figure 51-13),
which you can then move around and dock to the IDE
fiGure 51-13
as if it were any other tool window.
Visual Studio will remember the location of the window (using its unique ID to
store and retrieve these details), so the next time you load Visual Studio the window
will appear where you last placed it (although this only works when the add - in is
not being debugged). However, for it to be displayed when Visual Studio is started,
you will have to create the tool window when the add - in is started (rather than
when its menu item is selected).
accessing the Visual studio automation Model
You can now add your own additional functionality to the tool window (in the user control) such
as loading and saving the text to a text file (if you want) as if you were programming a standard
application. However, this example doesn’t currently demonstrate integrating with the functionality
of Visual Studio and the events it raises, so add a feature to demonstrate this by creating a button to
take selected code from the code editor and insert it into the notes at the current caret position.
To get to the Visual Studio object model from the user control, you’ll have to make the class-level
variable _applicationObject in the Connect class static and expose it publicly by wrapping it in a
property as shown in the following code:
Vb
Private Shared _applicationObject As DTE2
Public ReadOnly Property ApplicationObject() As DTE2
Get
Developing an add-in .
1019
Return _applicationObject
End Get
End Property
c#
private static DTE2 _applicationObject;
public static DTE2 ApplicationObject
{
get { return Connect._applicationObject; }
}
Add a ToolStrip control to the user control with a button that will copy the selected text in the
code editor and insert it into the textbox when clicked. In the event handler for this button, add the
following code:
Vb
Private Sub btnCopy_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles btnCopy.Click
If Not Connect.ApplicationObject.ActiveDocument Is Nothing Then
Dim selection As TextSelection = DirectCast(
Connect.ApplicationObject.ActiveDocument.Selection, TextSelection)
rtbNotes.SelectedText = selection.Text
End If
End Sub
c#
private void btnCopy_Click(object sender, EventArgs e)
{
if (Connect.ApplicationObject.ActiveDocument != null)
{
TextSelection selection =
Connect.ApplicationObject.ActiveDocument.Selection as TextSelection;
rtbNotes.SelectedText = selection.Text;
}
}
This will take the selected text from the active code editor document and insert it at the current
caret position in the rich textbox in the user control. Note that the code will be unformatted (that
is, no syntax coloring) when it’s put into the rich textbox. Alternatively, you can use the following
code to copy the text out of the code editor and paste it into the rich textbox, which would retain
the syntax coloring but lose the existing contents of the clipboard:
Vb
Private Sub btnCopy_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles btnCopy.Click
If Not Connect.ApplicationObject.ActiveDocument Is Nothing Then
1020 .
chaPter 51 Add-inS
Connect.ApplicationObject.ActiveDocument.Selection.Copy()
rtbNotes.Paste()
End If
End Sub
c#
private void btnCopy_Click(object sender, EventArgs e)
{
if (Connect.ApplicationObject.ActiveDocument != null)
{
Connect.ApplicationObject.ActiveDocument.Selection.Copy();
rtbNotes.Paste();
}
}
handling Visual studio events
As a final example, handle an event raised by Visual Studio. You’ll handle the Cut and the Copy
command events (before the command is actually executed), get the selected text from the code
editor, and automatically insert it into the rich textbox.
First, you need to get a reference to the commands whose events you want to capture (the Cut and
Copy commands), and then the command events objects themselves. C# developers will also add an
event handler for the BeforeExecute event for each command.
Vb
Private WithEvents cutEvent As CommandEvents = Nothing
Private WithEvents copyEvent As CommandEvents = Nothing
Private Sub EnableAutoCopy()
’ Enable the event listening for the Cut and Copy commands
Dim cmdCut As Command = Connect.ApplicationObject.Commands.Item("Edit.Cut", 0)
Dim cmdCopy As Command = Connect.ApplicationObject.Commands.Item("Edit.Copy",
0)
cutEvent = Connect.ApplicationObject.Events.CommandEvents(cmdCut.Guid,
cmdCut.ID)
copyEvent = Connect.ApplicationObject.Events.CommandEvents(cmdCopy.Guid,
cmdCopy.ID)
End Sub
c#
private CommandEvents cutEvent = null;
private CommandEvents copyEvent = null;
private void EnableAutoCopy()
{
// Enable the event listening for the Cut and Copy commands
Command cmdCut = Connect.ApplicationObject.Commands.Item("Edit.Cut", 0);
Developing an add-in .
1021
cutEvent = Connect.ApplicationObject.Events.get_CommandEvents(cmdCut.Guid,
cmdCut.ID);
cutEvent.BeforeExecute += new
_dispCommandEvents_BeforeExecuteEventHandler(OnBeforeCutCopy);
Command cmdCopy = Connect.ApplicationObject.Commands.Item("Edit.Copy", 0);
copyEvent = Connect.ApplicationObject.Events.get_CommandEvents(cmdCopy.Guid,
cmdCopy.ID);
copyEvent.BeforeExecute += new
_dispCommandEvents_BeforeExecuteEventHandler(OnBeforeCutCopy);
}
Now you can define the event handler method that will handle the BeforeExecute event for both
commands, extracting the selected text from the code editor and inserting it into the rich textbox:
Vb
Private Sub OnBeforeCutCopy(ByVal guid As String, ByVal id As Integer,
ByVal customIn As Object, ByVal customOut As Object,
ByRef cancel As Boolean) _
Handles cutEvent.BeforeExecute, copyEvent.BeforeExecute
Dim codeWindow As TextWindow = TryCast(
Connect.ApplicationObject.ActiveWindow.Object, EnvDTE.TextWindow)
If Not codeWindow Is Nothing Then
rtbNotes.SelectedText = codeWindow.Selection.Text &
Environment.NewLine & Environment.NewLine
End If
End Sub
c#
private void OnBeforeCutCopy(string guid, int id, object customIn,