Introduction
I wrote the Copy-To-Html add-in to learn more about extending the Visual Studio IDE – it’s a perfect mix of usefulness and simplicity. This article uses the addin as the foundation to learning about VS.NET’s extensibility capabilities. The original Copy-To-HTML addin was written by Colin Coller and although I’ve copied its name and concept, credit for the idea squarely goes to him (apologies if someone did it before Colin).
Commands in Visual Studio
Before starting the walkthrough there is a concept relating to add-ins that needs to be understood. Add-ins in Visual Studio work using the concepts of commands. Commands, or named commands as they are referred to, are an encapsulation of functionality that can be attached to a menu, keyboard shortcut, or a toolbar button. Menus and toolbars are referred to as Command Bars in the Visual Studio environment and that makes sense, commands are mapped to command bars. If you are familiar with the command design pattern this philosophy should be familiar to you.
The whole of the Visual Studio environment appears to have been developed along this approach. Each action performed in the Visual Studio IDE has an associated command. You can view these commands and their mapping to keyboard shortcuts by selecting Tools->Options->Keyboard. You can also type the commands into the command window to invoke them, note that the command window supports full intellisense. The commands are basically found by the name of the menu that they appear in. For example the Edit->Copy command can be ran by typing Edit.Copy into the command window.
Although they do not need to use the command approach the majority of add-ins you might want to create will follow the named command approach so it’s important to get your head around the idea.
Interfaces of an add-in and they’re methods
All add-ins are required to implement one interface called IDTExtensibility2. This interface defines five methods that are used to set up and tear down the add-in from within the IDE. The methods are:
OnConnection
OnStartupComplete
OnBeginShutdown
OnAddInsUpdate
OnDisconnection
OnConnection is the most important method as it is where you add your own named commands to Visual Studio and where you map them to command bars. The other methods are generally self explanatory but for more information on them see the MSDN documentation.
Another interface that needs to be implemented if the add-in supports named commands is the IDTCommandTarget interface. This defines two methods, which are:
QueryStatus is used by Visual Studio to determine and set the properties of the command based on the Visual Studio environment’s current state. It’s were you place code to disable the commands button and so forth.
Exec is called whenever a command is executed. This method is where you place the code that calls the relevant command for the action performed by the user in Visual Studio.
The QueryStatus and Exec methods for a single add-in are called for each command you might have in your add-in. So although a single add-in may have more than one command it doesn’t have more than one QueryStatus and Exec method. In these methods you need to place a switch/select statement that determines what action was performed and from that decides what command needs to run. You will see this at work in the example add-in.
OnConnection method in more detail
Three things should happen in the OnConnection method; these are and should happen in the order listed.
- Object references to Visual Studio IDE should be set up.
- Custom command(s) are added to Visual Studio command list.
- The custom command(s) are added to a command bar(s).
1. Object references to Visual Studio IDE should be set up
There are two important objects that every add-in should have.
DTE2 is an object that you use to reference the Visual Studio IDE. This object lets you get access to the command bars, the tool windows, the code editors, and other components of the Visual Studio IDE.
Addin is an object that lets you reference the instance of your add-in. It contains information about your add-in that can be used during runtime.
The references to these objects must be set up in the OnConnection method. The first couple of lines of your add-ins OnConnection method should look like this:
//instance of the visual studio enviroment
private DTE2 m_applicationObject;
//instance of the addin
private AddIn m_addInInstance;
public void OnConnection(object application, ext_ConnectMode connectMode,
object addInInst, ref Array custom)
{
m_applicationObject = (DTE2)application;
m_addInInstance = (AddIn)addInInst;
2. Custom commands are added to Visual Studio’s command list
Adding commands to Visual Studio didn’t quite work the way I had first expected, after thinking about what was actually going on it’s a very powerful approach to dynamically creating controls and displaying them at runtime. If I were adding a button to a command bar in Excel my first thoughts would be to create an instance of a control, set some of its properties, and add it to a control collection of the command bar.
The approach the Visual Studio developers took works differently. Instead of coding directly against the command bar, you create a command object, set it’s display properties, and then let the command object add itself to the command bar. The command is responsible for adding itself to the user interface, rather than the user interface knowing how to add the command.
In the Excel example it’s like creating an instance of a control, setting some properties of it, and then passing the command bar to the control so it can add itself to the control collection of the command bar.
This is an excellent and powerful example of using the Command design pattern in such a way that it can dynamically create the user interface controls used to run the command.
To create a command you use the DTE2.Commands.AddNamedCommand method. DTE2 is the reference to the Visual Studio 2005 environment, and is set up on the first calls to OnConnection().
//named command to perform the copy as html
private Command m_copyAsHtmlCmd;
object[] contextGUIDS = new object[] { };
m_copyAsHtmlCmd =
m_applicationObject.Commands.AddNamedCommand(m_addInInstance,
m_NAME_COMMAND, "Copy As Html", "Copies the
selected text formatted as HTML",
true, 52, ref contextGUIDS,
(int)vsCommandStatus.vsCommandStatusSupported+(int)
vsCommandStatus.vsCommandStatusEnabled);
The AddCreateNamed takes the add-in instance, the command name, the caption of the command, a tooltip, a flag indicating if a button is used to fire the command, and an icon for the button. One important thing to note is the command doesn’t contain any references to the code that’s run when the command is used. The command is not mapped to its code until the command is fired through the Exec method, which we’ll look at briefly.
One point worth noting is the icons used by commands are similar to the FaceID icons used by Office Addins. To be able to select a in build icon for you add-ins command I recommend you using one of the many Excel addins available that let you browse these in build icons and see their relevant index.
3. The custom command(s) are added to command bar(s)
The last thing to do during OnConnection is to add the command to a command bar. This is accompllished by, getting a reference to a command bar using the DTE2.CommandBars collection.
In this example a reference to the ‘Code Window’ command bar is used; this is the context menu that’s displayed on a right click in a code editor. There are a few different command bars available and they generally go by their name, for example to add to the Tools menu use the ‘Tools’ command bar.
//get a reference to the context menu displayed
in the code window on a right click
//menu items and tool bar items are all of type command bar
CommandBars cmdBars = (_CommandBars)m_applicationObject.CommandBars;
CommandBar cmdBar = cmdBars["Code Window"];
//adds a control for the command onto the command bar of the right
click context menu
if (cmdBar != null)
{
//read as add control for copy as html command to the context menu
m_copyAsHtmlCmd.AddControl(cmdBar, 1);
}
Remember the command adds itself to command bar so once you get a reference to the command bar pass it to the commands AddControl() method.
The QueryStatus method in more detail
The QueryStatus method is used to change the visual properties of your command, also known as its status, based on the IDE state or any other conditions you need. More likely than not you use this to disable or enable the command. Again a select case/switch statement is used to determine which command is being processed.
public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText,
ref vsCommandStatus status, ref object commandText)
{
switch (commandName)
{
//case sensitive, the command must be named exactly
//Assembly.Class.Command
case "CopyAsHtmlAddin.Connect.copyAsHtmlCommand":
//todo place logic in here that determines if the command button is
enabled or disabled
//use logic here to set the status of the command
status = vsCommandStatus.vsCommandStatusSupported |
vsCommandStatus.vsCommandStatusEnabled;
break;
}
}
The small example above demonstrates how to code within the QueryStatus method. As you can see setting the status value to a vsCommandStatus enumeration will in effect change the visual properties of the command.
The Exec method in more detail
The Exec method is where the code of your add-in goes. It’s important to remember that this method only gets called once even if your add-in supports multiple commands. To determine which code runs for each of the command you need a switch/select statement based on the running commands name. The command name is passed to the Exec method.
public void Exec(string commandName, vsCommandExecOption executeOption,
ref object varIn, ref object varOut, ref bool handled)
{
handled = false;
//not sure what this is
if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
{
if (commandName.ToLower() == "copyashtmladdin.connect.copyashtmlcommand")
{
//perform the action in here
handled = true;
return;
}
}
}
The Exec method must also set a Boolean variable called handled to indicate if the command was processed or not.
Deploying and Add-in
Add-ins has two files which need to be installed. One is the assembly containing the add-ins code while the other is an XML manifest describing the add-in. Add-ins used to only be installed by registering them with COM; the XML manifest replaces that requirement.
<?xml version="1.0" encoding="UTF-16" standalone="no"?>
<Extensibility xmlns="http://schemas.microsoft.com/AutomationExtensibility">
<HostApplication>
<Name>Microsoft Visual Studio Macros</Name>
<Version>8.0</Version>
</HostApplication>
<HostApplication>
<Name>Microsoft Visual Studio</Name>
<Version>8.0</Version>
</HostApplication>
<Addin>
<FriendlyName>CopyAsHtmlAddin</FriendlyName>
<Description>Copies source code/xml to the clipboard
as formatted HTML.</Description>
<Assembly>CopyAsHtmlAddin.dll</Assembly>
<FullClassName>CopyAsHtmlAddin.Connect</FullClassName>
<LoadBehavior>0</LoadBehavior>
<CommandPreload>1</CommandPreload>
<CommandLineSafe>0</CommandLineSafe>
</Addin>
</Extensibility>
This XML describes the addin to Visual Studio and it must be saved with an .Addin extension. You don't generally need to create this file manually. If you select the Addin project type when initially creating your Addin, Visual Studio will, create it for you.
These two files should be copied to the ‘MyDocuments\Visual Studio 2005\Addins’ directory. Once there they become available through the add-in manager in Visual Studio 2005. Installing this way means the add-in is user specific and not shared between all users of Visual Studio 2005.
Summary
To create a Visual Studio addin it’s worthwhile thinking in terms of commands. Implementing two interfaces IDTExtensibility2 and IDTCommandTarget provides the class the methods it needs to act as an Addin.
In the OnConnection method you set up your commands and map them to UI elements in Visual Studio’s IDE. In the QueryStatus method you add logic to enable or disable your command and in the Exec method you add the commands code using a select/switch statement to handle multiple commands.
To install the Addin requires two files, the assembly containing the addin, plus an XML manifest with the .Addin extension. The XML manifest describes the addin to Visual Studio and removes the need to register the Addin with COM.
Download Source Code
CopyAsHtmlAddin
References
Developing Visual Studio .NET Macros and Addins
Jeff Cogswell
Working with Visual Studio 2005
Craig Skibo, Marc Young, Brian Johnston (MSPress)
Frequently Asked Questions About Visual Studio .NET Automation
Kemp Brown
Custom Add-Ins Help You Maximize the Productivity of Visual Studio .NET
Leo A. Notenbooma
Visual Studio Add-In That Converts C# Code To Visual Basic
Scott Swigart
Creating add-ins and Wizards
MSDN
About Derek Smyth
 |
Sorry, no bio is available
This author has published 6 articles on DotNetSlackers. View other articles or the complete profile here.
|
You might also be interested in the following related blog posts
Html Encoding Code Blocks With ASP.NET 4
read more
Mozilla and Microsoft work together on WPF\ClickOnce plugins
read more
Where is C# in the Programming Language Lifecycle?
read more
Telerik announces second Beta release of RadControls for Silverlight/WPF Q3 2009
read more
Adding additional power to RadGridView for Silverlight with attached behaviors
read more
Business Apps Example for Silverlight 3 RTM and .NET RIA Services July Update: Comments about Adding
read more
Telerik Extensions for ASP.NET MVC Troubleshooting
read more
Mixing Silverlight and MS ASP.NET AJAX 3.5 in the same web application.
read more
Formatting Text in RadControls for WinForms Q3 2009
read more
Postgresql - Day 2
read more
|
|
Please login to rate or to leave a comment.