【実務・中級編】【中級者向け】タスクの進捗率に応じた色分けをカスタムフィールドで実現する – Project VBA解析バイブル

スポンサーリンク
スポンサーリンク

視覚的マネジメントを支配せよ:MS Project VBAにおけるGraphicalIndicatorの設計思想

プロジェクト管理の現場において、進捗状況を「言葉」で説明するのは二流だ。一流は「一目でわかる視覚情報」を設計する。

MS Projectを使用しているエンジニアやPMの多くは、タスクの進捗率(% Complete)を見て一喜一憂する。しかし、プロジェクトが大規模化し、数百、数千のタスクが並ぶとき、数値の羅列はただのノイズに変わる。ここで求められるのが、「異常値(遅延や停滞)を即座に視覚化する仕組み」だ。

多くの開発者は、VBAでタスクをループし、条件に合致したタスクのフォント色を力技で書き換えようとする。それは致命的な設計ミスだ。

本稿では、オブジェクトのライフサイクルと計算コストを最適化し、MS Projectのネイティブ機能である`GraphicalIndicator`(グラフィカルインジケーター)をVBAから制御することで、堅牢かつ高パフォーマンスな「視覚的ダッシュボード」を構築する極限の知見を伝授する。

1. なぜ「フォントの色変更」はプロのコードではないのか

まず、初心者が陥る「アンチパターン」を指摘しておく。

' 【アンチパターン】非効率で壊れやすい実装
For Each t In ActiveProject.Tasks
    If t.PercentComplete < 50 And t.Start < Date Then
        t.FontColor = pjRed ' 直接フォントの色をいじる
    End If
Next t

このコードには、アーキテクトの視点から見て3つの致命的な欠陥がある。

1. 計算コストの爆発: タスクが増えるたびに、あるいはプロジェクトを開くたびに、全タスクに対してループと条件判定を走らせる必要がある。これは $O(n)$ の計算量であり、大規模プロジェクトでは動作を著しく重くする。
2. 状態の非整合性: VBAで色を塗りつぶすと、ユーザーが手動で色を変えたのか、プログラムが変えたのかの区別がつかなくなる。また、進捗が更新された際に「色を戻す」処理も別途必要になり、ロジックが複雑化する。
3. データの分離原則の欠如: 「データ(進捗率)」と「表現(色)」が密結合している。本来、表現は「計算式」や「インジケーター」という定義に委ねるべきであり、VBAは「定義のセットアップ」に徹するべきだ。

正解は「GraphicalIndicator」の定義である

我々が目指すべきは、「条件(Rule)をカスタムフィールドの定義として一度だけ流し込み、その後の判定はMS Projectのエンジンに任せる」という宣言的なアプローチだ。

2. GraphicalIndicatorのオブジェクトモデルを掌握する

`GraphicalIndicator`は、`CustomField`オブジェクトに紐付くプロパティである。これを操作することで、特定の数値を「アイコン」や「色」に変換するルールを、プロジェクトのメタデータとして書き込むことができる。

設計の鍵となるのは、以下のステップだ。
1. 対象となる`CustomField`を特定する。
2. 既存のインジケーター設定をクリアする(残留した古いルールによるバグを防ぐ)。
3. 条件(`IndicatorType`、`Value`、`Color`など)を定義し、追加する。

3. プロダクション級の実装例:進捗監視インジケーターの自動構築

以下に、タスクの進捗率に基づき、「遅延しているか」「順調か」をアイコンで表示するカスタムフィールドを自動設定する、堅牢なコードを示す。

Option Explicit

''' <summary>
''' タスクの進捗状況を可視化するカスタムフィールドを構築する。
''' </summary>
Public Sub SetupProgressVisualizer()
    Dim pjApp As MSProject.Application
    Set pjApp = Application
    
    Const TARGET_FIELD_NAME As String = "Visual_Status"
    Dim targetField As MSProject.CustomField
    Dim i As Integer
    
    On Error GoTo ErrorHandler
    
    ' 1. 既存のカスタムフィールドの存在確認と取得
    ' 効率のため、全フィールドをループして名前で照合する
    Set targetField = Nothing
    For i = 1 To pjApp.CustomFields.Count
        If pjApp.CustomFields(i).Name = TARGET_FIELD_NAME Then
            Set targetField = pjApp.CustomFields(i)
            Exit For
        End If
    Next i
    
    ' 2. フィールドが存在しない場合は新規作成(設計の柔軟性)
    If targetField Is Nothing Then
        ' CustomFieldのインデックスは1から始まる。
        ' 本来は空いているフィールドを探すロジックが必要だが、ここでは簡略化。
        MsgBox "ターゲットとなるカスタムフィールド [" & TARGET_FIELD_NAME & "] が見つかりません。" & vbCrLf & _
               "事前にプロジェクト設定でフィールドを確保してください。", vbExclamation
        Exit Sub
    End If

    ' 3. 既存のインジケーター設定をクリーンアップ
    ' これを行わないと、コードを再実行した際にルールが重複・蓄積される
    targetField.GraphicalIndicators.Clear

    ' 4. グラフィカルインジケーターの定義(宣言的プログラミング)
    ' ここでは、PercentComplete(進捗率)をベースにしたルールを定義する
    ' ※ 注意:カスタムフィールド自体に数式(Formula)が設定されていることが前提
    
    With targetField.GraphicalIndicators
        ' 【ルール1:赤】進捗が0% かつ 開始日が過ぎている(重大な遅延)
        ' IndicatorType: 1 = Icon, 2 = Color, 3 = Both
        .Add
        With .LastAdded
            .IndicatorType = pjIndicatorBoth
            .Condition = pjConditionLessThan
            .Value = "0" ' 実際には数式の結果と照合される
            ' ここでは、カスタムフィールドの数式自体で「遅延フラグ」を計算させておくのがベスト
            ' 今回は「進捗率が低い」という単純な条件で実装例を示す
            .Color = pjRed
            ' アイコン(例:停止マーク)の設定などはAPI仕様に基づき行う
        End With
        
        ' 【ルール2:黄】進捗が50%未満(注意)
        .Add
        With .LastAdded
            .IndicatorType = pjIndicatorBoth
            .Condition = pjConditionBetween
            .Value = "1,49" 
            .Color = pjYellow
        End With

        ' 【ルール3:緑】進捗が100%(完了)
        .Add
        With .LastAdded
            .IndicatorType = pjIndicatorBoth
            .Condition = pjConditionEquals
            .Value = "100"
            .Color = pjGreen
        End With
    End With

    MsgBox "インジケーターの再構築が完了しました。", vbInformation
    Exit Sub

ErrorHandler:
    MsgBox "エラーが発生しました。" & vbCrLf & _
           "Error No: " & Err.Number & vbCrLf & _
           "Description: " & Err.Description, vbCritical
End Sub

アーキテクトの解説:なぜこの設計なのか

1. `GraphicalIndicators.Clear` の徹底:
VBAによる自動設定において最も多いバグは、「前回の実行結果が残っていること」だ。`Clear` を呼び出すことで、常に「現在のコードが定義した状態」が正解となる、冪等性(Idempotency)を確保している。
2. `LastAdded` プロパティの活用:
インデックス番号で指定するのではなく、`LastAdded` を使うことで、コードの可読性を高め、かつ追加順序に依存しない堅牢な記述を実現している。
3. 数式(Formula)との分離:
上記のコードでは、`targetField.Formula` に「遅延判定ロジック」が既に書き込まれていることを前提としている。VBAは「見た目のルール」を、Formulaは「計算のロジック」を担当する。この関心の分離(Separation of Concerns)こそが、保守性の高いシステムを作る絶対条件だ。

4. 実務における発展的な注意点

データベース(Excel/SQL)連携時の罠

もし、外部のデータベースから進捗データを流し込み、その直後にこのインジケーターを適用する場合、「再計算のタイミング」に注意せよ。
データの流し込み(Write)が行われた直後は、MS Projectの内部エンジンが計算を完了していない場合がある。大規模なデータ更新を行う場合は、`DoEvents` を挟むか、計算完了を待機する設計を検討すべきだ。

パフォーマンスの限界

`GraphicalIndicator` は非常に高速だが、カスタムフィールドの数式(Formula)が極めて複雑(例:他のタスクの値を参照する重い数式)な場合、インジケーターの描画がボトルネックになる。
「見た目」を豪華にする前に、その「根拠となる計算式」が軽量であることをまず確認せよ。

結論

MS Project VBAにおける真の自動化とは、単に値を書き換えることではない。「プロジェクトが自律的に状況を判断し、視覚的に報告する仕組み」を構築することである。

`GraphicalIndicator` を使いこなし、計算コストを抑え、宣言的なコードを書く。この一歩が、単なる「ツール作成者」から「業務自動化アーキテクト」へと君たちを昇華させる。

Keep Coding, Keep Designing.

タイトルとURLをコピーしました