TFS2010 – Customizing the Build Details View – Summary View

In some previous posts (here and here), I talked about ways to change the log view part of the build details view. This time I want to discuss how to change the Summary view. It is a little more difficult, but the extensibility is there for you to change it!

So, let me first show you the end product… It’s a new section just below “Latest Activity” that shows a famous quote:

 

 

It’s a very simple example, but you get the idea. Now, let’s talk about the code. To add a new section to the Build Details View, you have to implement a few interfaces: IBuildDetailFactory, IBuildDetailSection, and IBuildDetailNode. The factory class returns instances of the other two classes when called. And the factory class gets registered with Visual Studio through the build extensibility objects.

Here is my factory implementation:

 using System;
using System.Windows.Documents;
using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.Build.Controls;

namespace CustomBuildDetailsView
{
    public class MySectionFactory : IBuildDetailFactory
    {
        String QuoteOfTheDay { get; set; }
        String QuotedBy { get; set; }

        /// <summary>
        /// This method should make all necessary server calls to aquire the data
        /// needed to display the sections and nodes for this factory.
        /// This method is called on a background thread (no UI work can be done).
        /// </summary>
        /// <param name="build">a reference to the IBuildDetail being displayed</param>
        public void LoadData(IBuildDetail build)
        {
            // Here is where you would make server calls and store the result.
            // For instance, I could query a separate web service to return information
            // like famous quotes...
            QuoteOfTheDay = "Whatever good things we build end up building us.";
            QuotedBy = "Jim Rohn";
        }

        /// <summary>
        /// This method should return all IBuildDetailSections for this factory.
        /// </summary>
        /// <param name="view">a reference to IBuildDetailsView</param>
        /// <returns>array of IBuildDetailSections</returns>
        public IBuildDetailSection[] CreateSections(IBuildDetailView view)
        {
            return new IBuildDetailSection[] { new MySection() };
        }

        /// <summary>
        /// This method should return all IBuildDetailNodes for this factory.
        /// </summary>
        /// <param name="view">a reference to IBuildDetailsView</param>
        /// <returns>array of IBuildDetailNodes</returns>
        public IBuildDetailNode[] CreateNodes(IBuildDetailView view)
        {
            Paragraph p = new Paragraph();
            p.Inlines.Add(new Italic(new Run(QuoteOfTheDay)));
            p.Inlines.Add(new LineBreak());
            p.Inlines.Add(new Bold(new Run(QuotedBy)));

            return new IBuildDetailNode[] { 
                new MySectionNode() { 
                    SectionName = "MySection", 
                    Content = p
                }
            };
        }

        /// <summary>
        /// Returns the name of the factory (this name should never be localized).
        /// </summary>
        public string Name
        {
            get { return "MySectionFactory"; }
        }
    }

    public class MySection : IBuildDetailSection
    {
        /// <summary>
        /// Returns the header for this section. Usually this is just a localized string.
        /// However, it can also return a Windows Presentation Foundation document paragraph 
        /// that contains any valid elements for display.
        /// </summary>
        public object Header
        {
            get { return "My Section"; }
        }

        /// <summary>
        /// Returns the name of the section (this name should never be localized).
        /// </summary>
        public string Name
        {
            get { return "MySection"; }
        }

        /// <summary>
        /// Returns the relative priority of this section. This value is used to sort 
        /// the sections in ascending order before display. Standard sections have 
        /// values like 100, 200, 300, etc.
        /// </summary>
        public int Priority
        {
            get { return 150; }
        }
    }

    public class MySectionNode : IBuildDetailNode
    {
        public object Content { get; set; }
        public string SectionName { get; set; }
    }
}

 

I put these classes into a Visual Studio AddIn that I created using the Wizard. For more information on creating an AddIn, try this article.

Here is the code to register/unregister the factory with the BuildDetailsView:

 public void OnStartupComplete(ref Array custom)
{
    IVsTeamFoundationBuild VsTfBuild = (IVsTeamFoundationBuild)_applicationObject.DTE.
        GetObject("Microsoft.VisualStudio.TeamFoundation.Build.VsTeamFoundationBuild");

    if (VsTfBuild != null)
    {
        _mySectionFactory = new MySectionFactory();
        VsTfBuild.DetailsManager.RegisterSummaryViewFactory(_mySectionFactory);
    }

}

public void OnBeginShutdown(ref Array custom)
{
    if (_mySectionFactory != null)
    {
        IVsTeamFoundationBuild VsTfBuild = (IVsTeamFoundationBuild)_applicationObject.DTE.
            GetObject("Microsoft.VisualStudio.TeamFoundation.Build.VsTeamFoundationBuild");

        VsTfBuild.DetailsManager.UnregisterSummaryViewFactory(_mySectionFactory);
    }
}

private DTE2 _applicationObject;
private AddIn _addInInstance;
private MySectionFactory _mySectionFactory;

Things you should try:

  1. Play around with priority values for your section like 0, 550, 1000.
  2. Change the name of your factory to "OpenedWorkItem". (This is the name we use for the Latest Activity factory).
  3. Do something that takes a long time in the LoadData or just add a Thread.Sleep.

Let me know how it goes!