Chapter 54. Introducing Visual Studio Extensibility

Visual Studio is with no doubt a great application, offering hundreds of integrated tools that cover hundreds of aspects of the development experience. It is also a complex and composite application, made of many components. For example, each tool window is a single component developed separately and then put together with the rest of the environment. Developing Visual Studio components and then putting them together is something made possible because of Visual Studio Extensibility. This means that Visual Studio is an extensible application and that other developers, like you and me, can build their own components to put together within the IDE. Although, Visual Studio offers hundreds of tools that covers many developmental needs, it cannot cover all possible requirements. One of the biggest benefits inside the Visual Studio development environment is your ability to customize it with additional tools, windows, and items that can make your developer life easier. In this chapter, you get started with the Visual Studio 2012 extensibility by building custom components like add-ins and extensions.

Introducing Visual Studio Extensibility

Since previous versions, Visual Studio has always been an extensible environment. This means it can be extended and enhanced with additional tools, windows, add-ins, packages, and macros to increase your productivity with specific instruments you might need for adjusting the environment to your developer needs. Behind the scenes, Visual Studio is a mixed-mode application, which means it is built on both COM and .NET architectures (although in 2008, 2010, and 2012 versions, the managed architecture plays a bigger role than in the past).

Visual Studio takes advantage of several .NET assemblies whose names begin with Microsoft.VisualStudio.XXX.dll, where XXX stands for a particular environment area, for maintaining its infrastructure. These assemblies expose a lot of namespace whose names begin with Microsoft.VisualStudio and play an important role in the IDE extensibility because the developer can build components referencing those assemblies to get access to IDE functionalities and extend these functionalities with custom packages or add-ins. As you can imagine, this opens to interesting development scenarios. Building extensions for Visual Studio can be an important business, and several companies build extensions for Visual Studio. But this is what developers could do with Visual Studio until the 2008 version. With Visual Studio 2010, Microsoft completely revisited the IDE architecture and infrastructure so the environment can be extended in further ways other than classic add-ins. This has been retaken and enhanced in Visual Studio 2012. Let’s learn the important concepts about extensibility before showing practical examples.

About Extensibility with Visual Studio 2012

The IDE was completely revisited in Visual Studio 2010, and Visual Studio 2012 retakes most of its infrastructure. Several areas rely on Windows Presentation Foundation, which brought many improvements to the developer experience. The most evident area affected by this infrastructure is the code editor, entirely built upon WPF. Visual Studio 2012 enables extending the code editor with specific WPF objects that can actually enrich the code editor with useful or just attractive extensions. Another key concept is how the IDE infrastructure is built. The extensibility architecture existing until Visual Studio 2008 was replaced in Visual Studio 2010 with the Managed Extensibility Framework (or just MEF), a set of .NET libraries that favors building extensible applications with composition techniques according to a plug-in model. MEF is now an integral part of .NET Framework 4.5 and you can use it to build your own extensible applications. After this brief introduction, we can divide the Visual Studio extensibility into two main areas: packages and add-ins development and code editor extensions development. The next subsection provides more detail on the available projects. For now, focus on the concept of extension. Each component extending the IDE is called extension, independently from its nature (for example, packages or code editor extensions). This concept, together with the WPF-based extensibility features, requires a specific deployment system for extensions. Since Visual Studio 2010, Microsoft introduced the .VSIX file format, specific for deploying extensions and is intended as a replacement for the old .VSI file format (with some exceptions explained later in this chapter). But before going into further discussions, you need some additional tools for developing versus Visual Studio, known as the Visual Studio 2012 SDK.

The Visual Studio 2012 SDK

You create custom extensions for and with Visual Studio taking advantage of specific projects templates. To enable Visual Studio 2012 extensibility projects, you need to download and install the Visual Studio 2012 Software Development Kit, which is available from the Visual Studio Developer Center at http://msdn.microsoft.com/en-US/vstudio/ff718165.aspx. The SDK setup can install tools, project templates, and documentation so you can build custom extensions for the IDE. In the Start|Microsoft Visual Studio 2012 SDK, you can find shortcuts to online tools, samples, and documentation about the extensibility. In addition, in a subfolder named Tools you can find a shortcut for starting Visual Studio under the experimental instance and for resetting this environment. The experimental instance is a fully functional instance of Visual Studio used for extension debugging and testing, and in most cases, you do not need to launch it manually because it will launch by the development instance of Visual Studio. The experimental instance keeps track of all extensions you develop and debug, so you can reset the instance when you want it to be clean.


Extensibility Samples

The Visual Studio Extensibility team from Microsoft published (and periodically updates) code examples about extending Visual Studio 2012 onto the MSDN Code Gallery. I suggest you visit the dedicated web page at http://code.msdn.microsoft.com/vsx. You can find several interesting examples covering almost every extensibility area.


The SDK installs additional projects templates for the Visual Studio extensibility as summarized in the following list:

• Editor extensibility projects. These projects are fully functional code examples you can use for understanding how extensions work.

• Add-ins, integration packages, and Visual Studio Shell projects.

• Extension deployments projects, including toolbox controls.

All the listed projects templates are available in the New project dialog box. In the next section, you develop your first extension for Visual Studio 2012 taking advantage of the WPF infrastructure.

Building a Visual Studio Package

The goal of this introductory chapter on the Visual Studio extensibility is to show you how to create and deploy a Visual Studio Package. Visual Studio is made of packages. Each package represents a working unit. For example, the toolbox is a package; Solution Explorer is another package, and so on. You can extend Visual Studio by building custom integration packages. There are different kinds of integration packages, such as tool windows, menus, wizards, and languages. The big difference between a package and an add-in is a package can add completely new tools and features to the IDE, whereas add-ins typically extend existing features in the IDE with other features. In the next code example, you learn to build a custom tool window that can provide the ability of compiling code snippets on the fly in both Visual Basic and Visual C#. Open the New project window by selecting File, New project. When the New project window appears, select the Other Project Types, Extensibility subfolder on the left and then the Visual Studio Package project template. Name the new project SnippetCompilerVSPackage (see Figure 54.1) and then click OK.

Image

Figure 54.1. Creating the new extensibility project.

At this point, the Visual Studio Integration Package Wizard starts. Select Visual Basic as the programming language and leave unchanged the new key file option, as shown in Figure 54.2.

Image

Figure 54.2. Setting language and key file options.

In the next window, you can set information for the new package, such as author, description, and icon. Figure 54.3 shows an example for these settings.

Image

Figure 54.3. Setting package information.

The next step is important and is the place where you can select the package type. Select Tool window and then click Next (see Figure 54.4). You can select multiple options depending on where you want the tool to be available.

Image

Figure 54.4. Package type selection.

Next, you can specify the Window name and Command ID. The Window name is the tool window title, whereas the ID is used internally by Visual Studio for invoking the new package. Figure 54.5 shows an example of setting this information.

Image

Figure 54.5. Setting title and ID for the new tool window.

The next step requires you to specify whether you want to add test projects for the new package. For this example, uncheck both projects and go ahead. You can complete the wizard and Visual Studio will generate the new project. When the new project is ready, the first thing you notice is the tool window is nothing but a WPF custom control. Now double-click the MyControl.xaml file in Solution Explorer to enable the designer. Figure 54.6 shows how the IDE appears.

Image

Figure 54.6. The IDE is ready on the new extensibility project, showing the WPF custom control.

Visual Studio implements a WPF skeleton for a new tool window you need to customize. But first consider the meaning of files available within Solution Explorer. This is summarized in Table 54.1.

Table 54.1. VS Package Code Files

Image

In addition, a subfolder named Resources contains the icons used within the package and identifies the new tool in Visual Studio. All code files contain comments that can help you understand what a particular file is for. For instance, look at the SnippetCompilerPackage.vb file. For your convenience, Listing 54.1 shows the content of this file. Notice how comments are detailed and how they provide complete explanations on types and their roles within the user interface.

Listing 54.1. Understanding Packages Behind the Scenes


Imports Microsoft.VisualBasic
Imports System
Imports System.Diagnostics
Imports System.Globalization
Imports System.Runtime.InteropServices
Imports System.ComponentModel.Design
Imports Microsoft.Win32
Imports Microsoft.VisualStudio
Imports Microsoft.VisualStudio.Shell.Interop
Imports Microsoft.VisualStudio.OLE.Interop
Imports Microsoft.VisualStudio.Shell

''' <summary>
''' This is the class that implements the package exposed by this assembly.
'''
''' The minimum requirement for a class to be considered a valid package for Visual
''' Studio  is to implement the IVsPackage interface and register itself with the
''' shell. This package uses the helper classes defined inside the Managed Package
''' Framework (MPF) to do it: it derives from the Package class that provides the
''' implementation of the IVsPackage interface and uses the registration attributes
‘’' defined in the framework to register itself and its components with the shell.
''' </summary>
' The PackageRegistration attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class
' is a package.
'
' The InstalledProductRegistration attribute is used to register the information needed to show this package
' in the Help/About dialog of Visual Studio.
    '
' The ProvideMenuResource attribute is needed to let the shell know that this package exposes some menus.
    '
' The ProvideToolWindow attribute registers a tool window exposed by this package.

    <PackageRegistration(UseManagedResourcesOnly := true), _
    InstalledProductRegistration("#110", "#112", "1.0", IconResourceID := 400), _
    ProvideMenuResource("Menus.ctmenu", 1), _
    ProvideToolWindow(GetType(MyToolWindow)), _
    Guid(GuidList.guidSnippetCompilerPkgString)> _
    Public NotInheritable Class SnippetCompilerPackage
Inherits Package

''' <summary>
''' Default constructor of the package.
''' Inside this method you can place any initialization code that does not require
''' any Visual Studio service because at this point the package object is created
''' but not cited yet inside Visual Studio environment. The place to do all the
''' other initialization is the Initialize method.
''' </summary>
Public Sub New()
    Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", Me.GetType().Name))
End Sub

        ''' <summary>
''' This function is called when the user clicks the menu item that shows the
''' tool window. See the Initialize method to see how the menu item is associated to
''' this function using the OleMenuCommandService service and the MenuCommand class.
''' </summary>
Private Sub ShowToolWindow(ByVal sender As Object, ByVal e As EventArgs)
    ' Get the instance number 0 of this tool window. This window is single instance so this instance
    ' is actually the only one.
    ' The last flag is set to true so that if the tool window does not exists it will be created.
    Dim window As ToolWindowPane = Me.FindToolWindow(GetType(MyToolWindow), 0, True)
    If (window Is Nothing) Or (window.Frame Is Nothing) Then
        Throw New NotSupportedException(Resources.CanNotCreateWindow)
    End If

    Dim windowFrame As IVsWindowFrame = TryCast(window.Frame, IVsWindowFrame)
    Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(windowFrame.Show())
End Sub


''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Overridden Package Implementation
#Region "Package Members"

''' <summary>
''' Initialization of the package; this method is called right after the package is cited, so this is the place
''' where you can put all the initialization code that rely on services provided by VisualStudio.
''' </summary>
Protected Overrides Sub Initialize()
    Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", Me.GetType().Name))
    MyBase.Initialize()

            ' Add our command handlers for menu (commands must exist in the .vsct file)
    Dim mcs As OleMenuCommandService = TryCast(GetService(GetType(IMenuCommandService)), OleMenuCommandService)
    If Not mcs Is Nothing Then
                ' Create the command for the tool window
                Dim toolwndCommandID As New CommandID(GuidList.guidSnippetCompilerCmdSet, CInt(PkgCmdIDList.cmdidSnippetCompiler))
        Dim menuToolWin As New MenuCommand(New EventHandler(AddressOf ShowToolWindow), toolwndCommandID)
        mcs.AddCommand(menuToolWin)
            End If
        End Sub
#End Region

End Class


The class inherits from Microsoft.VisualStudio.Shell.Package, the base class exposing the required interface for every functional package. The ShowToolWindow method gets an instance of the Microsoft.VisualStudio.Shell.ToolWindowPane class pointing to the custom tool window (Me.FindToolWindow(GetType(MyToolWindow))). The same exam can be done on the ToolWindow.vb file. After doing this, you can customize the WPF control. The goal of the tool window is enabling on-the-fly compilation for code snippets. With that said, there is the need of implementing the user-interface side. In the XAML editor, type the code shown in Listing 54.2.

Listing 54.2. Implementing the Tool Window User Interface


<UserControl x:Class="MyControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:vsfx="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.10.0"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300"
             Name="MyToolWindow"
             Background="{DynamicResource
             {x:Static vsfx:VsBrushes.ToolWindowBackgroundKey}}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition Height="40" />
            <RowDefinition />
            <RowDefinition Height="50" />
            <RowDefinition Height="40" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <!-- This will allow selecting the compiler -->
        <ComboBox Name="LanguageCombo" Text="VisualBasic" Margin="5">
            <ComboBoxItem Content="VisualBasic" />
            <ComboBoxItem Content="CSharp" />
        </ComboBox>
        <TextBlock Margin="5" Grid.Row="1"
                       Foreground="{DynamicResource
                       VsBrush.ToolWindowTextKey}">
                Write or paste your code here:</TextBlock>
        <TextBox Grid.Row="2"  Name="CodeTextBox" Margin="5"
                     Foreground="{DynamicResource
                     VsBrush.ToolWindowTextKey}"
                     AcceptsReturn="True" AcceptsTab="True"
                     VerticalAlignment="Stretch"
                     VerticalScrollBarVisibility="Auto"
                     HorizontalScrollBarVisibility="Auto" />
        <Button Grid.Row="3" Content="Compile code" Width="80" Height="40"
                    Name="button1"/>
        <TextBlock Grid.Row="4" Margin="10"
                       Foreground="{DynamicResource
                       VsBrush.ToolWindowTextKey}">
                Compilation results:</TextBlock>
        <ListBox Grid.Row="5" ItemsSource="{Binding}"
                Name="ErrorsListBox" Margin="5"
                Foreground="{DynamicResource
                VsBrush.ToolWindowTextKey}"/>
    </Grid>
</UserControl>


On the Visual Basic side, enter the MyControl.xaml.vb file and write the code shown in Listing 54.3. This adds compile functionalities to the tool window when the button is clicked. The code uses the System.CodeDom namespace for getting instances of the .NET compilers, as you will understand through comments in the code.

Listing 54.3. Code for Compiling On-the-Fly Code Typed Inside the Tool Window


Imports System.Security.Permissions
Imports System
Imports System.Reflection
Imports System.Reflection.Emit
Imports System.CodeDom.Compiler
Imports System.Windows.Controls

'''<summary>
'''  Interaction logic for MyControl.xaml
'''</summary>
Partial Public Class MyControl
    Inherits System.Windows.Controls.UserControl

    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization",
                        "CA1300:SpecifyMessageBoxOptions")> _
    Private Sub button1_Click(ByVal sender As Object,
                              ByVal e As System.EventArgs) Handles button1.Click
        Try
            If String.IsNullOrEmpty(CodeTextBox.Text) = False Then

                Me.ErrorsListBox.ItemsSource = _
                Compile(CType(Me.LanguageCombo.SelectedItem,
                        ComboBoxItem).Content.ToString)

            End If
        Catch ex As Exception
            'Handle other exceptions here, no compiler errors
        End Try

    End Sub

    Private Function Compile(ByVal language As String) As IEnumerable(Of String)

        'Gets the ComboBox selected language
        Dim languageProvider As String = language

        'Creates an instance of the desired compiler
        Dim CompilerProvider As CodeDomProvider = _
            CodeDomProvider.CreateProvider(languageProvider)

        'Sets compiler parameters
        Dim params As New CompilerParameters()
        Dim results As CompilerResults

        'Configure self-explanatory parameters
        With params
            .GenerateExecutable = False
            .GenerateInMemory = True
            .IncludeDebugInformation = False
            'You can add multiple references here
            .ReferencedAssemblies.Add("System.dll")
        End With

        'Compiles the specified source code
        results = CompilerProvider.
                  CompileAssemblyFromSource(params,
                                            CodeTextBox.Text)

        'If no errors, the ListBox is empty
        If results.Errors.Count = 0 Then
            Return Nothing
        Else
            'If any errors, creates a list of errors...
            Dim errorsList As New List(Of String)

            '..iterating the compiler errors
            For Each item As CompilerError In results.Errors
                errorsList.Add(item.ErrorText & " Line " & item.Line.ToString)
            Next
            Return errorsList.AsEnumerable
        End If

    End Function
End Class


You can now test the new tool window. You can do this by pressing F5 as you would do in any other kind of .NET application. This starts a new instance of Visual Studio known as Experimental Instance. It is a fully functional instance of Visual Studio that is used for debugging custom extensions. If the new tool window is not visible, click the new View, Other windows, Snippet compiler command. Figure 54.7 shows how the new tool window appears in the IDE.

Image

Figure 54.7. The new tool window running inside Visual Studio 2012.

The new tool window is a fully functional one, so it can be anchored like any other Visual Studio window. To stop the test environment, close the experimental instance of Visual Studio. Until now, you saw a debugging scenario. When the debugging and testing phase is completed, you need to deploy the extension to other developers. As explained in next section, Visual Studio 2012 offers a specific deployment system for this kind of extensions that is simple to accomplish and has been inherited from the previous versions of the product.

Deploying Visual Studio Extensions

To deploy Visual Studio extensions, you package components inside a file type named VSIX (with .vsix extension). This format is intended as a replacement for the previous .vsi file format first introduced with Visual Studio 2005 (and that actually still survives for the sake of backward compatibility). A VSIX package is nothing but a zip archive that is built with regard of the open packaging convention. This means if you rename the .vsix package into .zip, you can browse its content with any compression tool supporting zips. This kind of package needs to store some other files:

• A [Content_Types].xml file that describes the archive content according to the open packaging convention

• An extension.vsixmanifest file storing information on the extension and on how it will be deployed

• Binary files for the extension (set named product payload)

• Support files, such as license, icons, etc.

VSIX packages cannot deploy add-ins, macros, and code snippets, whereas they can deploy any other kind of extensions. You can deploy extensions via Windows Installer packages; this is preferable when you need to accomplish specific requirements such as installing assemblies to the GAC or writing to the Registry. For all other cases, VSIX packages are a good thing.


Deploying add-ins and Code Snippets

Because you cannot deploy Visual Studio add-ins and code snippets with VSIX packages, you still need to build a .vsi package or recur to Windows Installer projects.


There are some other good reasons for preferring VSIX packages. First, they can check for updates. Second, they can be uploaded to the Visual Studio Gallery so other developers can download your extension directly from the Visual Studio Extension Manager. (The latter will be covered later in this chapter.) Another good reason is that you do not need to edit a VSIX package manually. Visual Studio offers an integrated designer for creating VSIX packages directly into the current project. Continuing from the previous example, double-click the source.extension.vsixmanifest file in Solution Explorer. This file is added to each extensibility project at creation time and is the deployment manifest for the extension. Once this is complete, Visual Studio 2012 looks like Figure 54.8.

Image

Figure 54.8. Customizing properties for the deployment package.

With the exception of the ID field, which is filled by Visual Studio, you just need to fill blank and self-explanatory fields with custom values, as Figure 54.8 shows. It is worth mentioning that VSIX packages are localizable (check out the Language combo box). Here, you can specify a license agreement (License field) by adding an existing text file or RTF file. If you select the Install Targets tab, you will be able to specify multiple editions of Visual Studio that will be able to receive your extension. Once in the Install Target tab, click the New button to specify another edition of Visual Studio or click Edit or Delete to modify or remove an edition. The Asset tab enables you to specify additional assets that describe your extension and must be installed together with it. The Dependencies tab enables you to specify other extensions that the current one depends on, if any.

You can find further information about the VSIX designer at http://bit.ly/PU9mKf. To build the deployment package, build the project. The VSIX package is now available in the project output folder (BinDebug or BinRelease). With regard to the previous example, the package is named SnippetCompiler.Vsix. If you double-click this file, you will be prompted with some information before installation begins, as seen in Figure 54.9.

Image

Figure 54.9. Installing the new custom extension.

By clicking Install, the new custom extension will be available onto the target system. This means you need to deploy the VSIX package and you are done.


Publishing Extensions to the Visual Studio Gallery

You can publish and share your extensions to the Visual Studio Gallery (http://visualstudiogallery.com). You just need to sign in with a Windows Live ID and then select My Contributions, Upload. You will need to supply the required information and upload your VSIX package. After it is uploaded, it will be available through the Extensions and Updates tool to other developers.


Managing Extensions with Extensions and Updates

Visual Studio 2012 has an integrated tool to easily manage installed extensions and to find online extensions that can be installed from the Internet. To enter this tool, select the Tools, Extensions and Updates command. Figure 54.10 shows how the Extension and Updates dialog box appears.

Image

Figure 54.10. The Extensions and Updates tool.

The Extension and Updates tool can be used to find, download, and install extensions to Visual Studio 2012. If you select the Online, Visual Studio Gallery option on the left, the tool shows all available extensions in the Visual Studio Gallery. This is an online website from Microsoft specific for Visual Studio extensions (http://visualstudiogallery.com). This website will provide a complete overview of extensions and possibly the source code where available. The tool shows the list of available extensions, providing a brief description on the right side of the dialog box. You click Download to install the desired extension. Each time you install an extension, Visual Studio might need to be restarted to correctly recognize these additions. Extensions are divided into three categories: Controls, Templates (including project and item templates), and Tools. Each category is divided into subcategories, explaining what the extension is bound to. The Extensions and Updates tool can find only VSIX packages, meaning that add-ins and code snippets are not supported and need to be handled differently. You can search through online additions using the search box in the upper right of the dialog box. A new feature in Visual Studio 2012 that was not available in Visual Studio 2010 is the capability to search for and install code examples from the MSDN Code Gallery. The way examples work was discussed back in Chapter 2, “Getting Started with the Visual Studio 2012 IDE,” but with this tool, you can not only download samples but also manage installed samples and search for sample updates. Similarly, you can easily manage installed additions. Click Installed, All to get the full list of available extensions on your system. See Figure 54.11, which lists extensions available on my development machine.

Image

Figure 54.11. Managing installed extensions.

Here you can disable an extension, keeping it installed on the machine and available for future reuse, or completely uninstall. As you can see, you can filter the list of extensions by controls, code examples, project and item templates, software development kits, and tools. The dialog box can also find updates for installed extensions. Do this by selecting the Updates command on the left side.

Managing Add-Ins with the Add-In Manager

Visual Studio 2012, exactly as its predecessors, lets you enhance the environment with add-ins. As mentioned at the beginning of this chapter, an add-in extends an existing functionality. You can manage installed add-ins via the Add-in Manager tool. You enter the tool by selecting Tools, Add-In Manager. Figure 54.12 shows the tool when some add-ins are installed.

Image

Figure 54.12. The Add-in Manager.

For each add-in you can specify, it must be loaded at the IDE startup or if it has command-line support. Because building custom add-ins is something that was already available in previous versions of the IDE, this topic is not covered here, so refer to the official MSDN page at http://msdn.microsoft.com/en-us/library/80493a3w(v=vs.110).aspx for more information.

Extending the Code Editor

One of the most important features in the Visual Studio 2012 extensibility is the capability of extending the code editor, which is based on WPF since Visual Studio 2010. Code editor extensions get the instance of the WPF objects keeping the editor itself alive. For a better comprehension, instead of building a particular extension, we explain required objects taking advantage of one of the sample projects added by the Visual Studio 2012 SDK. Create a new project and select the Visual Basic|Extensibility folder. Finally, select the Editor Text Adornment project template, as shown in Figure 54.13.

Image

Figure 54.13. Selecting the code editor extension template.

The goal of this sample project is simple—adorning each a character in the code with a different background color. The most important object in providing editor extension is the Microsoft.VisualStudio.Text.Editor.IWpfTextView type that represents the instance of the code editor. For the current example, there is the need of placing an adornment on all occurrences of the specified character. To place adornments, you need an instance of the IAdornmentLayer type that represents a space for placing adornments. Listing 54.4 shows the complete code. Be sure to read comments that can help you understand what is under the hood.

Listing 54.4. Providing a Code Editor Extension with Adornments


Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Media
Imports Microsoft.VisualStudio.Text
Imports Microsoft.VisualStudio.Text.Editor
Imports Microsoft.VisualStudio.Text.Formatting

''' <summary>
''' ScarletCharacter adornment places red boxes behind all
''' the "a"s in the editor window
''' </summary>
Class ScarletCharacter

    Private WithEvents _view As IWpfTextView
    Private ReadOnly _layer As IAdornmentLayer
    Private ReadOnly _brush As Brush
    Private ReadOnly _pen As Pen

    'The IWpFTextView object represents the
    'instance of the code editor
    Public Sub New(ByVal view As IWpfTextView)
        _view = view

        'IAdornmentLayer represents the place where
        'adorners are placed
        _layer = view.GetAdornmentLayer("ScarletCharacter")

        'Create the pen and brush to color the box behind the a's
        Dim brush As New SolidColorBrush(Color.
                         FromArgb(&H20, &H0, &H0, &HFF))
        brush.Freeze()
        Dim penBrush As New SolidColorBrush(Colors.Red)
        penBrush.Freeze()
        Dim pen As New Pen(penBrush, 0.5)
        pen.Freeze()

        _brush = brush
        _pen = pen
    End Sub

    ''' <summary>
    ''' On layout change add the adornment to any reformatted lines
    ''' </summary>
    Private Sub OnLayoutChanged(ByVal sender As Object,
                                ByVal e As TextViewLayoutChangedEventArgs) _
                                Handles _view.LayoutChanged

        'TextViewLayoutChangedEventArgs provides information when
        'the code editor layout changes
        For Each line In e.NewOrReformattedLines
            Me.CreateVisuals(line)
        Next line
    End Sub

    ''' <summary>
    ''' Within the given line add the scarlet box behind the a
    ''' </summary>
    Private Sub CreateVisuals(ByVal line As ITextViewLine)
        'grab a reference to the lines in the current TextView
        Dim textViewLines = _view.TextViewLines
        Dim lineStart As Integer = line.Start
        Dim lineEnd As Integer = line.End

        'Loop through each character, and place a box around any a
        For i = lineStart To lineEnd - 1
            If _view.TextSnapshot(i) = "a"c Then
                Dim charSpan As New SnapshotSpan(_view.TextSnapshot,
                                    Span.FromBounds(i, i + 1))
                Dim g As Geometry = textViewLines.GetMarkerGeometry(charSpan)
                If g IsNot Nothing Then
                    Dim drawing As New GeometryDrawing(_brush, _pen, g)
                    drawing.Freeze()

                    Dim drawingImage As New DrawingImage(drawing)
                    drawingImage.Freeze()

                    Dim image As New Image()
                    image.Source = drawingImage

                    'Align the image with the top of the bounds of the text geometry
                    Canvas.SetLeft(image, g.Bounds.Left)
                    Canvas.SetTop(image, g.Bounds.Top)

                    'AdornmentPositioningBehavior sets how
                    'the adornment is placed
                    _layer.AddAdornment(AdornmentPositioningBehavior.
                                        TextRelative, charSpan,
                                        Nothing, image, Nothing)
                End If
            End If
        Next
    End Sub

End Class


Notice how the Microsoft.VisualStudio.Text namespace exposes objects and other namespaces for interacting with the code editor. Now run the extension by pressing F5. Try to create a new console project and write some text containing the “a” character and you will see how they are surrounded with a different background, as shown in Figure 54.14.

Image

Figure 54.14. The WPF editor extension adorning multiple “a”s in a row.

In many scenarios you might need to extend the Visual Studio code editor. You can find more interesting extensions by searching the Visual Studio Gallery with the Extension Manager.

Summary

Visual Studio 2012 is an extensible development environment that can be enhanced with custom extensions such as add-ins, packages, and new code editor extensions due to an efficient architecture based on Windows Presentation Foundation. Instrumentation required for creating extensibility projects is available when installing the Visual Studio 2012 SDK, which provides project templates, tools, and documentation. This chapter explained how to build a custom tool window based on WPF for the Visual Studio development environment. Then you learned how custom extension could be packaged into VSIX files and deployed to other developers. Next, you saw how you could use the new Extension Manager for getting and installing extensions from the Visual Studio Gallery. The last example provided in this chapter was about extending the WPF-based code editor and using Visual Studio’s managed assemblies.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.137.59