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!


Comments (17)

  1. Jonno says:

    What namespace / assembly is the object IVsTeamFoundationBuild found?

  2. Sorry for the late response – my guess is you already found it, but it is in the Microsoft.VisualStudio.TeamFoundation.Build assembly. And that assembly is in the PrivateAssemblies folder under Visual Studio.

  3. Dhanaji says:

    Which namespace is having "IBuildDetailFactory" interface?

  4. Tapas Mondal says:

    Sir,

    I want to add a custom menu in build explorer grid. please help me. how to do this.

    With regards,

    Tapas

  5. Varshant says:

    Nice post.

    But looking to put my custom section next to Summary.

    Is there anyway where I can define the position to my custom section?

    Please help.

    Thanks.

  6. The Priority property determines the position. Lower priorities are first. Ours usually start at 100 and grow by 100.

  7. Varshant says:

    Yes., Priority worked.

    But strucked at getting the details of existing section.

    Is there any way to retrieve the details of existing section, for ex Summary section, to custom section?

    I don't want to call the TFS APIs again to retrieve the build details, as the details are available in the sections.

    Please explore. Thanks

  8. Mike says:

    Thank you for the post.

    As a TFS novice, I'm finding it difficult to complete the implementation of this custom section.  I have successfully created a VS Add-In, implementing the necessary interfaces above, but I'm at a bit of a loss as to the next steps for actually getting this custom section to appear in the build summary.  Install the Add-in on the TFS Server? On the Build Controller? Manually run the Add-In from the VS Tools Menu?  There seems to be a dearth of resources out there for actually completing the implementation of this custom build summary section.

    Thanks for your time!

  9. Hi

    Great post.  I just have one issue.  My customers wish me to remove all the non-custom factories from the view so that only my summary is visible.  Is this possible?

    Cheers

    Darren

  10. Hi Jason,

    I tried to customize build summary to include hyperlink in new section. Hyperlink is visible but when I click on it nothing happens.

    Could you please take a look and help me in sorting it out.

    Paragraph parx = new Paragraph();

    Run run1 = new Run("Text preceeding the hyperlink.");

    Run run2 = new Run("Text following the hyperlink.");

    Run run3 = new Run("Link Text.");

    Hyperlink hyperl = new Hyperlink(run3);

    hyperl.NavigateUri = new Uri("http://www.msn.com&quot;);

    parx.Inlines.Add(run1);

    parx.Inlines.Add(hyperl);

    parx.Inlines.Add(run2);

    Thanks,

    Akshay

  11. Hi Akshay,

    Unfortunately, we don't have a NavigationHost. So, setting the NavigationUri is not sufficient. What I recommend is that you hook the click event of the hyperlink and open the default browser yourself. You can also open the Web page in Visual Studio if you like. It will use an embedded IE and host it inside the document well of Visual Studio. I don't have any sample code for doing this, but you should be able to find something online.

    We will have a much easier way of doing this in our next release. More on that later…

    Thanks,

    Jason

  12. Thanks Jason.

    I got the solution and would like to share it here.

    To get the hyperlink working, I added handler to Click event.

    Hyperlink link = new Hyperlink(new Run(FilePath));

    link.NavigateUri = FileUri;

    link.Click += new System.Windows.RoutedEventHandler(link_Click);

    void link_Click(object sender, System.Windows.RoutedEventArgs e)

    {

       NavigationWindow newWindow = new NavigationWindow();

       newWindow.Source = FileUri;

    }

    All you need to do is type "link.Click +=" (without quotes) then tab twice, VS will do rest.

    Thanks,

    Akshay

  13. Alexander says:

    Hi. Thank you for the post. But I have the same problem as Mike (20 Jul 2011 11:13 AM). Can anyone ask me, how to install this Add-In on the TFS Build Controller?

  14. The Add-In has to be installed on the client machines. It is just a UI component that you need when viewing the build. It does not go on the server or the build machine.

  15. Darío says:

    Hello Jason.

    Post is very clear but i have problems implementing these interficies. I cannot found Microsoft.TeamFoundation.Build.Client;

    Microsoft.TeamFoundation.Build.Controls;

    If a tried to add the reference my visual Studio 2010 cannot found it.

    How can i get it?

    Thank you!

  16. Hi Dario,

    You will most likely have to browse for those assemblies in c:program files (x86)Microsoft visual studio v10.0common7ideprivateassemblies

    Hope that helps,

    Jason

  17. dario2791 says:

    Thank you very much!

    I have already found.