【実務・中級編】Taskオブジェクトの「OutlineLevel」を操作してWBSを動的に再構築するアルゴリズム – Project VBA解析バイブル

スポンサーリンク

【Project VBAを掌握する極限の知見】ExcelリストからWBSを自動構築する:Task.OutlineLevelを支配する動的再構築アルゴリズム

こんにちは。開発プロジェクトの現場で幾多のスケジュール崩壊を防いできた、チーフアーキテクトの私だ。

世の中には「Excelで管理したタスクリストをMicrosoft Projectにインポートする」という要件があふれている。しかし、大半のツールやマクロは、インポートした瞬間にタスクの階層が崩壊したり、謎のガントチャートが生成されたりして使い物にならなくなる。

なぜか?
それは、開発者が 「Projectのオブジェクトモデルのライフサイクル」「OutlineLevel(アウトラインレベル)の正しい制御順序」 を理解していないからだ。

今回は、Excelなどのフラットなリスト構造から、MS Projectの厳格な階層構造(WBS)へ動的に変換し、インデントのズレを完全に排除する堅牢なアルゴリズムを授けよう。

なぜ従来のコードは「WBSの再構築」に失敗するのか?

多くのプログラマブルなアプローチでは、上から順にタスクを追加し、その場で `OutlineLevel` を設定しようとする。

‘ 【アンチパターン】やってはいけない実装例
For i = 1 To LastRow
Set t = ActiveProject.Tasks.Add(Name:=excelData(i, 1))
t.OutlineLevel = excelData(i, 2) ‘ <-- ここで直接レベルを設定すると破綻する Next i この実装が地獄を見る理由は以下の通りだ: 1. 追加順序の罠: Projectはタスクを追加した瞬間、デフォルトで親タスクの配下やルートレベルに勝手にねじ込もうとする。
2. インデント方向の制約: `OutlineLevel` は、先行するタスクとの差分(急激なジャンプなど)に対して非常に敏感であり、不正な階層を指定するとCOMエラー、あるいは意図しないガントの歪みを引き起こす。
3. サマリータスクの自動判定: 子を持つ親タスクは、明示的にサマリーとして振る舞わせるか、正しいアウトライン操作(Outdent / Indent)を経由しなければならない。

これらをクリアするためには、「一度すべてのタスクをフラット(最上位レベル)で高速に流し込み、その後に正しい順序でインデント(階層化)を適用する」 という2段階のライフサイクル管理が必要不可欠だ。

堅牢なWBS動的再構築アルゴリズムの設計思想

今回紹介するプロダクションコードの核となる設計思想は以下の3点だ。

1. 一括生成によるパフォーマンスの最大化:
DOM(オブジェクト)への頻繁なアクセスを避け、まずは一気にタスクを生成する。
2. ボトムアップまたは正しい順序でのインデント制御:
`OutlineLevel` を直接代立するのではなく、ProjectのUI操作に相当する `Outdent` / `Indent` メソッド、あるいは正確なレベル計算に基づいたプロパティ操作を行う。
3. トランザクショナルなエラーハンドリング:
途中でデータ不整合が起きても、Project側が半端な状態で固まらないようにする。

【実戦投入可能】プロダクションコード例

以下のコードは、Excelなどの外部データソースから取得した二次元配列(`vData`:1列目がタスク名、2列目がインデントレベル)を想定し、MS Project上で完璧なWBSツリーを構築するプロシージャである。

Option Explicit

‘ =========================================================================
‘ 処理名 : BuildWBSFromList
‘ 概要 : フラットなタスク配列から、MS Projectの正確なWBS階層を構築する
‘ 引数 : vData(N, 1) = タスク名, vData(N, 2) = アウトラインレベル (Long)
‘ =========================================================================
Public Sub BuildWBSFromList(ByRef vData As Variant)
Dim prj As Project
Set prj = ActiveProject

‘ 画面描画と自動計算を停止し、パフォーマンスを極限まで引き上げる
App.ScreenUpdating False

On Error GoTo ErrorHandler

Dim i As Long
Dim t As Task
Dim targetLevel As Long
Dim currentLevel As Long

‘ —————————————————————–
‘ Step 1: まずはすべてのタスクをルートレベル(Level 1)で高速追加する
‘ —————————————————————–
For i = LBound(vData, 1) To UBound(vData, 1)
If Trim(vData(i, 1)) <> “” Then
Set t = prj.Tasks.Add(Name:=vData(i, 1))
‘ 一時的にカスタムフィールド等に本来のレベルを保持させることも可能だが、
‘ 今回は配列のインデックスとタスクIDが1:1に対応することを利用する
End If
Next i

‘ —————————————————————–
‘ Step 2: 階層の整合性を担保しながら、下から順に(あるいは上から適切なデルタで)
‘ OutlineLevelを再構築する
‘ —————————————————————–
‘ ※Projectの仕様上、インデントの適用は「上位から下位へ」正しいコンテキストで
‘ 行うか、あるいは直接 OutlineLevel を代入する場合は、
‘ ジャンプが許容範囲内(+1ずつ深い方へ)であることを確認する必要がある。

Dim previousLevel As Long
previousLevel = 1

For i = 1 To prj.Tasks.Count
If Not prj.Tasks(i) Is Nothing Then
Set t = prj.Tasks(i)
targetLevel = CLng(vData(i, 2))

‘ レベルの急激な跳ね上がり(例: Lv1からいきなりLv3)を防ぐバリデーション
If targetLevel > previousLevel + 1 Then
targetLevel = previousLevel + 1 ‘ 安全装置:階層のトビを補正
End If

‘ OutlineLevelプロパティの設定
‘ ※Projectでは親から子へ順に設定していく必要がある
t.OutlineLevel = targetLevel

previousLevel = targetLevel
End If
Next i

‘ 仕上げとして、プロジェクト全体の再計算を実行
Calculate

App.ScreenUpdating True
MsgBox “WBSの動的再構築が正常に完了しました。”, vbInformation, “成功”
Exit Sub

ErrorHandler:
App.ScreenUpdating True
MsgBox “WBS構築中に致命的なエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“説明: ” & Err.Description, vbCritical, “エラー”
End Sub

現場で絶対に押さえるべき実装上の注意点

1. `ScreenUpdating` の神聖化

MS ProjectのVBAにおいて、タスクの追加やアウトラインの変更ごとに画面やガントチャートが再描画されると、処理速度が数百倍〜数千倍低下する。処理の冒頭で必ず `App.ScreenUpdating False` をかけ、終了時に `True` に戻すこと。これはプロの基本中の基本だ。

2. 階層の「トビ(跳ね上がり)」に対する防御

人間が作成したExcelリストは、しばしば「レベル1の次がいきなりレベル3」といった不正なインデントを含んでいる。ProjectのCOMコンポーネントはこれを嫌い、不正値エラーや構造崩壊を起こす。上記のコードにあるように、「前回のレベル + 1」を超える深さへのジャンプを強制補正するバリデーションを入れるのが、現場で生き残るエンジニアの知見である。

3. データベース・外部ファイル連携時のトランザクション

もしこのデータをSQL ServerやWeb APIから非同期で取得する場合は、ローカルの一時テーブル(あるいは二次元配列)に全件キャッシュしてからProjectへ流し込むこと。Projectのオブジェクトを掴んだまま外部通信を行うような設計は、ネットワーク切断時に即座にタスクファイルが破損するため絶対に避けるべきだ。

結びにかえて

VBAはレガシーな言語と揶揄されることもある。しかし、Microsoft Projectの内部モデルを直接叩き、数万行のスケジュールを一瞬で意図通りにコントロールするこの領域においては、いまだに最強の自動化ツールである。

「なぜ動かないのか」と悩む前に、オブジェクトのライフサイクルと、ツールが要求する「正しい順序」を逆算せよ。
あなたの書くコードが、現場のプロジェクトマネージャーを無駄な手作業から解放する最高の武器になることを期待している。

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