【入門編】【中級者向け】ルックアップテーブルの選択肢をVBAで動的に更新する – Project VBA解析バイブル

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

「静的な選択肢」から脱却せよ。Project VBAで実現する、動的ルックアップ・バリデーションの極意

こんにちは。現場の設計図をコードで描き出す、あなたの頼れるシニアエンジニアです。

マクロの記録機能を使って「あ、これ自動化できるな」と気づけたあなたは、すでに素晴らしい第一歩を踏み出しています。でも、プロジェクトが大規模になり、工程が増え、データの種類が複雑になってくると、ある壁にぶつかるはずです。

それは、「カスタムフィールドの選択肢(ドロップダウンリスト)が、プロジェクトの進行に合わせて変わらないこと」という問題です。

「設計フェーズ」では「要件定義」「基本設計」という選択肢があればいい。でも、プロジェクトが「構築フェーズ」に移ったら、それらはもう古い。本来なら選択肢から消えてほしいし、新しい「詳細設計」「実装」が出てきてほしい。

しかし、MS Projectのカスタムフィールドのドロップダウンリストは、一度作ると手動で書き換えない限り「静的」なままです。これをVBAで無理やり書き換えようとするのは、実はあまりスマートな解決策ではありません。

今日は、プロのアーキテクトが実践する「マスターリスト参照型・動的バリデーション」という手法を伝授します。これは、リストそのものを書き換えるのではなく、「今、その選択肢は正しいのか?」をVBAが判断し、データの整合性を守るという、より高度で堅牢なアプローチです。

ここをクリアすれば、あなたの作るツールは「単なる自動化ツール」から「信頼されるシステム」へと進化します。さあ、一緒に見ていきましょう。

1. なぜ「リストの書き換え」を目指してはいけないのか?

エンジニアとして最初に覚えておいてほしい概念があります。それは「UI(見た目)とロジック(論理)を分離せよ」ということです。

MS Projectのカスタムフィールドのドロップダウンリスト(Lookup Table)をVBAで直接操作して中身を書き換えるコードは、実は非常に不安定です。オブジェクトモデルの制約により、エラーが出やすく、プロジェクトファイル(.mpp)の構造自体を壊してしまうリスクがあります。

代わりに、私たちが目指すのは以下の設計パターンです。

1. マスターデータ(正解リスト)を、プロジェクト内の特定の場所に持っておく。
2. バリデーション(検証)ロジックをVBAで書き、カスタムフィールドの値が「マスターデータ」に含まれているかチェックする。
3. 不正な値があれば、警告を出したり、自動的に修正したりする。

これが、プロが現場で採用する「動的な選択肢」の正体です。

2. 実装のシナリオ:フェーズに応じたステータス管理

今回は、以下のルールを自動化する仕組みを作ります。

  • カスタムフィールド(Text1)に「工程ステータス」を入力する。
  • プロジェクトの「現在のフェーズ」(これも特定のタスクに書き込んでおく)に応じて、許可されるステータスが変わる。
  • 許可されていない値が入力されていたら、VBAが検知して警告を出す。

準備するもの

  • マスタータスク: 名前を「`[Master_Config]`」としたタスクを1つ作っておきます。
  • そのタスクの `Text1` に、現在のフェーズ(例:`Design`)を書き込みます。
  • そのタスクの `Text2` に、カンマ区切りの許可リスト(例:`要件定義,基本設計,詳細設計`)を書き込みます。

3. 魂のコード:動的バリデーション・エンジン

それでは、実際のコードを見ていきましょう。初心者の方でも理解しやすいよう、丁寧にコメントを添えています。

Option Explicit

''' <summary>
''' プロジェクトの工程ステータスが、現在のフェーズの許可リストに適合しているか検証する
''' </summary>
Sub ValidateTaskStatus()
    Dim t As Task
    Dim masterTask As Task
    Dim currentPhaseList As String
    Dim allowedValues As Collection
    Dim isValid As Boolean
    Dim errorCount As Integer
    
    errorCount = 0
    Set allowedValues = New Collection
    
    ' --- 1. マスターデータの取得 ---
    ' [Master_Config] という名前のタスクを探す(これが「真実の源」となる)
    On Error Resume Next
    Set masterTask = ActiveProject.Tasks("[Master_Config]")
    On Error GoTo 0
    
    If masterTask Is Nothing Then
        MsgBox "エラー: マスター設定タスク '[Master_Config]' が見つかりません。", vbCritical
        Exit Sub
    End If
    
    ' Text2に格納されたカンマ区切りのリストを取得
    currentPhaseList = masterTask.Text2
    
    ' カンマ区切りの文字列をCollectionオブジェクトに分解して格納
    ' これにより、高速な検索が可能になる
    Dim val As Variant
    For Each val In Split(currentPhaseList, ",")
        allowedValues.Add Trim(val)
    Next val
    
    ' --- 2. 全タスクの検証ループ ---
    ' プロジェクト内のすべてのタスクを走査する
    For Each t In ActiveProject.Tasks
        ' 空白タスクやサマリータスクをスキップするロジック
        If Not t Is Nothing Then
            If Not t.Summary Then
                
                ' Text1に入力されているステータスを取得
                Dim currentStatus As String
                currentStatus = Trim(t.Text1)
                
                ' ステータスが入力されている場合のみチェック
                If currentStatus <> "" Then
                    isValid = False
                    
                    ' 許可リストの中に現在の値があるか確認
                    On Error Resume Next
                    Dim check As String
                    check = allowedValues.Item(currentStatus)
                    If Err.Number = 0 Then
                        isValid = True
                    End If
                    On Error GoTo 0
                    
                    ' --- 3. 不正な値へのアクション ---
                    If Not isValid Then
                        ' 不正な値が見つかったら、タスク名を赤くしてカウントアップ
                        ' (実務では、ここを「警告メッセージ」や「自動修正」に変える)
                        t.FontColor = pjRed
                        errorCount = errorCount + 1
                    Else
                        ' 正しい値ならフォントを標準に戻す
                        t.FontColor = pjBlack
                    End If
                End If
                
            End If
        End If
    Next t
    
    ' --- 4. 結果の報告 ---
    If errorCount > 0 Then
        MsgBox "検証完了: " & errorCount & " 件の不正なステータスが見つかりました。" & vbCrLf & _
               "タスク名が赤色に変わっています。確認してください。", vbExclamation
    Else
        MsgBox "検証完了: すべてのステータスは最新のマスターリストに適合しています。", vbInformation
    End If
    
End Sub

4. プロの視点:ここが「設計」の分かれ道

このコードをただコピペして終わりにするのは、もったいない!
あなたが「エンジニア」として一段上に上がるための、重要な解説をします。

① `Collection` オブジェクトの活用

コードの中で `allowedValues` という `Collection` を使っていますね。
リストの中に特定の文字があるか探すとき、配列を毎回ループで回すよりも、`Collection` を使って「エラーが出なければ存在する」と判定する手法は、非常に高速でスマートです。これは「オブジェクトの特性を理解している」証拠です。

② 「マスタータスク」という概念(Single Source of Truth)

「どこに設定を書いたっけ?」という混乱を防ぐため、設定値を一箇所(`[Master_Config]`タスク)に集約しています。これを専門用語で SSoT (Single Source of Truth:信頼できる唯一の情報源) と呼びます。
設定をコードの中に直接書く(ハードコーディングする)のではなく、プロジェクト内のデータとして持たせることで、プログラミングができないPM(プロジェクトマネージャー)でも、タスクの値を書き換えるだけでシステムを運用できるようになります。

③ エラーハンドリングの重み

`On Error Resume Next` を使うときは細心の注意が必要です。
今回のコードでは、「リストに値があるかどうか」を判定するためだけに限定して使用しています。このように、「エラーが起きることを前提としたロジック」を、最小限の範囲で制御するのが、堅牢なプログラムを書くコツです。

5. まとめ:次のステップへ

いかがでしたか?
「ドロップダウンのリストそのものを操作する」のではなく、「マスターデータを参照して、入力の正しさを判定する」。この考え方の転換こそが、マクロ記録から脱却し、業務自動化エンジニアへと進化するための鍵です。

今回学んだこと:
1. UI(ドロップダウン)の書き換えよりも、ロジック(バリデーション)の構築を優先する。
2. マスターデータをプロジェクト内に定義し、SSoT(唯一の情報源)を作る。
3. Collection等のオブジェクトを活用して、効率的な検索を実現する。

次は、このバリデーションに失敗したときに「自動的に正しい値に書き換える」機能や、「入力時にポップアップで警告を出す(Eventプロシージャ)」といった応用にも挑戦してみてください。

一歩ずつ、着実に。あなたのコードが、プロジェクトの品質を支える強力な武器になる日を楽しみにしています。

ここをクリアすれば、Project VBAの基本はバッチリですよ。応援しています!

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