Project VBAの壁を越える:数式では届かない領域を攻略する「ハイブリッド計算」の設計思想
こんにちは。プロジェクト管理の自動化に情熱を注ぐ皆さん、調子はどうですか?
Microsoft Projectのカスタムフィールド(Text1やNumber1など)を使いこなし、数式(Formula)を使って自動計算ができるようになると、「自分でもプロジェクトを制御できる!」という万能感に包まれますよね。それは素晴らしい一歩です。
しかし、ある時あなたは必ず「壁」にぶつかります。
「もし、タスク名に特定の文字列が含まれていて、かつリソースの稼働率が〇%以下で、さらに先行タスクが完了していない場合のみ、この値を計算したい……」
といった、条件が複雑に絡み合うロジックを数式に書き込もうとした瞬間、Projectの数式エディタは「そんな書き方はできないよ」と、無言の拒絶を突きつけてきます。
実は、Projectの数式エンジンは、VBAの関数(User Defined Function)を直接呼び出すことができないという仕様があるのです。
今日は、この壁を突破するためのプロフェッショナルな設計手法「ハイブリッド計算」についてお話ししましょう。ここをクリアすれば、あなたは「マクロを記録する人」から「業務システムを設計するエンジニア」へと進化できますよ。
1. なぜ「数式」だけでは限界があるのか?
まず、敵を知ることから始めましょう。Projectのカスタムフィールドに設定する「数式」は、Excelの数式に近いものですが、実行されている環境が異なります。
- 数式エンジン: Projectのコア機能。非常に高速だが、あらかじめ決められたプロパティ(`Duration`や`Start`など)と、単純な論理演算(`IIf`など)しか扱えない。
- VBAエンジン: ユーザーが自由にコードを書ける強力な環境。複雑な条件分岐、外部ファイル参照、高度な計算が可能。
この二つは「住んでいる世界が違う」ため、数式の中から「VBAで作った関数を呼び出す」という橋渡しができないのです。
陥りやすい勘違い
「VBAで `Function MyCalc()` と書いたんだから、数式欄に `=MyCalc([Text1])` と書けば動くはずだ」
→ 残念ながら、これは動きません。
2. 解決策:ハイブリッド計算の設計思想
では、どうすればいいのか? 答えはシンプルです。
「数式で計算させるのを諦め、VBAが計算結果をフィールドに書き込む」という逆転の発想です。
これを私は「ハイブリッド計算(Hybrid Calculation Pattern)」と呼んでいます。
ハイブリッド計算のワークフロー
1. 器(フィールド)を作る: カスタムフィールド(例:`Text1`)を「計算結果を格納する場所」として定義する。
2. ロジックをVBAで書く: 数式では不可能な複雑なロジックを、VBAのプログラムとして記述する。
3. 一括書き出し(バッチ処理): VBAを実行し、全タスク(または変更があったタスク)をループして、計算結果を`Text1`へ書き込む。
この設計にすることで、Projectの数式エンジンの限界を、VBAの圧倒的な自由度で補完することができるのです。
3. 実践:複雑なリスクスコアを計算するコード
では、具体的なコードを見てみましょう。
例として、「タスクの期間が長く、かつリソースが不足している場合に、リスクレベルを『High』と表示する」というロジックを実装してみます。
Option Explicit
''' <summary>
''' タスクのリスクレベルを計算し、カスタムフィールド(Text1)に書き出すメインルーチン
''' </summary>
Sub CalculateTaskRiskLevels()
Dim t As Task
Dim riskScore As Integer
Dim riskLevel As String
' 画面更新を停止してパフォーマンスを向上させる(重要!)
ScreenUpdating False
On Error GoTo ErrorHandler
' プロジェクト内のすべてのタスクをループ
For Each t In ActiveProject.Tasks
' 空白のタスク行をスキップ
If Not t Is Nothing Then
' --- ここから複雑なロジック(ハイブリッド計算の核心) ---
riskScore = 0
' 条件1: 期間が20日より長い場合はスコア加算
If t.Duration > 20 * Day(t.Duration) Then ' 単位に注意
riskScore = riskScore + 10
End If
' 条件2: リソースが割り当てられていない場合はリスク高
If t.ResourceCount = 0 Then
riskScore = riskScore + 20
End If
' 条件3: 先行タスクが未完了(例:完了率が100%未満)
' ※ここでは簡略化のため、タスクのステータス等で判定
If t.PercentComplete < 100 And t.Start < Date Then
riskScore = riskScore + 15
End If
' スコアに基づいた判定
If riskScore >= 25 Then
riskLevel = "🚨 High Risk"
ElseIf riskScore >= 10 Then
riskLevel = "⚠️ Medium Risk"
Else
riskLevel = "✅ Low Risk"
End If
' --- ロジック終了 ---
' 計算結果をカスタムフィールド「Text1」に書き込む
' ※事前にプロジェクト設定でText1を計算式なしの「テキスト」として用意しておくこと
t.Text1 = riskLevel
End If
Next t
MsgBox "リスク計算が完了しました!", vbInformation, "完了"
CleanUp:
ScreenUpdating True
Exit Sub
ErrorHandler:
MsgBox "エラーが発生しました: " & Err.Description, vbCritical
Resume CleanUp
End Sub
''' <summary>
''' Projectのパフォーマンスを制御するための補助関数
''' </summary>
Private Sub ScreenUpdating(ByVal bEnable As Boolean)
' ProjectにはExcelのようなScreenUpdatingプロパティが直接ないので、
' 実際には計算中の表示更新を抑える工夫が必要ですが、
' ここでは概念的な記述としています。
End Sub
コードのポイント解説
- `If Not t Is Nothing Then`: プロジェクトには空の行が存在することがあります。これを確認せずにプロパティにアクセスするとエラーになるため、プロのコードでは必須の作法です。
- ロジックの分離: `riskScore` を算出する部分は、将来的に「もっと複雑にしたい」となった時に、別の関数として切り出しやすい設計にしています。
- 書き出し先: `t.Text1 = riskLevel`。これがハイブリッド計算のキモです。計算結果を「値」としてフィールドに叩き込みます。
4. 【超重要】エンジニアが陥る「パフォーマンスの罠」
ここで、伝説のアーキテクトとして、皆さんに最も伝えたい「警告」があります。
ハイブリッド計算を実装する際、多くの人が「計算を自動化したい!」という一心で、`Project_Calculate` イベントの中に上記のループ処理を書いてしまいます。
' ⚠️ やってはいけない例
Private Sub Project_Calculate()
Call CalculateTaskLevels ' 計算のたびに全タスクをループ!
End Sub
これは絶対に避けてください。
なぜダメなのか?
`Project_Calculate` イベントは、ユーザーが日付を一つ動かしたり、リソースを一つ変更したりするたびに、Projectが内部で計算を行うたびに発生します。
もし、1000タスクあるプロジェクトで、このイベント内で全タスクをループさせていたらどうなるでしょうか?
1. ユーザーが値を変更する
2. Projectが計算を開始する
3. `Project_Calculate` が走る
4. VBAが1000回ループしてフィールドを書き換える
5. フィールドが書き換わったので、Projectが「値が変わった!」と検知して再び計算を開始する
6. 再び `Project_Calculate` が走る……(無限ループ、または極端な動作遅延)
プロジェクトがフリーズし、最悪の場合、強制終了するしかなくなります。
正解の設計
ハイブリッド計算は、「必要な時だけ、明示的に動かす」のが鉄則です。
- リボンにカスタムボタンを作る。
- 特定の操作(「進捗更新ボタン」など)の後に呼び出す。
- あるいは、タスクの変更を検知するのではなく、一日の終わりにまとめて実行する。
「自動化」とは「勝手に動くこと」ではなく、「必要なタイミングで、正確に動くこと」なのです。
まとめ:一歩先のエンジニアへ
「数式が使えないなら、VBAで書き込めばいい」
このシンプルな考え方こそが、複雑な業務要件をプロジェクト管理ツールに落とし込むための強力な武器になります。
1. 数式の限界を知る(VBA関数は呼べない)
2. ハイブリッド計算を採用する(VBAで計算 $\rightarrow$ フィールドに書き出し)
3. ライフサイクルを意識する(`Project_Calculate` での暴走を防ぐ)
この3点をマスターすれば、あなたの作るプロジェクト管理ツールは、単なるスケジュール表を超え、高度な意思決定支援システムへと進化するはずです。
さあ、恐れずにコードを書き始めましょう。応援していますよ!
