【入門編】【中級者向け】カスタムフィールドの計算結果を静的な値に変換して保存する – Project VBA解析バイブル

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

動的な計算を「歴史」に変える技術: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に限らず、あらゆるデータベース操作やシステム設計に応用できる極めて普遍的な概念です。

これができるようになると、「昨日の時点の進捗はどうだったか?」「計画変更前のコストはどうなっていたか?」という、プロジェクトマネージャーが最も求める問いに対して、データに基づいた回答ができるようになります。

一歩ずつ、着実に。
コードの裏側にある「データの流れ」を感じ取れるようになれば、あなたはもう立派な自動化エンジニアです。

応援していますよ。また次の技術解説でお会いしましょう!

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