【中級編】MS Project VBA:カスタムフィールドの「更新されない問題」を撲滅する
こんにちは。プロジェクトの自動化、順調に進んでいますか?
「マクロの記録」を卒業し、自分でコードを書き始めたあなたなら、一度はこんな壁にぶち当たったことがあるはずです。
「VBAでタスクの開始日や工数を書き換えたのに、計算式を入れたカスタムフィールドの値が古いままなんです……。どうしてですか?」
これ、実はMS Projectの「仕様」であり、同時に「エンジニアとしての腕の見せ所」でもあるんです。今日は、プロジェクト管理の自動化において避けては通れない「計算エンジンの制御」について、その本質的なメカニズムと、プロが現場で使う解決策を伝授します。
ここをクリアすれば、あなたの作るツールは「動くだけの道具」から「信頼できるシステム」へと進化しますよ。
1. なぜ「値が変わらない」という現象が起きるのか?
まず、MS Projectの頭の中を想像してみましょう。
Excelは、セルに値を入れた瞬間に「あ、計算しなきゃ!」と即座に反応する、いわば「せっかちな計算機」です。しかし、MS Projectは違います。彼は「超巨大な関係性(リレーションシップ)の塊」なのです。
タスクAの終了日が、タスクBの開始日に影響し、それがリソースの負荷に影響し……。もし、一つの値を書き換えるたびにプロジェクト全体を再計算していたら、大規模なプロジェクトでは操作一つに数分かかるようになってしまいます。
そのため、MS Projectの計算エンジンは、パフォーマンスを維持するために「計算を後回しにする(Lazy Calculation)」という戦略をとっています。
VBAで値を書き換えたとき、内部的には「値は変わったけれど、まだ計算結果を反映させるタイミングではないな」と判断されてしまう。これが、カスタムフィールドが古い値を表示し続ける正体です。
2. 解決策:計算エンジンを「叩き起こす」
この「眠っている計算エンジン」を強制的に目覚めさせ、カスタムフィールドに最新の値を反映させるには、VBAから「再計算(Recalculate)」の命令を出す必要があります。
もっとも一般的で強力な命令は、`Application.Calculate` です。
基本のコード例
まずは、最もシンプルに「計算を強制する」コードを見てみましょう。
Sub UpdateCustomFields_Basic()
Dim t As Task
' 1. タスクのデータを書き換える
For Each t In ActiveProject.Tasks
If Not t Is Nothing Then
' 例:タスク名に日付を付与するなどの処理
t.Start = Date + 7
End If
Next t
' --- ここが重要! ---
' 2. 計算エンジンを強制的に実行させる
' これを呼ばないと、カスタムフィールドの計算式の結果が更新されない
Application.Calculate
MsgBox "データの更新と再計算が完了しました!", vbInformation
End Sub
3. 【極意】絶対にやってはいけない「パフォーマンスの罠」
ここからが、初級者と中級者を分ける境界線です。
エンジニアとして最も注意しなければならないのが、「ループ(繰り返し処理)の中で再計算を呼び出すこと」です。
多くの人がやってしまいがちな、「もっとも効率の悪いコード」がこちらです。
❌ 避けるべきアンチパターン(低速コード)
' 【警告】これは大規模プロジェクトでは致命的に遅くなります!
Sub BadPractice_RecalculateInLoop()
Dim t As Task
For Each t In ActiveProject.Tasks
If Not t Is Nothing Then
t.Start = Date + 1
' ループの中で毎回計算させている
' タスクが1000個あれば、1000回プロジェクト全体を再計算することになり、地獄のような時間が流れます
Application.Calculate
End If
Next t
End Sub
✅ プロが書くベストプラクティス(高速コード)
計算の重みを理解したエンジニアは、「一括処理(Batch Processing)」の考え方を持っています。
「データを全部書き換え終わってから、最後に一度だけ計算させる」のです。
' 【推奨】効率的でプロフェッショナルな書き方
Sub GoodPractice_BatchUpdate()
Dim t As Task
' 処理速度を上げるため、画面更新を停止する(お作法)
Application.ScreenUpdating = False
' 1. まずは全てのデータ変更を「一気」に行う
For Each t In ActiveProject.Tasks
If Not t Is Nothing Then
t.Start = Date + 1
' ここでは Application.Calculate は絶対に呼ばない!
End If
Next t
' 2. 全ての変更が終わった「最後の一回」だけ、計算エンジンを叩き起こす
Application.Calculate
' 画面更新を元に戻す
Application.ScreenUpdating = True
MsgBox "高速に更新が完了しました。", vbInformation
End Sub
解説:
計算のコストは「書き換え回数」ではなく「計算エンジンの起動回数」に依存します。
`Application.Calculate` をループの外に出すだけで、処理時間は数分から数秒へと劇的に短縮されます。これが、オブジェクトのライフサイクルを制御するということの本質です。
4. 陥りやすいエラーとトラブルシューティング
もし `Application.Calculate` を使っても値が変わらない場合は、以下のチェックリストを確認してください。
1. 計算式の構文エラーはないか?
カスタムフィールド自体にエラー(`#ERROR`)が出ている場合、VBAから何度叩いても解決しません。まずは手動でカスタムフィールドの式が正しいか確認しましょう。
2. 再計算のモードが「手動」になっていないか?
MS Projectの設定で計算モードが特殊な状態になっている場合があります。基本的には `Application.Calculate` は強制的なものですが、プロジェクトの構造自体が壊れていると反応しないことがあります。
3. リソース(Resource)の更新が必要ではないか?
タスクの変更が「リソース」のカスタムフィールドに影響を与える場合、タスクだけでなくリソースの再計算が必要なケースがあります。その場合は、タスクの更新後に改めて計算を走らせる設計にしましょう。
まとめ:次の一歩へ
MS Project VBAの世界では、「命令した通りに動く」ことと同じくらい、「いかに効率よくエンジンの動きをコントロールするか」が重要になります。
- カスタムフィールドが古い? $\rightarrow$ `Application.Calculate` を使おう。
- 処理がめちゃくちゃ遅い? $\rightarrow$ ループの中で `Calculate` を呼んでいないか確認しよう。
この「一括で書き換えて、最後に一回だけ計算する」というリズムをマスターすれば、あなたの書くコードは劇的に洗練されます。
ここをクリアすれば、Project VBAの基本はバッチリですよ。自信を持って、もっと複雑な自動化に挑戦していきましょう!応援しています。
