Overview

Any modern application these days is
designed with extensibility in mind. This allows developers and even the end
user to replace existing pieces or add their own custom functionality.

When extending an existing application we
have basically two ways of doing this: Macros and plug-ins. Macros are small
interpreted programs created with scripting languages that run within
applications such as Excel and PowerPoint while Plug-ins are compiled
applications created against the application plug-in mechanism and have access
to a rich set of APIs that enables developers to create and build new tools
quickly, without having to start from scratch.

Visual Studio is no exception; it provides
a very rich object model to automate tasks, add new features like project
wizards, tools or even support any kind of compiler.

Introduction

Since version 2005 Visual Studio includes a
code snippet feature. Snippets are small pieces of generic reusable code. They
improve the developer’s efficiency and accuracy because they reduce typing and
help code reuse.

Visual Studio stores your snippets on the
file system ([My Documents]\Visual Studio [Version]\Code Snippets\Visual C#\My
Code Snippets to be exact) using xml syntax. That’s fine but if you are like me
you probably have more than one development machine which make a little more
complex to maintain your snippets synchronized between your computers and
almost impossible to share it with your friends or coworkers. Wouldn’t be great
to have a centralized database so you could have all your snippets available
all the time everywhere? If your answer is yes CodeXchange is for you!

For those of you who don't know CodeXchange
you can visit its website at www.codexchange.org and learn what is
all about. All software is free so this is not some kind of cheap commercial.

Our extension will be a very simple
CodeXchange client based on its public web services located at http://www.codexchange.org/api/v1/CodeXchangeservice.asmx. Because the purpose of this article is building the extension I
have placed all the irrelevant client code on a different assembly which
exposes a Windows Forms 2.0 UserControl that will be hosted on a Visual Studio
tool window.

Background

This extension is an improvement over a
previous addin I wrote for Visual Studio 2005, In this article I will document
the porting effort to Visual Studio 2010 as well as the changes required to take
advantage of the new features introduced in the last iteration of Visual Studio.

In this article we will learn how to:

Use the ‘Visual Studio Package’ project wizard
to create a skeleton extension

Creating the project file step by step

The SDK will install several new project templates
[1]:

Editor Viewport adornment: The editor classifier allows the text of the edit to be interacted
with and changed in a number of ways. The logic behind what text is
changed is totally at the discretion of the extension developer. The
changing of the text can be something as simple as changing the color, to
changing the size, opacity, etc.

Editor Classifier: The editor classifier allows the text
of the edit to be interacted with and changed in a number of ways.

Editor Margin: Similar
to the Viewport Adornment, except the editor is aware of its presence, and
handles it with scrollbars accordingly. Very similar to the WPF DockPanel
in that the content must be placed on the Left, Right, Top, or Bottom.

Editor Text Adornment
Very similar to the editor classifier in that you can
interact with the text of the editor. The result, however, is a WPF
UIElement that is rendered in the IDE. This can be placed anywhere
relative to the location of the text.

Visual Studio Package: is the most complete plugin type, and it lets you extend just
about anything at all in VS.

Let’s get started!

On the File menu, point to New, and then click
Project.

In the New Project dialog box, expand Other
Project Types, and then click Extensibility.

In the Templates pane click Visual Studio Studio
Package.

In the Location box, type the file path for your
new Extension.

In the Name box, type the name for the extension
and then click OK to start the wizard.

Choose your preferred language and click
next, currently extensions can be written using C#, Visual Basic or Visual C++
only.

This is the basic information about our
extension. This information will be used to appear in the about dialog box and
will be persisted on the registry

This is a important step, We will check the
“Menu Command” and “Tool Window” option which will generate the basic skeleton
for adding new menus to Visual Studio as well as floating tool window , by
default the tool window will be hosting a WPF user control , later we will
modify the code to host our Windows Forms UserControl.

After the last step of the Package wizard a
new project will be created and ready for you to customize. When you expand the
‘References’ node you will have an approximate idea about the amount of classes,
interfaces and services Visual Studio expose. Version after version more stuff
is nicely wrapped in .NET friendly types, despite large parts of the IDE are being
rewritten in managed code (like for example the new WPF based code editor) sometimes
Visual Studio shows its COM heritage and requires dealing with COM style
interfaces.

The first thing you should do is edit the
VSIX manifest file which is a xml file describing your extension, thankfully a
nice visual editor will make the process easier. Just double click the
source.extension.vsixmanifest from the Solution Explorer. I won’t go into too much detail about each field.

Most of the properties are self explanatory. If you want to publish your extension on the visual studio gallery you must pay special attention to:

Icon - The image that will be shown in the VS Extension Manager

Preview Image - A preview image that will be shown in the VS Extension Manager

Version - VS will prompt to automatically update (download and install) your extension if this field is incremeted

Adding and managing menus

Our extension will be registering 4
different menu commands:

cmdidCodeXchange : Will be added to the View
-> Other windows menu and will show/hide the CodeXchange tool window

cmdidInsertFromCodeXchange : Will be added to
the ‘Code Window’ context menu and will connect to the online repisitory

cmdidContributeToCodeXchange : Insert from
codexchange

cmdidConnectToCodeXchange : Contribute to
codexchange

To add new menu commands first of all you
must edit the PkgCmdID.cs generated by the project wizard, add a new unique
entry for every command:

Next step will be adding and defining our
menus in the .vsct file. The Visual Studio Command Table (.Vsct) file is a xml
based file that describes the set of command exposed by the VSPackage.In the
solution explorer double click Sand.Services.CodeXchange.VS2010Addin.vsct and
in the <Symbols> section add one <IDSymbol> element for every
command, The value must match those we used in PkCmdID.cs

Now, we need to group 3 of our 4 commands
to show them on the ‘Code window’ context menu, the other command will be shown
in the View->Other windows sub-menu, find the <Groups> section and add
the following:

<Buttonguid="guidCodeXchangeVS2010AddinCmdSet"id="cmdidCodeXchange"priority="0x0100"type="Button"><Parentguid="guidSHLMainMenu"id="IDG_VS_WNDO_OTRWNDWS1"/><Iconguid="guidImages"id="bmpPic1"/><Strings><CommandName>cmdidCodeXchange</CommandName><ButtonText>CodeXchange</ButtonText></Strings></Button><Buttonguid="guidCodeXchangeVS2010AddinCmdSet"id="cmdidInsertFromCodeXchange"priority="0x0100"type="Button"><Parentguid="guidCodeXchangeVS2010AddinCmdSet"id="MyMenuGroup"/><Iconguid="guidImages"id="bmpPic2"/><Strings><CommandName>cmdidInsertFromCodeXchange</CommandName><ButtonText>Insert snippet from CodeXchange</ButtonText></Strings></Button><Buttonguid="guidCodeXchangeVS2010AddinCmdSet"id="cmdidContributeToCodeXchange"priority="0x0100"type="Button"><Parentguid="guidCodeXchangeVS2010AddinCmdSet"id="MyMenuGroup"/><Iconguid="guidImages"id="bmpPic3"/><Strings><CommandName>cmdidContributeToCodeXchange</CommandName><ButtonText>Contribute snippet to CodeXchange</ButtonText></Strings></Button><Buttonguid="guidCodeXchangeVS2010AddinCmdSet"id="cmdidConnectToCodeXchange"priority="0x0100"type="Button"><Parentguid="guidCodeXchangeVS2010AddinCmdSet"id="MyMenuGroup"/><Iconguid="guidImages"id="bmpPic4"/><Strings><CommandName>cmdidConnectToCodeXchange</CommandName><ButtonText>Connect to CodeXchange</ButtonText></Strings></Button>

And finally map our command in C# code so
we can enable or disable them depending on the extension state and handling its
execution:

Not all commands will be always available,
for example, “insert from codexchange” does not make sense if we are not really
connected to the online database. To enable/disable a command we will use the BeforeStatusQueryCallback,
this callback is executed every time the IDE requests the current state of the
command.

privatevoid BeforeStatusQueryCallback(object sender, EventArgs e)
{
OleMenuCommand command = sender as OleMenuCommand;
if (command.CommandID.ID == (int)PkgCmdIDList.cmdidCodeXchange)
{
// This command is always available
command.Enabled = true;
}
if (command.CommandID.ID == (int)PkgCmdIDList.cmdidInsertFromCodeXchange)
{
// This command is only available if we are logged to the online repository
command.Visible = command.Enabled = toolwndCodeXchange.CodeXchange.IsConnected;
}
if (command.CommandID.ID == (int)PkgCmdIDList.cmdidConnectToCodeXchange)
{
// This command is only available if we are logged to the online repository
command.Visible = command.Enabled = !toolwndCodeXchange.CodeXchange.IsConnected;
}
if (command.CommandID.ID == (int)PkgCmdIDList.cmdidContributeToCodeXchange)
{
// This command is only available if we are logged to the online repository// and some text is selected on the active editorif (toolwndCodeXchange.CodeXchange.IsConnected)
{
//check if any text is highlighted or disable the menuif (GetSelectedText().Length >0)
command.Enabled = true;
else
command.Enabled = false;
}
else
{
command.Enabled = false;
}
}
}

Manipulating the source code editor

CodeXchange is about inserting code from the online repository to our local source code files. When the user wants to paste a snippet to the currently opened document we will open a new UndoContext. UndoContexts are a group of operations represented as a single transaction. Once we have the undo context opened we will create a new EditPoint and insert the code. Once we are done we will automatically indent the code with the SmartFormat method and the last step will be closing our previously created UndoContext.

Creating the tool window

The project wizard has created a class
inheriting from ToolWindowPane which is the base class for tool windows inside
the IDE. We will modify the constructor to create a new instance of our
CodeXchange user control and will return an instance of it by overriding the Window
public property from the base class ToolWindowPane.

We are done; remember that tool windows are
loaded by demand, an instance of our control will be created the first time the
user requires the tool window to be shown.

Adding toolbox items to the toolbox

Having access to your snippet list by using
the CodeXchange tool window is the way the extension was designed to be used,
in fact is the only way to do things like preview, delete or edit your
published code snippets, tool windows are great but sometimes we hide them to
save space. To address this situation we will be adding shortcuts to the
snippets inside the toolbox. The user should be able to insert any of them just
by dragging it on the source file.

Note: The CodeXchange Snippets toolbox
strip will be only visible when the active document is a text based editor.

Testing and debugging your Visual Studio
2010 extension

The first thing you will notice when you
try to run your newly created project is that a new instance of Visual Studio
is launched. Your extension will be automatically installed into this new
instance called "Experimental instance" which is basically an
isolated copy of Visual Studio with its own extensions, settings etc ... This
will allow you to test your project without breaking your main development
environment. The debugging session will end as soon as you close it.

Packaging your extension as a VSIX

In previous versions of Visual Studio
addins were installed from a msi installation package. Visual Studio 2010
introduces a new techonology for deploying packages called VSIX. A VSIX file is
just a standard zip file with the extension (.vsix) that conforms to the Open
Package Convention http://msdn.microsoft.com/en-us/magazine/cc163372.aspx
. It contains the extension and all the metadata, resource
files, images and manifest files required by Visual Studio to install and
manage the extension. When you build the project Visual Studio will generate it
for you.

Sharing your extension in the Visual Studio
gallery

Sharing your extension is as easy as
publishing the .vsix file we just created, to install your extension the user
will have to double click and follow the wizard. Visual Studio 2010 includes a
new feature called ‘Extension manager’ which will help you to install, update
and remove extensions with ease. Extensions can also be downloaded from within
the IDE using the new visual studio gallery.

Implement a local storage as a snippet cache
that allows working offline

Conclusions

Visual Studio 2010 is a big improvement
over previous versions. With its huge and mature extensibility model provides even
greater opportunities for creating rich powerful extensions and tools.

The CodeXchange sample client presented
with this article does not pretend to implement the complete command set
providing instead support for the basic features. You can use this code to
create your own customized client or use it as reference and guide for your own
projects. Have fun!

History

* Initial draft (V1.0) 3/10/2010

Acknowledgments

Thanks to DotNetPark for generously hosting the codexchange
website and database

Final notes

English is not my native language, so please
excuse any mistakes. This is my first article for CodeProject, so any
suggestions and/or feedback will be appreciated. Thanks for reading till here!
If you can add anything or have suggestions and/or tips, please post a message
below and if you like, don't forget to vote for it. :)

Comments and Discussions

Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machinekey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Web.HttpException: Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machinekey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.