Using the TeamProjectPicker API in TFS 2010

This post was written by Hayder Casey, a developer on the work item tracking team.

If you have used Visual Studio 2010, you may have noticed the new “Connect to Team Project” dialog:

clip_image002

This replaced the old DomainProjectPicker in Visual Studio 2008. The main change you will notice is the introduction of the left pane, Team Project Collections. This is because in TFS 2008, the server only had one project collection and it was synonymous with the server. A 2010 server can have multiple collections.

Another feature of the dialog is that all server trips are done on the background thread, so it never blocks the UI. The dialog can be dismissed or the selected server can be changed at any time.

Like the previous DomainProjectPicker dialog, the TeamProjectPicker dialog API is public and can be reused in your own application.

Dialog Modes

The dialog has three selection modes:

Select a Team Project Collection Only:
clip_image004

Select a single team project:
clip_image006

Select multiple team projects: (notice the checkboxes in the Team Projects panel)
clip_image002

TeamProjectPicker class

Namespace: Microsoft.TeamFoundation.Client
Assembly: Microsoft.TeamFoundation.Client.dll

     public class TeamProjectPicker : IDisposable
    {
        public TeamProjectPicker();
        public TeamProjectPicker(TeamProjectPickerMode mode, bool disableCollectionChange);
        public TeamProjectPicker(TeamProjectPickerMode mode, bool disableCollectionChange, ICredentialsProvider credentialsProvider);
        public string AcceptButtonText { get; set; }
        public string Text { get; set; }
        public TfsTeamProjectCollection HostActiveTeamProjectCollection { get; set; }
        public ProjectInfo[] SelectedProjects { get; set; }
        public TfsTeamProjectCollection SelectedTeamProjectCollection { get; set; }
        public event CancelEventHandler AcceptButtonClick;
        public void Dispose();
        public void SetDefaultSelectionProvider(ITeamProjectPickerDefaultSelectionProvider provider);
        public DialogResult ShowDialog();
        public DialogResult ShowDialog(IWin32Window parentWindow);
    }
  • TeamProjectPicker()

    Default constructor; initializes the dialog in SingleProject select mode.

     

  • TeamProjectPicker(TeamProjectPickerMode mode, bool disableCollectionChange);

    Constructor.

    mode: the dialog mode, one of:

 public enum TeamProjectPickerMode
{
    NoProject,
    SingleProject,
    MultiProject
}

disableCollectionChange: if true, will not allow changing the selected server and collection (you can only change the selected projects). You should not pass true here if you are using TeamProjectPickerMode.NoProjectMode.

  • TeamProjectPicker(TeamProjectPickerMode mode, bool disableCollectionChange, ICredentialsProvider credentialsProvider);

    Constructor.

    Allows you to pass your own credentialsProvider to use when connecting to servers. The provider will be invoked when the user clicks on “Use different credentials”. This option will show up when the current user doesn’t have permission to the connect to the server:

    clip_image008

     

  • string AcceptButtonText { get; set; }

    The default text to use for the accept button is “Connect”.

     

  • string Text { get; set; }

    The dialog title.

     

  • TfsTeamProjectCollection HostActiveTeamProjectCollection { get; set; }

    The “Servers…” button will invoke the Add/Remove Team Foundation Server dialog (used to manage registered servers). Setting this property will prevent the user from removing this server from the list.

     

  • ProjectInfo[] SelectedProjects { get; set; }

    Returns an array of the selected ProjectInfo objects; in single project select mode the array will only contain 1 item.

    Before invoking the dialog, you can set this property. The passed in project will be selected by default when the dialog is invoked. If you want to set a default set of project for several collections, do not set this property; instead, use SetDefaultSelectionProvider().

     public class ProjectInfo
    {
        public ProjectInfo(string uri, string name, ProjectState status);
    
        public string Name;
        public ProjectState Status;
        public string Uri;
    }
    
  • TfsTeamProjectCollection SelectedTeamProjectCollection { get; set; }

    Gets the selected Team Project Collection. Can also be used to set the default selected collection (the dialog will select this collection when invoked). Again, if you want to specify a default collection for several server do not use this property instead use SetDefaultSelectionProvider().

     

  • event CancelEventHandler AcceptButtonClick

    This event is raised when the user clicks on the Connect button. This gives a chance to do some processing and cancel the click if you choose to before closing the dialog. For example in Visual Studio when clicking on connect and there is already a server connected in Team Explorer we show this confirmation:

    clip_image010

     

  • void SetDefaultSelectionProvider(ITeamProjectPickerDefaultSelectionProvider provider)

In Visual Studio, the connect dialog will remember the user’s previous selection. Say the user connects to Server A, then selects collection B and connects to a couple team projects. Next time the connect dialog is invoked, Server A, collection B, and the previous projects will be automatically selected. Those previous selections are stored on the client and used in the connect dialog by default. However, if you want to use your own defaults instead of the ones from the Visual Studio client, you can implement you own ITeamProjectPickerDefaultSelectionProvider for the dialog to use. Here are the interface members:

 public interface ITeamProjectPickerDefaultSelectionProvider
{
    Uri GetDefaultServerUri(); // The Uri of the server to 
be selected by default
    Guid? GetDefaultCollectionId(Uri instanceUri); // Given a server 
Uri, returns the selected collection Id; you can return null here 
(no collection will be selected)
    IEnumerable<string> GetDefaultProjects(Guid collectionId); // The 
default projects for a given collection. If used with NoProject 
selection mode, you can return null here. If you are using 
SingleProject selection mode, you should only return a list of 
1 item. The string here is the project Uri.
}

Example

Here is some sample console application code that invokes the dialog with MultiProject selection mode and then prints the selection to the console output.

You will need to reference the following assemblies:

  • Microsoft.TeamFoundation.dll
  • Microsoft.TeamFoundation.Client.dll
 using System.Windows.Forms;
using Microsoft.TeamFoundation.Client;

using Microsoft.TeamFoundation.Server;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Application.EnableVisualStyles(); // Makes it look nicer from a console app.
            //"using" pattern is recommended as the picker needs to be disposed of
            using (TeamProjectPicker tpp = new TeamProjectPicker(TeamProjectPickerMode.MultiProject, false))
            {
                DialogResult result = tpp.ShowDialog();
                if (result == DialogResult.OK)
                {
                    System.Console.WriteLine("Selected Team Project Collection Uri: " + tpp.SelectedTeamProjectCollection.Uri);
                    System.Console.WriteLine("Selected Projects:");
                    foreach(ProjectInfo projectInfo in tpp.SelectedProjects)
                    {
                        System.Console.WriteLine(projectInfo.Name);
                    }
                }
            }
        }
    }
}

What if I want to implement my own connection experience? Is there an API I can use?

The APIs used in the connect dialog are public and you can implement your own connection experience with reasonable effort.

I suggest you start by reading this blog post by Taylor Lafrinere: Retrieve the List of Team Project Collections from TFS 2010 Client APIs.

After you read the blog post, and you started to write your own dialog, you may realize that you still need a way to get a list of servers that the user previously connected to. In 2008, we had the RegisteredServer class, but now it’s obsolete. The new replacement is RegisteredTfsConnections class.

RegisteredTfsConnections manages a list of previously connected to server (TfsConfigurationServer) and collections (TfsTeamProjectCollections). The API is straight forward to use. Here are its members:

 public static class RegisteredTfsConnections
    {
        public static RegisteredConfigurationServer GetConfigurationServer(string name);
        public static RegisteredConfigurationServer GetConfigurationServer(Uri uri);
        public static RegisteredConfigurationServer[] GetConfigurationServers();
        public static void RegisterConfigurationServer(TfsConfigurationServer configurationServer);
        public static void UnregisterConfigurationServer(string name);
        public static RegisteredProjectCollection[] GetLegacyProjectCollections();
        public static RegisteredProjectCollection GetProjectCollection(string name);
        public static RegisteredProjectCollection GetProjectCollection(Uri uri);
        public static RegisteredProjectCollection[] GetProjectCollections();
        public static void RegisterProjectCollection(TfsTeamProjectCollection projectCollection);
        public static void UnregisterProjectCollection(string name);
    }

Notes:

RegisteredConfigurationServers represent the server; this is a new concept in 2010. Each server can contain multiple team project collections. A 2008 server is treated in this API as a ProjectCollection. Thus, in the connect dialog server list, we list both:

  • the 2010 servers or RegisteredConfigurationServers
  • 2008 or older servers (we retrieve those by calling GetLegacyProjectCollections)

The name parameter in the above API is the friendly name of the server or collection.

How do I get the actual server object and connect to it after getting this RegisteredConfigurationServer or RegisteredProjectCollection?

You can use the server factories to get server objects:

 public static class TfsConfigurationServerFactory
    {
        public static TfsConfigurationServer GetConfigurationServer(RegisteredConfigurationServer application);
        public static TfsConfigurationServer GetConfigurationServer(Uri uri);
        public static TfsConfigurationServer GetConfigurationServer(RegisteredConfigurationServer application, ICredentialsProvider fallbackCredentialsProvider);
        public static TfsConfigurationServer GetConfigurationServer(Uri uri, ICredentialsProvider fallbackCredentialsProvider);
    }
    public static class TfsTeamProjectCollectionFactory
    {
        public static TfsTeamProjectCollection GetTeamProjectCollection(RegisteredProjectCollection projectCollection);
        public static TfsTeamProjectCollection GetTeamProjectCollection(Uri uri);
        public static TfsTeamProjectCollection GetTeamProjectCollection(RegisteredProjectCollection projectCollection, ICredentialsProvider fallbackCredentialsProvider);
        public static TfsTeamProjectCollection GetTeamProjectCollection(Uri uri, ICredentialsProvider fallbackCredentialsProvider);
    }

There are also some setting you can set on the RegisteredProjectCollection:

 public sealed class RegisteredProjectCollection
{
    public bool AutoReconnect { get; set; }
    public string Name { get; }
    public bool Offline { get; set; }
    public Uri Uri { get; }
}

AutoReconnect: flag to determine if Team Explorer will auto reconnect to this collection upon start up if it is the most recently connected collection.

Offline: If true, a version control bound project will not go online and load the TF server automatically.

Name: Collection friendly name; usually in the form of “server\collection”

Uri: The collection Uri; example: “https://companyServer:8080/tfs/mycollection”

Comments

  • Anonymous
    June 15, 2010
    I'd love to read the rest of this article, but the link seems to point back to itself!

  • Anonymous
    June 25, 2010
    The link doesn't work @ "Read the rest of Hayder Casey's article… >"