Project VBAを掌握する極限の知見:カスタムフィールドに「動的な計算ロジック」を宿らせる
こんにちは。現場の設計図(アーキテクチャ)を描き、自動化の力でプロジェクトの停滞を打ち破るチーフアーキテクトです。
マクロの記録機能を使って、「あ、ボタンを押せばこの操作ができるんだ」と感動した時期は、誰にでもあります。それは素晴らしい第一歩です。しかし、もしあなたが「毎回マクロを走らせないと値が更新されない」「リソースが増えるたびにコードを書き直している」といった壁にぶつかっているなら、それはあなたが「単なる操作の自動化」から「システムの自動化」へと進化する境界線に立っている証拠です。
今日、皆さんに伝授するのは、Microsoft Projectの真の力を引き出すテクニック。それは、「VBAを使って、カスタムフィールドの中に『計算式(Formula)』を動的に書き込む」という手法です。
これをマスターすれば、あなたは「値を書き込む人」ではなく、「プロジェクトのルールを定義する設計者」になれるはずですよ。
1. なぜ「値を書き込む」のではなく「式を書き込む」のか?
ここが、初級者と中級者を分ける決定的な境界線です。
多くの初心者は、以下のようなアプローチを取りがちです。
1. リソースの属性(ランクなど)を読み取る。
2. VBA内で計算を行う。
3. 計算結果をカスタムフィールドに「値」として書き込む。
これでは不十分なのです。なぜなら、「リソースの属性が後から変更されたとき、計算結果が古いまま残ってしまう」からです。
プロのエンジニアが目指すべきは、「リソースの属性が変われば、カスタムフィールドの値も即座に、自動的に再計算される仕組み」です。そのために、VBAの仕事は「計算結果を書き込むこと」ではなく、「カスタムフィールドに『こう計算せよ』という命令(式)をセットすること」に変わります。
2. 設計図:リソース単価の動的計算ロジック
今回のシナリオを定義しましょう。
- 前提条件:
- `Text1` フィールドに、リソースの「ランク(Senior, Middle, Junior)」が入っている。
- `Cost1` フィールドに、「計算後の単価」を表示させたい。
- ロジック:
- `Senior` なら 5,000円
- `Middle` なら 3,000円
- `Junior` なら 1,500円
- それ以外は 1,000円
このロジックを、VBAを使って `Cost1` フィールドの「数式プロパティ」に流し込みます。
3. 実装:魂のコード・エグゼキューション
それでは、実際に動くコードを見ていきましょう。このコードは、単に値をセットするのではなく、Projectの計算エンジンに「ルール」をインストールするものです。
Option Explicit
''' <summary>
''' リソースのカスタムフィールドに、ランクに基づいた動的なコスト計算式を設定します。
''' </summary>
Sub SetupResourceCostFormula()
Dim pjApp As MSProject.Application
Set pjApp = Application
Dim fd As CustomFieldDefinition
Dim targetFieldIndex As Integer
Dim formulaString As String
' --- 設定エリア ---
' 今回はリソース(Resource)の Cost フィールド(pjCost)を対象にします
' Index 1 は Cost1 フィールドを指します
targetFieldIndex = 1
' 構築する計算式(IIf関数をネストして条件分岐を作ります)
' Projectの数式内では、フィールド名は [Text1] のようにブラケットで囲む必要があります。
' 注意:VBAの文字列内でダブルクォーテーションを使う場合は、"" と2つ重ねる必要があります。
formulaString = "IIf([Text1]=""Senior"", 5000, " & _
"IIf([Text1]=""Middle"", 3000, " & _
"IIf([Text1]=""Junior"", 1500, 1000)))"
' ------------------
On Error GoTo ErrorHandler
' 1. カスタムフィールドの定義を探しに行きます
' CustomFieldDefinitions コレクションをループして、適切な型とインデックスを見つけます
For Each fd In pjApp.CustomFieldDefinitions
' リソース(pjResource)かつ、指定したインデックスのコスト(pjCost)フィールドか判定
If fd.Type = pjResource And fd.Field = pjCost And fd.Index = targetFieldIndex Then
' 2. 既存の数式をクリアし、新しい数式を流し込む
' ここが「設計」の核心です。
fd.Formula = formulaString
MsgBox "成功: リソースの Cost" & targetFieldIndex & " に計算式をセットしました。" & vbCrLf & _
"設定された式: " & formulaString, vbInformation
Exit Sub
End If
Next fd
MsgBox "エラー: 指定されたカスタムフィールドが見つかりませんでした。", vbExclamation
Exit Sub
ErrorHandler:
MsgBox "予期せぬエラーが発生しました: " & Err.Description, vbCritical
End Sub
4. アーキテクトの視点:陥りやすい「3つの罠」
このコードを動かそうとして、多くの人が躓くポイントがあります。ここを理解していれば、あなたはもう初心者ではありません。
① ダブルクォーテーションの地獄(String Escaping)
Projectの数式内で `”Senior”` と書きたい場合、VBAの文字列として定義すると `”””Senior”””` のように複雑な記述が必要になります。
上記のコードでは、`””Senior””` と記述しています。これは「VBAの文字列の中で、1つのダブルクォーテーションを表現する」ための作法です。ここを間違えると、数式が正しく認識されず、エラーになります。
② オブジェクトの「型」の不一致
`CustomFieldDefinitions` は、タスク用、リソース用、プロジェクト用が混在しています。
`fd.Type = pjResource` という判定を入れずに、ただインデックスだけで指定してしまうと、「タスクのコストフィールドを書き換えるつもりが、リソースのテキストフィールドを壊してしまった」という事故が起こります。必ず `Type` と `Field`(pjCostやpjTextなど)をセットで確認してください。
③ 計算エンジンの遅延
数式をセットした直後、画面上の値が更新されないことがあります。これはProjectの計算エンジンが「まだ計算の必要がない」と判断しているためです。
もしコード内で即座に計算結果を使いたい場合は、`Application.Calculate` を呼び出す癖をつけておきましょう。
5. まとめ:ここをクリアすれば、基本はバッチリです
今回の手法の凄さは、「一度マクロを実行すれば、あとは人間が一切何もしなくても、Projectが勝手に計算を続けてくれる」という点にあります。
1. マクロの記録レベル: 「AをBに書き換える」という点の操作。
2. エンジニアレベル: 「Aが変わればBが自動で変わる」という線(ロジック)の構築。
この「線」の概念が持てたなら、あなたのProject VBAスキルは一段上のステージへ到達しました。次は、この数式をさらに複雑な条件(例えば、工数や期間を掛け合わせるなど)に拡張していくのも面白いでしょう。
さあ、恐れずにコードを書いてみてください。失敗は、より堅牢なアーキテクチャを築くための大切なデータに過ぎません。
応援していますよ。
