Share via


Team System

作業項目のカスタマイズ

Brian A. Randell

サンプル コードのダウンロード

Team Foundation Server の作業項目トラッキング システムには、高度なカスタマイズを可能にするオプションがいくつも用意されています。作業項目のカスタマイズや作業項目のオブジェクト モデルにまだあまり詳しくない方は、私が執筆した過去のコラム (msdn.microsoft.com/ja-jp/magazine/cc301186.aspx) や関連ドキュメントを参考にしてください。今回のコラムでは、カスタム コントロールのサポートを取り上げます。そもそも、作業項目にカスタム コントロールを使用する理由は何でしょう。そう、ユーザー エクスペリエンスを向上すること、別の外部システム (ヘルプ デスク アプリケーションなど) からのデータにリンクすること、グラフ形式で既存の作業項目の表示することなどが挙げられるでしょう。どのような理由であれ、カスタム コントロールが役に立つことには変わりありません。

カスタム コントロール

Team System の 2005 SP1 リリースでマイクロソフトは、Visual Studio 内またはユーザー独自の Windows アプリケーション内で標準の作業項目レンダラーを使用する際に、カスタム コントロールのサポートを追加しました。2008 リリースでは、Team System Web Access 向けに Web ベースのカスタム コントロールのサポートも追加しました。カスタム コントロールは、ユーザー インターフェイスと、作業項目 API を操作するロジックの組み合わせとして作成します。標準の作業項目レンダラー内部でホストされるカスタム コントロールは、System.Windows.Forms.Control クラスから継承し、IWorkItemControl インターフェイスを実装するクラスです。Team System Web Access のカスタム コントロールの場合は、System.Web.UI.Control から継承し、IWorkItemWebControl を実装するクラスを作成します。

作業を始めるにあたって、Visual Studio 2008 Professional 以上、Visual Studio Team System 2008 チーム エクスプローラー、および Team Foundation サーバーへのアクセス許可が必要です。前回のコラムでも触れたように、マイクロソフトから提供されるバーチャル マシン イメージのようなテスト環境を使用することをお勧めします。Visual Studio 内部でクラス ライブラリ プロジェクトを作成してから、テンプレートによって追加される既定のクラスの名前を Rank に変更します。このカスタム コントロールでは、組み込みの作業項目の種類であるタスクで使用される、組み込みのランク フィールド向けのコンボ ボックスを提供します。マイクロソフトのガイダンスには次のように記載されています。「必須。ランク フィールドは、主観的な重要度評価です。ランク フィールドに 1 が設定されている場合、そのタスクは即座に完了すべき最優先タスクです。2 が設定されている場合、そのタスクはランク 1 のすべてのタスクに続いて完了すべき重要タスクです。3 に設定されている場合、ランク 1 とランク 2 のタスクの後に完了させればよい、優先度の低いタスクです。」ただし、レンダリング時点のランク フィールドはオープン テキスト フィールドで、ユーザーは無作為にデータを入力できます。そのため、コンボ ボックスを使用することで、ユーザーが入力する値を制限します。

コードを作成する前に、多数のアセンブリへの参照を追加する必要があります。まず、System.Windows.Forms への参照、次に Team System のいくつかのアセンブリへの参照が必要です。最初に、%Program Files%\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies にある Microsoft.TeamFoundation.WorkItemTracking.Controls.dll への参照を追加します。

クラス ファイルの先頭に、次の Imports ステートメントを追加します。

Imports System.Windows.Forms
Imports Microsoft.TeamFoundation.WorkItemTracking.Controls

既存のテキスト ボックス コントロールをコンボ ボックスに置き換えるので、Rank クラスは ComboBox クラスから継承します。次に、コントロールを作業項目レンダラーがホストするために、IWorkItemControl インターフェイスを実装する必要があります。図 1 に示したインターフェイスは、2 つのイベント、4 つのメソッド、および 4 つのプロパティから構成されています。図 2 に各メンバーとその使用目的の内訳を示します。

図 1IWorkItemControl インターフェイス

Public Interface IWorkItemControl
    Event AfterUpdateDatasource As EventHandler
    Event BeforeUpdateDatasource As EventHandler
    Sub Clear()
    Sub FlushToDatasource()
    Sub InvalidateDatasource()
    Sub SetSite(ByVal serviceProvider As IServiceProvider)
    Property Properties As StringDictionary
    Property [ReadOnly] As Boolean
    Property WorkItemDatasource As Object
    Property WorkItemFieldName As String
End Interface

メンバー説明

AfterUpdateDatasource作業項目値の変更後に、このイベントが発生します。

BeforeUpdateDatasource作業項目値の変更前に、このイベントが発生します。

Clearすべてのデータの表示要素をクリアします。

FlushToDatasourceコントロールに表示されたすべてのデータを作業項目に保存します。通常、作業項目保存操作の一環として行います。

InvalidateDatasource作業項目から最新データを再度読み込んで表示します。

SetSiteVisual Studio 内のサービスにアクセスする必要がある場合、IServiceProvider への参照を提供します。この値は null 値にすることもできます。

Propertiesホストしている作業項目の種類の XML 定義からの、特定フィールドの関連属性を含むプロパティ バッグです。

ReadOnlyコントロールが読み取り専用モードで表示されることを示します。

WorkItemDatasourceアクティブな作業項目インスタンスへの参照です。WorkItem インスタンスにキャストする必要があります。

WorkItemFieldNameコントロールのバインド先フィールド名です。

図 2 IWorkItemControl のメンバー

インターフェイスのプロパティごとに、対応するバッキング フィールドを作成する必要があります。WorkItemDatasource を厳密に型指定される参照として格納するために、%Program Files%\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies にある Microsoft.TeamFoundation.WorkItemTracking.Client.dll アセンブリへの参照を追加します。この参照を追加したら、Microsoft.TeamFoundation.WorkItemTracking.Client 名前空間もインポートします。このようにしてプロパティとバッキング フィールドをリンクしたら、インターフェイスのメソッドを実装できます。

ただし、その前に、コンボ ボックスのデータ要素を定義し、使用する値を簡単に調整できるようにするかどうかを検討する必要があります。値を簡単に調整できる方が望ましければ、コントロールが最新の値を取得できるように、Web サービスのようなメカニズムを実装します。また、文字列を表示する予定があれば、ローカリゼーションの問題も考慮します。この例として、値をハードコーディングする既定のコンストラクターを追加するところを次に示します。作業項目ランタイムは、引数を受け取らない既定のコンストラクターのあるコントロールしかサポートしない点に注意してください。

Public Sub New()
  MyBase.New()

  Me.Items.Add("1 - Must Have")
  Me.Items.Add("2 - Should Have")
  Me.Items.Add("3 - Could Have")
End Sub

また、Clear メソッドの実装も用意します。既定では、作業項目ランタイムがコントロールを読み込むと、表示される値は空白です。この状態に戻すには、Clear メソッドの SelectedIndex プロパティに -1 を設定します。実装するメソッドの最後の 2 つは、バインドされた作業項目フィールドの値と作業項目インスタンスの間の相互の読み込みや、ユーザーが作業項目を保存する場合に値を保持するために必要なメソッドです。InvalidateDataSource メソッドは、コントロールに現在の作業項目からの最新状態のデータを表示したり、前回読み込んだデータをクリアするために使用します。FlushToDataSource メソッドは、作業項目ランタイムがアクティブな作業項目を保存する際に呼び出すメソッドです。図 3 に、Rank カスタム コントロール用の両メソッドの実装を示します。

図 3 FlushToDatasource メソッドと InvalidateDataSource メソッドの実装

Public Sub FlushToDatasource() _
  Implements IWorkItemControl.FlushToDatasource
 
  If SelectedIndex > -1 And workItemDS IsNot Nothing Then
    RaiseEvent BeforeUpdateDatasource(Me, EventArgs.Empty)
    workItemDS.Fields(fieldName).Value = CStr(SelectedIndex + 1)
    RaiseEvent AfterUpdateDatasource(Me, EventArgs.Empty)
  End If
End Sub

Public Sub InvalidateDatasource() _
  Implements IWorkItemControl.InvalidateDatasource
  If workItemDS IsNot Nothing AndAlso _
    Not String.IsNullOrEmpty(workItemDS.Fields(fieldName).Value) Then

    Dim current As Integer = CInt(workItemDS.Fields(fieldName).Value)
    ' Make sure the value returned does not exceed our valid range
    ' If it does, set it to our most important value
    If current > Me.Items.Count Then
      current = 1
    End If
    SelectedIndex = (current - 1)
  Else
    Clear()
  End If
End Sub

FlushToDataSource メソッドは、ユーザーが値を選択しているかどうか、およびアクティブな作業項目への参照がまだ有効であるかどうかをチェックします。どちらの条件も満たしていれば、BeforeUpdateDataSource イベントを発生させ、現在値を作業項目に書き込み、AfterUpdateDataSource を発生させます。InvalidateDataSource メソッドは、アクティブな作業項目への有効な参照があるかどうかをチェックします。有効な参照があれば、バインドされたフィールド内に現在格納されている値があるかどうかをチェックします。値があれば、その値を取得します。この値は文字列として格納されているため、整数にキャストします。さらに、範囲チェックを行い、値がコントロールでサポートされる範囲を超えていないことを確認します。今回の例の範囲は 1 ~ 3 です。サポート範囲外の場合は 1 を設定しています。代わりに、ユーザーがサポート範囲内の値を選択できるユーザー インターフェイスを用意したり、別のエクスペリエンスを用意することも可能です。最後に、アクティブな作業項目がない場合、またはユーザーが値を選択していない場合は、Clear メソッドを呼び出してコントロールをリセットします。図 4 に Rank カスタム コントロールの完全な実装を示します。

図 4 Rank カスタム コントロール

Imports System.Windows.Forms
Imports Microsoft.TeamFoundation.WorkItemTracking.Controls
Imports Microsoft.TeamFoundation.WorkItemTracking.Client
Imports System.Collections.Specialized

Public Class Rank
  Inherits ComboBox
  Implements IWorkItemControl
  ' Backing fields
  Private fieldName As String
  Private fReadOnly As Boolean
  Private serviceProvider As IServiceProvider
  Private workItemDS As WorkItem
  Private workItemProperties As StringDictionary

  Public Event AfterUpdateDatasource( _
    ByVal sender As Object, ByVal e As System.EventArgs) _
    Implements IWorkItemControl.AfterUpdateDatasource

  Public Event BeforeUpdateDatasource( _
    ByVal sender As Object, ByVal e As System.EventArgs) _
    Implements IWorkItemControl.BeforeUpdateDatasource

  Public Sub New()
    MyBase.New()
 
    Me.Items.Add("1 - Must Have")
    Me.Items.Add("2 - Should Have")
    Me.Items.Add("3 - Could Have")
  End Sub

  Public Sub Clear() Implements IWorkItemControl.Clear
    SelectedIndex = -1
  End Sub

  Public Sub FlushToDatasource() _
    Implements IWorkItemControl.FlushToDatasource

    If SelectedIndex > -1 And workItemDS IsNot Nothing Then
      RaiseEvent BeforeUpdateDatasource(Me, EventArgs.Empty)
      workItemDS.Fields(fieldName).Value = CStr(SelectedIndex + 1)
      RaiseEvent AfterUpdateDatasource(Me, EventArgs.Empty)
    End If
  End Sub

  Public Sub InvalidateDatasource() _
    Implements IWorkItemControl.InvalidateDatasource
    If workItemDS IsNot Nothing AndAlso _
      Not String.IsNullOrEmpty(workItemDS.Fields(fieldName).Value) Then
 
      Dim current As Integer = CInt(workItemDS.Fields(fieldName).Value)
      ' Make sure the value returned does not exceed our valid range
      ' If it does, set it to our most important value
      If current > Me.Items.Count Then
        current = 1
      End If
      SelectedIndex = (current - 1)
    Else
      Clear()
    End If
  End Sub

  Public Property Properties() As StringDictionary _
    Implements IWorkItemControl.Properties
    Get
      Return workItemProperties
    End Get
    Set(ByVal value As System.Collections.Specialized.StringDictionary)
      workItemProperties = value
    End Set
  End Property

  Public Property IsReadOnly() As Boolean _
    Implements IWorkItemControl.ReadOnly
    Get
      Return fReadOnly
    End Get
    Set(ByVal value As Boolean)
      fReadOnly = value
      Enabled = (Not fReadOnly)
    End Set
  End Property

  Public Sub SetSite(ByVal serviceProvider As IServiceProvider) _
    Implements IWorkItemControl.SetSite
    Me.serviceProvider = serviceProvider
  End Sub

  Public Property WorkItemDatasource() As Object _
    Implements IWorkItemControl.WorkItemDatasource
    Get
      Return workItemDS
    End Get
    Set(ByVal value As Object)
      workItemDS = TryCast(value, WorkItem)
    End Set
  End Property

  Public Property WorkItemFieldName() As String _
    Implements IWorkItemControl.WorkItemFieldName
    Get
      Return fieldName
    End Get
    Set(ByVal value As String)
      fieldName = value
    End Set
  End Property
End Class

さて、コントロールを実際に試す前に、追加の手順が 3 つあることを記しておかなくてはなりません。第 1 に、作業項目ランタイム向けにコントロールを定義する簡単な XML 構成ファイルを用意する必要があります。第 2 に、ランタイムがサポートする有効な場所にコントロールのアセンブリと構成ファイル (デバッグする場合は PDB も) を配置する必要があります。第 3 に、ランク フィールドを使用する作業項目の種類の定義を更新し、適切な情報を追加してコントロールを読み込む必要があります。

配置とデバッグ

作成する各カスタム コントロールには、WICC ファイルが必要です。そのため、ホスト アセンブリとコントロールのクラス名を定義する、次のような簡単な XML ファイルを作成します。

<?xml version="1.0" encoding="utf-8" ?>
<CustomControl xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Assembly>BrianRandell.MSDNMag.WICC.Winforms.dll</Assembly>
  <FullClassName>BrianRandell.MSDNMag.WICC.Winforms.Rank</FullClassName>
</CustomControl>

このファイルを作成したら、WICC ファイル、DLL、および PDB を次のどちらかの場所にコピーします。カスタム コントロールを自分のアカウントに限り表示されるようにする場合は、%Local Application Data%\Microsoft\Team Foundation\Work Item Tracking\Custom Controls\9.0 にコピーします。特定のワークステーション上のすべてのユーザーに表示する場合は、%Common Application Data%\Microsoft\Team Foundation\Work Item Tracking\Custom Controls\9.0 にコピーします。いずれの場合も Microsoft フォルダーの下に新しいフォルダー構造を作成する必要があるでしょう。Environment.SpecialFolder.LocalApplicationData 値または Environment.SpecialFolder.CommonApplicationData 値のいずれかを指定して System.Environment.GetFolderPath を使用すると、使用中のローカル ワークステーション上でのパスを取得できます。これらのディレクトリのどちらかにファイルをコピーしたら、コントロールを使用する作業項目の種類の定義を変更します。今回の例では、MSF Agile テンプレートからのタスク作業項目に変更します (詳細については、私が執筆した別のコラム、msdn.microsoft.com/ja-jp/magazine/dd221363.aspx を参照してください)。

ランク フィールドのフォーム レイアウト情報は、次のコードで変更できます。実行時に、作業項目のプラミング コードによってフィールドのレイアウト定義の一部としてリストされる属性のすべてが IWorkItemControl.Properties プロパティ経由でカスタム コントロールに渡されます。Type 属性を既定の FieldControl から、新たに作成したカスタム コントロール名 Rank に変更するのが唯一の変更点です。この変更を行い、チーム プロジェクトに作業項目の種類の新しい定義をインポートしたら、実際にコードを実行することができます。

<Control Type="Rank" FieldName="Microsoft.VSTS.Common.Rank" 
Label="Ran&amp;k:" LabelPosition="Left" 
NumberFormat="WholeNumbers" MaxLength="10"/>

デバッグするには、あらかじめ最新のコードを適切な配置フォルダーにコピーし、変更した作業項目の種類の定義をチーム プロジェクトにインポートしておく必要があります。その後、現在のプロジェクトの [デバッグ] タブ内で [外部プログラムの開始] オプションを選択して、devenv.exe を指定します (図 5 参照)。これでブレークポイントをいくつか設定すれば、デバッグを開始できます。


図 5 [外部プログラムの開始] オプションの設定

運用環境への配置

カスタム コントロールの不具合を解決したら、変更の影響を受ける作業項目を使用する予定のすべてのユーザーのワークステーションにカスタム コントロールを配置する必要があります。配置にはいくつかの方法があります。Visual Studio の組み込みテンプレートを使ってインストール プログラムをビルドする方法、ユーザーのログイン スクリプトの一部として実行され、XCOPY を使って必要なファイルをコピーする簡単なスクリプトを作成する方法などがあります。また、2008 年 10 月にリリースされた Team Foundation Server Power Tools の配置を完了していれば、Power Tools の配置機能を利用することもできます。この機能を利用するには、アセンブリと WICC ファイルを %Team Project Name%/TeamProjectConfig/CustomControls フォルダーにチェックインします。次に、[Team Members] (チーム メンバー) ノードの [Personal Setting] (個人用の設定) にアクセスし、[Install downloaded custom components] (ダウンロードしたカスタム コンポーネントのインストール) を選択します。この機能の利用に当たっては、アセンブリに厳密名を付けることをお勧めします。Power Tools のヘルプ ファイル内に、この機能の詳細情報があります。

注意点

ここまで、カスタム コントロールのメリットについて見てきました。ここで、いくつか注意点があります。まず 1 番の問題点はホストのサポートです。作成したカスタム コントロールは、Visual Studio 2008 内、またはマイクロソフトが提供するカスタム コントロールをホストするアプリケーション内でのみ実行できます。Visual Studio 2005 をサポートする必要がある場合、チーム エクスプローラーの 2005 バージョン向けのバージョンをビルドし、アセンブリと WICC ファイルを個別に配置しなくてはなりません。Team System Web Access についてはどうでしょう。Web ブラウザーに Internet Explorer しか使用していなければ、理論上は同様の手法で問題ありませんが、実際にはうまくいきません。結果は図 6 のようになります。


図 6 Visual Studio 2005 のサポートにはさらに手順が必要

さいわいにも、Team System Web Access の 2008 リリースでは、独自のカスタム コントロール モデルがサポートされます。Team System Web Access をインストールしたコンピューターの %Program Files%\Microsoft Visual Studio 2008 Team System Web Access\Sdk に関連ドキュメントとサンプルがあります。ただし、これは 2008 バージョンを実行しているのみ機能し、2005 バージョンではカスタム コントロールがサポートされません。また、Teamprise のようなサードパーティ製品もサポートされません。最善の解決策は、作業項目の種類の定義に複数の Layout 要素を定義することです。その後、サポートされないホスト、または異なるホストごとに定義を複製します。次に、Target 属性で Layout 要素を修飾します。Windows の場合は値に WinForms を使います。Team System Web Access の場合は Web を使います。Teamprise の場合は Teamprise を使います。詳細については、msdn.microsoft.com/ja-jp/magazine/aa337635.aspx を参照してください。

カスタマイズした Layout 要素を使うとレンダリングの問題は解決されますが、これによって別の小さな問題にも光を当てることができます。Microsoft Excel や Microsoft Project のようなホストはカスタム コントロールを一切サポートしません。作業項目の種類の Layout 要素に調整が行われても影響を受けません。そのため、カスタム コントロールをサポートしないすべてのホストは、標準インターフェイス経由で未加工のデータを公開することになります。ランク フィールドの場合、標準のエディット フィールドになります。どのホストも作業項目に定義されたルールを適用しようとしますが、カスタム コントロール内に実装したカスタム ロジックがそれを阻害します。そのため、カスタム コントロールを使用する場合、コントロールをサポート対象のホストの外部で実行する際は、ユーザーが適切なデータを入力するように教育し、コントロールで適切なデータ検証を実行することが必要です。

Team System 2010

リリース間近の Team System 2010 リリースでは、クライアントおよびサーバーの長所が十二分に活かされています。マイクロソフトは 2009 年 5 月、ベータ 1 をレビュー用にリリースしました。このコラムを執筆している 2009 年秋にはベータ 2 のリリースが予定されています。まだ多少の変更が行われますが、作業項目トラッキング機能はかなり成熟しています。マイクロソフトから提供される機能の中で最も要望が多いのは、おそらく階層構造の作業項目でしょう。とは言うものの、このコラムではベータ 1 をリリースを使用しているため、最終リリースまでに変更が行われ、このコラムの内容がそのまま当てはまらない可能性があります。

Team Foundation Server の以前のリリースでは、作業項目をリンクするという考え方をサポートしています。作業項目を別の作業項目にリンクすると、双方向性の関係を構築することになります。すべての作業項目を任意の作業項目にリンクできます。この機能は便利ではありますが、親子関係を構築できません。また、Microsoft Project でタスクを管理する際のごく一般的手順として、タスクの優先順位を定義できることが必要になるでしょう。さいわいなことに、2010 リリースではこのような分野の問題に対処されています。新しいリンクのセマンティクスをサポートするために、マイクロソフトは作業項目のクエリに新たに 2 つの種類を追加しました。それが作業項目と直接リンク (リンク クエリ)、および作業項目ツリー (ツリー クエリ) です。新たに作業項目のクエリを作成する際、これら 2 つの新しい種類を選択できます。あるいは、以前のリリースで標準のクエリの種類だった作業項目のフラット リストも選択できます。

まず、新しい作業項目の組み込みの種類と作業項目のクエリを見てみましょう。以前のリリースと同様、今回のリリースでも 2 つのプロセス テンプレートが用意されています。開発サイクルの現段階では、MSFT での作業の大半が Agile Software Development v5.0 テンプレートに対するものです。CMMI ベースのバージョンはベータ 2 リリースで大きく更新される予定です。したがって、私は参照用に Agile テンプレートを使用することになります。新しいチーム プロジェクトを作成し、作業項目のノードを展開したら、おなじみのマイ クエリおよびチーム クエリのノードを確認できます。チーム エクスプローラー ウィンドウには細かい機能強化が 2 点施されていることに気付くでしょう。まず、並べ替え順序が変更されました。チーム クエリの前にマイ クエリが並べ替えられます。次に、各ノードに使われているアイコンがカスタマイズされました。チーム クエリ ノードを開くと、定義済みのクエリのリストが大きく変更されていることに気付くでしょう (図 7 参照)。また、Workbook Queries (ワークブック クエリ) という新しいフォルダーが追加されていることにも気付くでしょう。このフォルダーには、新しい Excel ワークブック機能をサポートするクエリが含まれています。このクエリは自由に実行できますが、変更しないでください。変更すると、この特定のクエリに依存するワークブックが機能しなくなる可能性があります。

TFS 2008 TFS 2010 ベータ 1
アクティブなバグ アクティブなバグ
すべての懸案事項 My Bugs (自分のバグ)
すべてのサービス品質要求 My Tasks (自分のタスク)
すべてのシナリオ My Test Cases (自分のテスト ケース)
すべてのタスク 担当作業項目
すべての作業項目 Open Issues (未完了の懸案事項)
すべてのチーム プロジェクトに対する担当作業項目 Open Tasks (未完了タスク)
プロジェクト チェック リスト Open Test Cases (未完了のテスト ケース)
解決済みバグ Open User Stories (未完了のユーザー ストーリー)
未トリアージのバグ Open Work Items (未完了の作業項目)
  P1 and P2 Active Bugs (P1 および P2 のアクティブなバグ)
  解決済みバグ
  User Stories Without Test Cases (テスト ケースのないユーザー ストーリー)

図 7 Team Foundation Server 2010 の定義済みクエリ

新しい名前から全テンプレートに大幅な変更が加えられたことがわかると思います。User Story (ユーザー ストーリー) や My Test Case (自分のテスト ケース) のように新しい作業項目の種類もあります (図 8 参照)。マイクロソフトはコミュニティからの意見に耳を傾け、今回のリリースに多くのフィードバックを採用しています (より一般的に採用されている用語の使用がその一例です)。また、簡素化やレポート機能の向上も図っています。前述の新しい Excel ベースのワークブックは SQL Server Reporting Services を必要としないプロジェクトのトラッキング機能を豊富に提供し、適切な見積もりやリソース トラッキング、バーンダウン チャートのような新しいグラフも提供しています。リリースが迫るにつれて、カスタマイズされたテンプレートやカスタム コードのアップグレード方法など、2010 リリースについてさらに詳しく調べていくつもりです。


図 8 User Story (ユーザー ストーリー) 定義済みクエリ

まとめ

Team Foundation Server の作業項目カスタム コントロールは、チーム内での作業項目の使いやすさを向上する優れたメカニズムを提供します。この機能はまだ完全ではありませんが、マイクロソフトは継続的に改善しています。Team System の 2010 リリースを控え、製品全体を見ても、作業項目トラッキング機能を見ても見通しは明るいと言えます。