Extending Visual Studio
This article is part of the series 'Extending Visual Studio'.
Part 1 - Creating Code Snippets
Part 2 - Creating Addins
Part 3 - Item Templates
Introduction
This is the second in my series of articles on extending Visual Studio. In this
article we're going to look into a way to extend Visual Studio that has been
around for a while - addins.
We'll create a very funky addin called 'Switch'. This addin will allow us to
switch between related files - C++ header and source, WinForms code and
designer, XAML and code-behind and so on.
In this article we'll start with one of the simplest ways to extend Visual
Studio - creating Code Snippets.
What is an Addin?
An Addin is a DLL that Visual Studio loads into memory to perform a task or
present functionality to the user. What is most useful about an addin, in terms
of development, is that we have access to the DTE2 object. DTE2
is the top-level object in the Visual Studio Automation Model. This is a set of
interfaces and objects that you can use to interact with Visual Studio.
So what can we do with DTE2? Here are some examples:
- Access and invoke the commands visual studio offers.
- Perform builds
- Go through the items in a solution
- Add controls to the user interface
- ...and much more...
Essentially with the Visual Studio Automation model, if Studio can do it, so can
you.
The Brief
So let's look at the brief for this addin project. What do we want to do?
First we can outline our requirements.
- Switch should add a command named 'Switch' to Visual Studio 2008 or Visual
Studio 2010 that we can invoke with the mouse or keyboard.
- Switch should switch between a C or CPP source file and it's header, and vice versa.
- Switch should switch between the Code View and Design View of a C# form.
- Switch should switch between the XAML and Code Behind of a WPF/WP7/Silverlight XAML file.
- Switch should have an installer to make deployment straightforward.
We can get all of the functionality required for the first four items from a
Visual Studio addin - and we can use a standard Deployment project for the fifth
item. So without further delay, let's get started. As an aside, all of the
screenshots and code in this article will be based on Visual Studio 2010. The
source code actually has the same addin for 2008 as well - however the code is
essentially the same (main classes are added as links in both projects, so the
functional code is in fact exactly the same).
Creating the Snippet
Kick of Visual Studio 2010 and choose File > New > Project...
We'll create a new Addin project, which is in the 'Extensibility' group of 'Other
Project Types'.

Now we must specify our project settings. The ones we need to be careful about
are - ensure you're using C# or VB, make sure that we choose Visual Studio as
the host, but not Visual Studio Macros and make sure you choose 'Yes, create a
tools menu item'.
Now that we have created the project, we have a good starting point for our
addin. The most key file that we have created is 'Connect.cs' - this is what is
actually doing the work when the addin is loaded.
The Connect Class
The Connect class is the class that handles the integration of the addin into
Visual Studio. So let's take a look at it in a bit more detail.

Connect
The constructor, here we can perform any initialisation we may need to do.
However, in general try and defer anything complex to one of the later
functions.
Exec
This function is called to actually execute the addin command. An addin may
actually add many commands to Visual Studio, this function will be used to make
sure we perform the correct behaviour.
OnAddinsUpdate
When the set of Visual Studio Addins is changed, this function will be called.
OnBeginShutdown
This function is called when Visual Studio is about to be closed.
OnConnection
This function is called when the addin is being loaded. It is in this function
that we add the 'Switch' command to the Tools menu.
OnDisconnection
This function is called when the addin is being unloaded.
OnStartupComplete
When the host application (Visual Studio) is fully loaded, this function is
called.
QueryStatus
Visual Studio will call this function to see if the addin should be shown,
whether it should be enabled or disabled and so on.
So all together, the Connect class is not very complicated - it has to do some
work to add the Switch command to the appropriate menu, but asides from that
it's very basic. It has as member variables the addin instance, and the
_applicationObject - this is the DTE2 class that allows us to interface with
Visual Studio.
To keep things clean, we will create a new class called 'Switcher' which will
perform all of the switching functionality. This class will be a singleton, so
all we need to do in preparation is modify the Exec function, as below:
public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
{
handled = false;
if(executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
{
if(commandName == "Switch.Connect.Switch")
{
Switch2010.Switcher.Instance.Switch(_applicationObject,
_applicationObject.ActiveDocument);
handled = true;
return;
}
}
}
This has put us into a good place. All we need to do now is create a singleton that
switches the active document and we've got the core functionality we need.
The Switcher Class
Let's now create a class that'll actually take a document and attempt to switch
it to a related document. We'll call this class 'Switcher'. I've implemented it
as a singleton, it could just be a class with static methods, it's really down
to personal preferences. The first thing we can do is make it 'singleton-ish' by
having a private static instance, a static property to get the instance, and a
private constructor to do anything that it's nice to delay:
public class Switcher
{
private Switcher()
{
}
private static Switcher instance = null;
public static Switcher Instance
{
get
{
if (instance == null)
instance = new Switcher();
return instance;
}
}
This is the basic layout for a singleton. This is not an article on the singleton
pattern, I'm just using it so that from here on I don't have to bloat the code
in the article with 'static' modifiers, which should make it clearer. Look at
Martin Lapierre's excellent article on generic singletons in C# for a really
good article on singletons -
Generic Singleton Pattern using Reflection in C#.
The first thing the switcher will do is try and work out if the active document
has a designer - and if it does try and toggle between the code and design view.
Let's put some functions together to do this. The first will try and determine
whether the file has a designer.
private bool DoesDocumentHaveDesigner(string path)
{
string designerPath = Path.Combine(Path.GetDirectoryName(path),
Path.GetFileNameWithoutExtension(path)) + ".designer.cs";
return File.Exists(designerPath);
}
This is a slightly clunky way of checking whether a solution item has a designer
- is there a file of the same name that ends in 'designer.cs'. Determining
whether a document can have a designer is very hard in Studio (but not as hard
as determining whether a window IS a designer) so this will do the trick with
the minimum amount of effort. Now we need a function to try and toggle between
the code and design view.
private void TryToggleCodeDesignView(Document activeDocument)
{
if (activeDocument.ActiveWindow.Caption.Contains("[Design]"))
activeDocument.ProjectItem.Open(vsViewKindCode).Activate();
else
activeDocument.ProjectItem.Open(vsViewKindDesigner).Activate();
}
This is the real fudge. There is no programatic way (that I have found) to
determine whether the active document is open in the designer or not. However,
designer windows have the text '[Design]' at the end of the title. We can check
to see if we are in a designer window, and then re-open the project item in the
code view (or vice versa). This is the first thing we must note for testing and
further development - in other languages this trick may not work!
These functions will let us switch between code and designer, but what about
switching between cpp/h or xaml/xaml.cs files? To do this, we'll create a class
called 'SwitchTarget'. A SwitchTarget will just represent what we go From, to
what we go To. If the document path ends in 'From', we'll try and find a
document that ends in 'To' and open it. We can then create as many switch
targets as we like - and even potentially chain them so that we can switch
between three or more documents in order.
Here's the SwitchTarget class - it stores the From and To and can map a path that
ends in From to one that ends in To:
public class SwitchTarget
{
public SwitchTarget()
{
}
public SwitchTarget(string from, string to)
{
From = from;
To = to;
}
public string MapPath(string path)
{
if (path.Length < From.Length)
return null;
return path.Substring(0, path.Length - From.Length) + To;
}
public string From
{
get;
private set;
}
public string To
{
get;
private set;
}
}
Now we can add a list of SwitchTargets to the Switcher class, and set them up in
the constructor. Change the constructor of Switcher to the code below, and add
the collection:
private List<SwitchTarget> switchTargets = new List<SwitchTarget>();
private Switcher()
{
switchTargets.Add(new SwitchTarget("c", "h"));
switchTargets.Add(new SwitchTarget("cpp", "h"));
switchTargets.Add(new SwitchTarget("h", "c"));
switchTargets.Add(new SwitchTarget("h", "cpp"));
switchTargets.Add(new SwitchTarget("xaml", "xaml.cs"));
switchTargets.Add(new SwitchTarget("xaml.cs", "xaml"));
}
Now we have a way to define what we switch from, to what we switch to. With this,
we can now build the final Switch function:
public void Switch(DTE2 application, Document activeDocument)
{
if (DoesDocumentHaveDesigner(activeDocument.FullName))
{
TryToggleCodeDesignView(activeDocument);
}
List<SwitchTarget> targets = new List<SwitchTarget>();
foreach (var target in switchTargets)
if (activeDocument.FullName.EndsWith(target.From))
targets.Add(target);
foreach (var target in targets)
if(TryOpenDocument(application, target.MapPath(activeDocument.FullName)))
break;
}
And that's it! The project includes a StringExtensions.cs file that includes an
extension method named 'EndsWith', in case you are wondering where this function
comes from! The only function that we haven't got is 'TryOpenDocument' - so here
it is. Remember that there's no guarentee the switch target will exist, so we
can only try to open it - we can't guarentee.
private bool TryOpenDocument(DTE2 application, string path)
{
foreach(Document document in application.Documents)
{
if(string.Compare(document.FullName, path) == 0)
{
if (document.Windows.Count > 0)
{
document.Activate();
return true;
}
}
}
if (File.Exists(path))
{
try
{
application.Documents.Open(path, "Text", false);
return true;
}
catch
{
}
}
return false;
}
We've got the core functionality done - if you hit F5 you can run up Visual
Studio and switch between related documents.
Building an Installer
An installer for an addin is very trivial - just make sure that the *.addin file
doesn't have a path to the assembly, just the assembly name, then put the addin
file and the assembly in the folder 'My Documents/Visual Studio 2010/Addins'.
Here's a screenshot of how the MSI File System screen should look (the main
project actually supports Visual Studio 2008 as well, that's why there's more
folders!)

Using a Custom Icon for the Command
If you've been following the code and building your own project, you'll notice
that the command has a big smiley face for an icon. While this is very jolly, we
may want to use our own icon. This is a rather tricky thing to do as it turns
out, so I have left this to the end.
How is the Icon set?
Take a look at the OnConnection function of the Connect object - the key line is
below:
Command command = commands.AddNamedCommand2(_addInInstance,
"Switch",
"Switch",
"Switch between related files",
true, 59, ref contextGUIDS,
(int)vsCommandStatus.vsCommandStatusSupported+
(int)vsCommandStatus.vsCommandStatusEnabled,
(int)vsCommandStyle.vsCommandStylePictAndText,
vsCommandControlType.vsCommandControlTypeButton);
The boolean parameter set to true tells the environment that the following value
(59) is the index of the visual studio icon to use. There are thousands to
choose from, this article will show you how to see them all: http://msdn.microsoft.com/en-us/library/aa159658(office.11).aspx.
However, what if we don't want to use an icon from this set but our own? Here's
how we do that. First, add a new resources file to the solution, named it
Resources.resx and delete the associated designer file. Your 16x16 icon bitmap
to the solution. If you need to have a transparent part of the icon, use the
color RGB(0, 254, 0). The screenshot below shows the switch icon with the
transparent special colour:

Now add the icon to the resources file, and ensure it has the name '1'. The resources code should have an entry like this:
<data name="1" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\Switch.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
Make sure the build action is set to 'no action' - we'll build the satellite
assembly ourselves. The resources must be built in a satellite assembly or
Visual Studio won't pick up on them. We can build the satellite with the
commands below:
"C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\resgen.exe" Resources.resx
"C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\al.exe" /t:lib /embed:Resources.resources /culture:en-US /out:Switch.Resources.dll
Now when we deploy the Switch assembly - we must make sure the
Switch.resources.dll is in a subfolder named 'en-US' or Visual Studio won't pick
up on it! There are batch files in the download to perform these commands.
The final thing to do is add the .vscontent and .snippet file into a new *.zip
file and rename it from zip to vsi. This creates a vsi installer - double click
on it and you get the below:
Final Thoughts
Now that we have a Switch addin, we can add it anywhere to the Visual Studio UI
and bind a keyboard command to it. In my setup I have Switch super-accessible at
the right of the main menu and bind it to Ctrl+Alt+S. Now whether I'm in C++
code, XAML or a WinForms project I can flick between related files.
There are loads of potential improvements to the project - making switching
configurable, fixing the fudges that won't work in other languages and so on -
but a basic project like this is a great way to get started with Visual Studio
Extensions.
I hope you have enjoyed this article and found it useful, keep up to date with my writing
by following my blog at www.dwmkerr.com -
as always any suggestions are welcome.
Update History
26/08/2012 - Updated the Switch Setup download with support for Visual Studio 2012.