【実務・中級編】【初心者向け】Project VBAでタスクの階層(インデント)を操作する基本メソッドの書き方 – Project VBA解析バイブル

スポンサーリンク

【Project VBA】OutlineLevelでWBSの階層構造を完全に掌握する:タスク昇格・降格の要塞設計

開発現場のリーダー諸君。Excelのマクロに飽き足らず、Microsoft Project(MSP)のVBA領域に踏み込んだ君なら、一度はこう絶望したことがあるはずだ。

「なぜ、タスクをインデントしただけなのに、先行・後続タスクのリンクが狂うのか」
「数千行のWBSを生成する際、画面がガタガタ点滅し、処理に何分もかかるのはなぜか」

世の入門書は「`OutlineIndent` や `OutlineOutdent` メソッドを使えば階層が上がります」と平然と書く。だが、プロの現場でそんな場当たり的なメソッドをループ内で乱用すれば、オブジェクトのライフサイクルと再計算エンジンのオーバーヘッドに足元をすくわれ、数日で作った自動化ツールはゴミクズと化す。

今回は、MSPのタスク階層(WBS)操作の核心である `OutlineLevel` プロパティ を取り上げる。単なる「インデントの基本」にとどまらず、バグが絶対に起きない堅牢な設計 と、プロダクション環境で耐えうる極限の最適化コードを伝授しよう。

1. なぜ「インデントメソッド」ではなく `OutlineLevel` なのか?

初心者が最初に陥る罠が、UIの操作をそのままコードにした `Indent` や `Outdent` メソッドの多用だ。これらは「現在地から相対的に」動かすため、マクロが途中で中断したり、再実行されたりした際に、意図せぬ階層崩壊(データの迷子)を引き起こす。

プロダクションコードにおける鉄則は、「絶対位置(Absolute Value)で指定する」 ことだ。

タスクの階層構造は、UIの見た目の操作ではなく、データモデルのプロパティとして厳密に制御すべきである。そこで登場するのが `OutlineLevel` プロパティ である。

  • `OutlineLevel = 1`:大項目(サマリーの親)
  • `OutlineLevel = 2`:中項目
  • `OutlineLevel = 3`:詳細タスク

このように、各タスクが「どの階層に属すべきか」を数値として直接保持・代号化することで、何度マクロを走らせても結果がブレない冪等性(いどせい)を担保できるのだ。

2. パフォーマンスの魔物:「画面描画」と「再計算」を断ち切れ

数千行のWBSをプログラムで動かす際、最大の敵はMSPの標準機能である「自動再計算」と「UI描画」だ。
タスクを1行追加・階層変更するたびに、MSPは全体の日程計算と画面の再描画を走らせる。これを放置してコードを書くエンジニアは、業務自動化の資格がない。

堅牢なツールを作るための3大原則:
1. 画面描画の完全停止 (`ScreenUpdating = False` は使えない。MSPでは `App.Calculation` を制御する)
2. 自動計算の抑制
3. エラー時の確実に元の状態に戻す例外処理(トランザクション的思考)

これらを実装したプロ仕様の骨組みを見ていこう。

3. 【実戦】堅牢性と速度を極めたWBS階層化エンジン

外部のCSVやデータベースからタスク群を読み込み、`OutlineLevel` を使って一瞬で美しいWBSツリーを構築するプロダクションコードだ。そのままコピペしてプロジェクトに組み込んでほしい。

Option Explicit

‘ ==============================================================================
‘ 処理名 : BuildWBSHierarchy
‘ 概要 : 外部データ等から取得したタスク群のOutlineLevelを一括制御し、
‘ 堅牢かつ高速にWBSの階層構造を構築する
‘ ==============================================================================
Public Sub BuildWBSHierarchy()
Dim prj As Project
Set prj = ActiveProject

‘ パフォーマンスと整合性を守るための退避変数
Dim originalCalc As Long
originalCalc = Application.Calculation

On Error GoTo ErrorHandler

‘ 1. 【最重要】自動計算を「手動」に切り替え、MSPの無駄な再計算地獄を防ぐ
Application.Calculation = pjCalcManual

‘ 2. トランザクション開始(メッセージボックス等を抑制して高速化)
App_StartOptimization

Dim t As Task
Dim targetLevel As Integer

‘ — 【シミュレーション用ループ】 —
‘ 実際の現場では、ここでDBやExcelのレコードセットをループさせます。
‘ 今回は簡易的に、タスク名に含まれるプレフィックスや別領域のデータから
‘ OutlineLevelを決定していると仮定します。
For Each t in prj.Tasks
If Not t Is Nothing Then

‘ 【ロジックの核心】
‘ ここでは例としてタスク名(Name)のインデント文字数や
‘ 固有のコード体系から「本来あるべきOutlineLevel」を算出・設定する
targetLevel = GetTargetOutlineLevel(t)

‘ プロパティに直接「絶対値」を書き込むことで、多重実行しても破綻しない
If t.OutlineLevel <> targetLevel Then
t.OutlineLevel = targetLevel
End If

End If
Next t

‘ 3. すべての階層が整った段階で、一度だけ再計算を走らせる
Application.Calculation = originalCalc
Calculate

‘ 4. 最適化の終了
App_EndOptimization

MsgBox “WBSの階層構造の構築が正常に完了しました。”, vbInformation, “VBA Automation”
Exit Sub

ErrorHandler:
‘ 異常発生時も確実に計算モードと画面を復旧させる(ロールバック的アプローチ)
Application.Calculation = originalCalc
App_EndOptimization

MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “System Error”
End Sub

‘ ==============================================================================
‘ 補助関数 : タスクごとの正しい階層レベルを判定するロジック(Stub)
‘ ==============================================================================
Private Function GetTargetOutlineLevel(ByRef t As Task) As Integer
‘ 例:名前に “【大】” があれば Level 1, “【中】” なら Level 2, それ以外は Level 3
If Left(t.Name, 4) = “【大】” Then
GetTargetOutlineLevel = 1
ElseIf Left(t.Name, 4) = “【中】” Then
GetTargetOutlineLevel = 2
Else
GetTargetOutlineLevel = 3
End If
End Function

‘ ==============================================================================
‘ 最適化ラッパー関数
‘ ==============================================================================
Private Sub App_StartOptimization()
‘ Microsoft ProjectにおけるUI描画・警告の抑制
On Error Resume Next
Application.DisplayAlerts = False
‘ 注: MSPのバージョンや仕様により使用できないUI制御メソッドは除外・スルーする
End Sub

Private Sub App_EndOptimization()
On Error Resume Next
Application.DisplayAlerts = True
End Sub

4. プロジェクトリーダーからの実践的アドバイス

このコードを運用するにあたり、データベースやExcel連携を行う際の「実務上の急所」をいくつか伝えておく。

1. サマリータスク(親タスク)の期間を手動で触るな
`OutlineLevel` を変更して親子関係が構築されると、MSPは親タスクの期間(Duration)を自動的に子タスクの集計値に書き換える。もし外部DBから親タスクの「手動設定された工数」を無理やり流し込もうとすると、MSPのエンジンとコンフリクトを起こす。親タスクはあくまで「サマリー(自動計算)」として扱い、工数は末端の詳細タスク(Leaf Task)にのみ持たせる設計を徹底せよ。

2. タスクの並び順(IDの順序)の担保
`OutlineLevel` を変更する際、親タスクが子タスクよりも若いID(上)に存在しなければならない。データベースからタスクをバルクインサートする際は、必ずWBSのツリー順(Pre-order traversal)に並べ替えてからMSPへ流し込むこと。順序が逆転している状態で `OutlineLevel` を操作すると、意図しない階層ズレやランタイムエラーの温床となる。

総括

タスクの階層操作は、単なる「見た目のインデント」ではない。プロジェクトという複雑な生命体の「骨格」をコードで組み上げる神聖なプロセスだ。
`OutlineLevel` を制する者は、Project VBAを制する。場当たり的なコードを捨て、トランザクションと冪等性を意識した堅牢なアーキテクチャで、真の業務効率化をその手で成し遂げたまえ。

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