動的な計算を「歴史」に変える技術:MS Project VBAでカスタムフィールドを静的に保存する極限の設計術
こんにちは。現場の設計図をコードで描き変える、あなたのエンジニア仲間です。
MS Projectを使ってプロジェクト管理をしていると、必ずと言っていいほど「カスタムフィールド(ユーザー定義フィールド)」の壁にぶつかります。
「計算式を入れて、進捗率に応じたコストを自動計算させる。便利だ!」
そう思って使い始めたはずなのに、ふとした瞬間に気づくのです。
「あ、計算式で出している値は、スケジュールが変わると書き換わってしまう……。先週時点の数値、どこに行ったんだっけ?」
そう、カスタムフィールドの「計算式(Formula)」は、常に最新の状態を反映しようとする「動的な存在」です。しかし、プロジェクト管理において、私たちは「過去の特定の時点での数値」という「静的な記録(スナップショット)」を喉から手が出るほど欲しがる瞬間があります。
今日は、マクロの記録から一歩踏み出し、「データのライフサイクル」を制御するための重要なテクニック、『計算結果を静的な値に変換して保存する手法』を伝授します。ここをクリアすれば、あなたのProject VBAは「単なる自動化ツール」から「信頼できる記録システム」へと進化します。
1. 本質を理解する:Formula(数式)と Value(値)の分離
まず、アーキテクトとして最も重要な概念を整理しておきましょう。MS Projectのカスタムフィールドには、実は2つの側面があります。
1. Formula(数式層): 「A × B を計算せよ」というロジック。
2. Value(データ層): 計算された結果としての実体。
通常のカスタムフィールドに計算式を設定すると、Projectはタスクが更新されるたびに「数式層」を再評価し、「データ層」を上書きしてしまいます。これでは、過去の記録は残せません。
これを解決する黄金律は、「計算用のフィールド」と「保存用のフィールド」を分けることです。
| フィールドの役割 | 設定内容 | 性質 | 目的 |
|---|---|---|---|
| 計算用フィールド | 計算式(Formula)を入力 | 動的(常に変化) | 現在の最新値を算出する |
| 保存用フィールド | 計算式は空にする | 静的(変化しない) | 計算結果を「記録」として固定する |
この2つを、VBAという「橋渡し役」を使って、特定のタイミングでコピーする。これが今回の設計思想です。
2. 実装の設計図:スナップショット・プロトコル
では、具体的にどのようなコードを書けばよいのか。
「計算用フィールド(例:`Text2`)」の値を、「保存用フィールド(例:`Text1`)」にコピーする、実戦的なコードを見ていきましょう。
実装コード例
Option Explicit
''' <summary>
''' カスタムフィールドの計算結果を、静的な保存用フィールドにコピーする
''' </summary>
Sub CaptureTaskSnapshot()
Dim t As Task
Dim count As Long
' パフォーマンス向上のための定石:画面更新を停止
' 大規模なプロジェクトでは、これが実行速度に劇的な差を生みます
Application.ScreenUpdating = False
On Error GoTo ErrorHandler
count = 0
' プロジェクト内の全タスクをループ処理
' ActiveProject.Tasks は、削除されたタスクが Nothing になっている場合があるため注意が必要
For Each t In ActiveProject.Tasks
' タスクオブジェクトが存在するか、および削除されていないかを確認
If Not t Is Nothing Then
' 【ここが核心】
' Text2 (計算用フィールド) の値を、Text1 (保存用フィールド) にコピーする
' 計算式が入っているフィールドは、.Text や .Value でその「結果」が取得できる
t.Text1 = t.Text2
count = count + 1
End If
Next t
' 画面更新を再開
Application.ScreenUpdating = True
MsgBox "スナップショットの作成が完了しました。" & vbCrLf & _
"処理タスク数: " & count & " 件", vbInformation, "完了"
Exit Sub
ErrorHandler:
Application.ScreenUpdating = True
MsgBox "エラーが発生しました。処理を中断します。" & vbCrLf & _
"エラー内容: " & Err.Description, vbCritical, "エラー"
End Sub
3. プロが教える「陥りやすい3つの罠」
コードが書けただけでは、プロの仕事とは言えません。現場で必ず遭遇する「エラー」と「パフォーマンス低下」を回避するための知見を共有します。
① 「Nothing」の恐怖(オブジェクトの生存確認)
MS Projectのタスクリスト(`Tasks`コレクション)には、ユーザーによって削除されたタスクの「残骸」が `Nothing` として含まれていることがあります。
`For Each t In ActiveProject.Tasks` でループを回す際、`If Not t Is Nothing Then` というチェックを怠ると、「オブジェクト変数または With ブロック変数が設定されていません」というエラーでプログラムが止まってしまいます。これは初学者が最も陥りやすい罠です。
② 型の不一致(Type Mismatch)
例えば、`Text1`(テキスト型)に、計算結果として数値が入っている場合は問題ありません。しかし、もし計算用フィールドが `Number1`(数値型)で、保存先が `Text1` の場合、あるいはその逆の場合、データ変換のルールを意識する必要があります。
基本的には、「保存先は、計算結果を格納できる十分な容量(型)を持つフィールドにする」のが鉄則です。
③ パフォーマンスの重み(ScreenUpdating)
タスクが数千件あるプロジェクトで、一つずつセルを書き換える様子を画面に表示させると、MS Projectは描画処理にリソースを割いてしまい、処理が極端に遅くなります。
`Application.ScreenUpdating = False` は、単なる「おまじない」ではなく、「計算資源をロジックに集中させるための、エンジニアの規律」だと考えてください。
4. まとめ:ここをクリアすれば、基本はバッチリです
今回の手法をまとめると、以下のステップになります。
1. 設計: 「計算用(動的)」と「保存用(静的)」の2つのフィールドを用意する。
2. 実装: VBAで `保存用フィールド = 計算用フィールド` のコピー処理を書く。
3. 防御: `Nothing` チェックと `ScreenUpdating` 管理を徹底する。
この「動的な値を、ある瞬間に静的な値として固定する」という考え方は、MS Projectに限らず、あらゆるデータベース操作やシステム設計に応用できる極めて普遍的な概念です。
これができるようになると、「昨日の時点の進捗はどうだったか?」「計画変更前のコストはどうなっていたか?」という、プロジェクトマネージャーが最も求める問いに対して、データに基づいた回答ができるようになります。
一歩ずつ、着実に。
コードの裏側にある「データの流れ」を感じ取れるようになれば、あなたはもう立派な自動化エンジニアです。
応援していますよ。また次の技術解説でお会いしましょう!
