June 2013

Volume 28 Number 6

ALM Rangers - Extending Visual Studio Team Explorer 2012

By Mike Fourie

In this article I’ll discuss new extensibility points provided in Microsoft Visual Studio Team Explorer 2012. I’ll build a working sample to highlight the architecture of Team Explorer 2012 while hopefully providing you with some inspiration to build extensions that will be beneficial to you, your team and perhaps even the wider community.

Since Visual Studio 2005, Team Explorer has provided the primary interface for interacting with the various team-centric features provided in Visual Studio such as Work Items, SharePoint Documents, Reports, Builds and Source Control.

The subsequent releases of Visual Studio 2008 and Visual Studio 2010 provided minor updates to Team Explorer, but the basic Tree View navigation paradigm remained in place—until Visual Studio 2012 arrived, that is. Visual Studio 2012 introduced a completely redesigned Team Explorer and UX. Gone is the Tree View, which has been replaced by a collection of Pages that provides access to the same features we’ve become accustomed to, as well as some new features like My Work.

Also, note that with Visual Studio 2012, you can now work entirely in managed code without any concerns about dealing with COM internals.

Team Explorer 2012 Overview

Team Explorer 2012 comprises several Pages, each of which can be split into Sections and host Navigation Items and Navigation Links. The default page is called Home and it provides easy access to the other pages provided in Team Explorer, namely My Work, Pending Changes, Work Items, Builds, Web Access, Team Members and Settings.

Figure 1 shows the Home page and the various Navigation Items and Links provided.

The Team Explorer 2012 Home Page
Figure 1 The Team Explorer 2012 Home Page

Figure 2 shows the use of Sections, which you can add to Pages.

Sections in the Team Explorer Builds Page
Figure 2 Sections in the Team Explorer Builds Page

If this is the first time you’ve seen Team Explorer 2012, you’ll no doubt agree that the design is completely different from the previous Tree View-based experience provided in Visual Studio 2005, 2008 and 2010.

Given the simplicity and efficiency of the previous Tree View implementations, you might wonder why there’s been such a radical redesign. After working in the new Page-based Team Explorer you might feel—and I think with some justification—that the navigation isn’t as efficient as the simple Tree View that it replaced. It certainly takes some getting used to, though hopefully by the end of this article you’ll understand that the current loss in navigation efficiency is outweighed by the powerful extensibility features that the new Page design provides and the reduction in modal dialogs that are prevalent in previous versions. Note that I say “current” loss in navigation efficiency, as I expect the design to be tweaked throughout the course of the quarterly updates Microsoft is now providing for Visual Studio. In fact, Visual Studio 2012 Update 2 has a handy new Connect Page that displays every Team Foundation Server (TFS), Team Project Collection and Team Project to which you’ve previously connected, making navigating TFS connections much easier.

Existing Team Explorer Extensibility Resources

In addition to this article, you should be aware of a few other resources that can help you find your way around extending Team Explorer. MSDN has API-level documentation covering the Microsoft.TeamFoundation.Controls namespace that hosts all the objects I’ll extend.

The Visual Studio Developer Center has a sample, “Extending Team Explorer in Visual Studio 2012” (bit.ly/Zp1OFf), that was published by the team that developed Team Explorer 2012. The MyHistory sample extension provided with this article is based on that sample and I recommend you read it first.

Team Explorer Architecture

Before you start creating extensions for Team Explorer it will be beneficial to understand a little about how Team Explorer is architected.

The extensions you create need to implement the interface for the extension point you’re targeting. These interfaces are implemented in the Microsoft.TeamFoundation.Controls namespace and are defined as follows:

Extensions you create are discovered using the Managed Extensibility Framework (MEF). To aid discovery, you must decorate your class with the appropriate attribute as defined here:

  • Page: [TeamExplorerPage(“guid”)] (bit.ly/17kBzQD)
  • Section: [TeamExplorerSection(“guid”, “parentPageGuid”, priority)] (bit.ly/12nMxCC)
  • Navigation Item: [TeamExplorerNavigationItem(“guid”, priority)] (bit.ly/Y6YQ7S)
  • Navigation Link: [TeamExplorerNavigationLink(“guid”, “parentNavigationItemGuid”, priority)] (bit.ly/12KFghj)

For your extension to be discovered, your assembly needs to be in one of the “well-known” extension locations for Visual Studio. These locations are defined in the “Master PkgDef” file, located at <VsInstallRootFolder>\Common7\IDE\devenv.pkgdef.

The recommended packaging mechanism for your extensions is to use a Visual Studio Extension (VSIX). This mechanism will take care of all the deployment plumbing for you. If you’d like to read more on discovery, see The Visual Studio Blog post, “How VSIX extensions are discovered and loaded in VS 2010” (bit.ly/xTJrSv).

To illustrate the extension lifetime, navigation, context management and other features available to your extension, let’s take a look at the My History sample provided with this article.

Getting Started with the Sample App

You can download the full working sample for this article, but I believe it’s important to highlight a few key points to help you successfully get started with building your own extension.

To create an extension you’re going to need Visual Studio 2012—and in particular, the Visual Studio 2012 SDK—to create the VSIX project. When you create a VSIX project, Visual Studio takes you straight to the .vsixmanifest file shown in Figure 3. I’ve added my own license file to the project and completed a few fields as shown.

Details of the .vsixmanifest File
Figure 3 Details of the .vsixmanifest File

You can choose to add your extension in a separate assembly or host all your code within the VSIX project. If you choose to host all your code in the VSIX, it’s important to manually tweak the .csproj file so the extension assembly is included in the VSIX package. By default, the VSIX project creates only the VSIX package. When you deploy your extension, it will show as installed; however, your assembly won’t be deployed, thus it won’t be discovered. To include the assembly, you need to open the .csproj file in a text editor and set the project settings as shown in the following code (this change can’t be made via the UI):

<IncludeAssemblyInVSIXContainer>true</IncludeAssemblyInVSIXContainer>
<CopyBuildOutputToOutputDirectory>true</CopyBuildOutputToOutputDirectory>

Within the manifest file you need to configure Assets so the extension can deploy your extension appropriately. On the Assets tab, add a new Microsoft.VisualStudio.VsPackage for the project. This tells the VSIX that you’re creating a Visual Studio package. Also, add an Asset of type Microsoft.VisualStudio.MefComponent, which provides the name of the extension assembly in the VSIX package.

A VSIX project has no References by default, so you need to add all those that are required. At a minimum you’re likely to need the following references:

  • Microsoft.TeamFoundation.Client
  • Microsoft.TeamFoundation.Controls
  • Microsoft.TeamFoundation.VersionControl.Client
  • Microsoft.TeamFoundation.VersionControl.Controls
  • Microsoft.VisualStudio.Shell.Interop
  • Microsoft.VisualStudio.Shell.Interop.10.0
  • Microsoft.VisualStudio.Shell.Interop.8.0
  • Microsoft.VisualStudio.Shell.Interop.9.0
  • Microsoft.VisualStudio.TeamFoundation.VersionControl
  • Microsoft.VisualStudio.OLE.Interop
  • Microsoft.VisualStudio.Shell.11.0
  • Microsoft.VisualStudio.Shell.Immutable.10.0
  • PresentationCore
  • PresentationFramework
  • System
  • System.ComponentModel.Composition
  • System.Core
  • System.Drawing
  • System.Windows.Forms
  • System.Xaml
  • System.Xml

Although you can create your extension in managed code, you’ll likely still need to integrate with Visual Studio via COM, so you’ll most likely also need COM references to EnvDTE and EnvDTE80.

Once you’ve done all that, you’re ready to start creating content to extend Team Explorer.

Debugging

If this is your first adventure using VSIX projects, you may be wondering how to debug them. In Visual Studio 2012 the new project is now created with the required Debug information, so you can simply hit F5 and a new “experimental” instance of Visual Studio (bit.ly/12KRCGq) will start with your extension loaded.

You can set breakpoints in your code and debug like any other app, although perhaps a little slower than you may be used to for simpler applications. Somehow my project managed to lose the settings, so I’ve shown them in Figure 4. Basically the Start Action is set to Start external program with the value C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\devenv.exe and the Command line arguments are set to /rootsuffix Exp.

VSIX Debug Settings
Figure 4 VSIX Debug Settings

Walk-Through of the My History Sample

Developers often need to multitask (that’s the fancy word for randomization!) and sometimes forget what they did just a few minutes or days ago. The My History extension aims to help improve your multitasking experience by providing a list of Shelvesets, Changesets, Work Items and Projects/Solutions with which you’ve recently worked. Shown in Figure 5, it consists of a new Page along with multiple Sections, Navigation Items and Navigation Links.

The My History Extension
Figure 5 The My History Extension

At this point I’d recommend installing the extension to see how it extends Team Explorer, then come back to this article to go through some principal parts of the extension code.

The Code in My History

The solution is organized into a set of folders for easier navigation and discovery.

The Base folder contains a set of classes based on those provided in the aforementioned “Extending Team Explorer in Visual Studio 2012” sample. The Internals and Resources folders contain various helper classes. You’re most interested in the Sections folder, which contains the implementation of the objects from which you need to derive. Figure 6 shows how the classes are related.

Diagram of MyHistory Classes
Figure 6 Diagram of MyHistory Classes

To create an entry point to the new Page you need to add a Navigation Link to one of the existing sections on the Home Page.

Figure 7 shows the code for the custom Navigation Link contained in MyHistory­NavigationLink.cs. This code produces the My History link shown in Figure 5.

Figure 7 The My History Navigation Link

[TeamExplorerNavigationLink(MyHistoryNavigationLink.LinkId,
   TeamExplorerNavigationItemIds.MyWork, 200)]
public class MyHistoryNavigationLink : TeamExplorerBaseNavigationLink
{
  public const string LinkId = "e49a882b-1677-46a9-93b4-db290943bbcd";
  [ImportingConstructor]
  public MyHistoryNavigationLink([Import(typeof(SVsServiceProvider))]
     IServiceProvider serviceProvider) : base(serviceProvider)
  {
    this.Text = "My History";
    this.IsVisible = true;
    this.IsEnabled = true;
  }
  public override void Execute()
  {
    try
    {
      ITeamExplorer teamExplorer = GetService<ITeamExplorer>();
      if (teamExplorer != null)
      {
        teamExplorer.NavigateToPage(new Guid(MyHistoryPage.PageId), 
          null);
      }
    }
    catch (Exception ex)
    {
      this.ShowNotification(ex.Message, NotificationType.Error);
    }
  }

When the link is clicked, the Execute method is run and Team Explorer is made to navigate to your custom My History Page. Let’s take a closer look at the first line in Figure 7. The TeamExplorerNavigation­Link class attribute is used to associate the link with the MyWork section, and the priority of 200 means that link will likely be displayed last, assuming someone hasn’t installed another extension with a higher number (lower priority).

Figure 8 shows the code in MyHistoryPage.cs, which provides the addition of a new Team Explorer Page. Note the attribute usage and unique ID used.

Figure 8 The MyHistory Page

/// <summary>
/// MyHistory Page. We're extending Team Explorer by adding a new page and
/// therefore use the TeamExplorerPage attribute and pass in our unique ID
/// </summary>
[TeamExplorerPage(MyHistoryPage.PageId)]
public class MyHistoryPage : TeamExplorerBasePage
{
  // All Pages must have a unique ID; use the Tools | Create   
  // GUID menu in Visual Studio to create your own GUID
  public const string PageId = "BAC5373E-1BE5-4A10-97F5-AC278CA77EDF";
  public MyHistoryPage()
  {
    // Set the page title
    this.Title = "My History";
  }
}

You have an entry point to your new Page, and you can get to your new Page; now you need to get content onto the Page. I’ll cover the Changesets Section in detail and leave it to you to take a look at the other implemented Sections in your own time. All follow the same basic pattern.

In the accompanying code download is the ChangesetsSection.cs class, which is too long to list in this article. In this class you register a new ITeamExplorerSection with the MyHistory Page, using the TeamExplorerSection class attribute. Note that the value of 20 in the class attribute is the priority of the section. The lower the priority, the higher up in the Page the Section gets rendered. The class declares an ObservableCollection of Changesets to which your UI is bound. In the constructor of the class you define additional visual and behavioral settings. It’s important to link the class to its rendered content, which is a new instance of your User Control, ChangesetsSectionView.xaml.

Note that in the Initialize method you check for context so you can reload it from memory rather than re-query it. The SaveContext method saves necessary context information for future use, and the ContextChanged method can be used to refresh the data if the user changes projects or collections. In all cases, if you need to re-query it, you do so in an async fashion to avoid blocking the UI thread. The ViewChangesetDetails method is called from your User Control. This method interacts with Team Explorer and provides it with the information it needs to render the Changeset details in the correct Page.

The final piece in the puzzle is to create your user control to display the content. The Changesets section provides a double-­clickable list of the last 10 check-ins the user did, each with a tooltip showing a few pertinent details and a hyperlink to take the user to the full Changeset history, as shown in Figure 9.

The Changesets Section UI
Figure 9 The Changesets Section UI

The code for the user control is fairly basic, and it really just handles the user events, which make calls through to the ParentSection to interact with Team Explorer. Figure 10 provides an abbreviated view over some of the codebehind. Note the use of the Dependency­Property class (bit.ly/11FJNQW), which helps to bind the user control to the class you’ve registered with Team Explorer.

Figure 10 The Changesets User Control Code

public partial class ChangesetsSectionView
{
  public static readonly DependencyProperty ParentSectionProperty =       
    DependencyProperty.Register("ParentSection",
    typeof(ChangesetsSection), typeof(ChangesetsSectionView));
  public ChangesetsSection ParentSection
  {
    get { return (ChangesetsSection)GetValue(ParentSectionProperty); }
    set {SetValue(ParentSectionProperty, value);}
  }
  private void HistoryLink_Click(object sender, RoutedEventArgs e)
  {
    this.ParentSection.ViewHistory();
  }
  private void ChangesetList_MouseDoubleClick(
    object sender, MouseButtonEventArgs e)
  {
    if (e.ChangedButton == MouseButton.Left &&
       changesetList.SelectedItems.Count == 1)
    {
      this.ViewChangesetDetails();
    }
  }
}

Awareness, Insight and a Starting Point

There’s a lot more code to look through in the sample, which, together with what I’ve covered in this article, hopefully provides you with:

  • An awareness of the extensibility Team Explorer 2012 provides.
  • An insight into the vast API Visual Studio provides with which you can interact (be sure to read the MSDN Magazine article, “Version Control in the TFS Client Object Model” [msdn.microsoft.com/magazine/jj883959], if you’re interested in more API-level reading matter).
  • A starting point and the confidence to create your first extension.

Thanks for taking the time to read this, and please look for more articles from the ALM Rangers (aka.ms/vsarunderstand).


Mike Fourie is an independent consultant with more than 13 years of software development experience who specializes in build and deployment automation. He’s a Microsoft ALM MVP and Distinguished ALM Ranger. He can be reached via his blog at freetodev.com. You can also follow him on Twitter at twitter.com/mikefourie.

Thanks to the following technical experts for reviewing this article: Chad Boles (Microsoft), Jeff Bramwell (Farm Credit Services of America), Willy-Peter Schaub (Microsoft) and Hamid Shahid (Consultant)

Jeff Bramwell is director of enterprise architecture at Farm Credit Services of America. He has more than 20 years of software development experience and always strives to follow the mantra of start simple and work your way up from there. His blog is at devmatter.blogspot.com. You can follow him on Twitter at twitter.com/jbramwell.

Willy-Peter Schaub is a senior program manager with the Visual Studio ALM Rangers at the Microsoft Canada Development Center. Since the mid-’80s, he’s been striving for simplicity and maintainability in software engineering. Read his blog at blogs.msdn.com/b/willy-peter_schaub and follow him on Twitter at twitter.com/wpschaub.

Hamid Shahid is a Microsoft Visual Studio ALM Ranger and a software consultant. He has more than 13 years of experience working with Microsoft technologies with a special interest in MSBuild and Team Foundation Server.