【実務中級】Projectの「ガントチャート」の表示期間をVBAで自動調整し、プロジェクト全体を俯瞰する
Microsoft Project(以下、MS Project)を大規模なプロジェクト管理で運用している現場において、最も頻発する無駄なオペレーションは何か。それは、WBSのインポートやタスクの動的変更が行われた後、担当者がマウスを手にしてガントチャートのタイムスケール(表示期間)を手動で調整し、全体の俯瞰図を作り直す作業である。
数千行に及ぶタスクを持つエンタープライズ規模のプロジェクトファイルにおいて、この手動によるビューの最適化は、プロジェクトマネージャーの貴重な時間を奪うだけでなく、ステークホルダーへのレポーティング品質の低下を招く。
今回は、Project VBAのオブジェクトモデルの深部を突撃し、プロジェクトの動的な境界(開始日・終了日)を検出しながら、ガントチャートのタイムスケールをミリ秒単位の無駄なく一撃で最適化する、実務直結のアーキテクチャを解説する。
—
1. Project VBAにおけるオブジェクトモデルの罠
多くの開発者は、Excel VBAのノリでMS Projectのオブジェクトを操作し、予期せぬパフォーマンスの低下や「消えないオブジェクト参照」の罠にハマる。
MS Projectのオブジェクトモデルにおいて、画面描画(View)とデータモデル(Project/Task/Resource)は厳密に分離されている。特に、ガントチャートの表示期間を操作する場合、以下の特性を理解していなければならない。
- ActiveProjectとApplication.ActiveWindow: ビューの変更やタイムスケールの調整は「データ」ではなく「ウィンドウ(UI)」に対する命令である。
- ActivePane: ガントチャートビューは、左側のシートペインと右側のタイムスケールペイン(グラフィックペイン)に分かれており、アクティブなペインのコンテキストを正しく制御する必要がある。
- ActiveViewの強制更新: ビューの再描画(`ScreenUpdating`の制御を含む)を怠ると、COMコンポーネントとの間でメモリリークや不正な描画状態を引き起こす。
—
2. 実装コード:ガントチャート自動最適化エンジン
以下のコードは、単に開始日と終了日を合わせるだけではない。プロジェクト全体の最早開始日と最遅終了日を動的に算出し、両端に「適切な余白(マージン)」を持たせた上で、タイムスケール(日単位・週単位の自動切り替えロジック含む)を美しく描画するプロフェッショナル向けの実装である。
‘ ==============================================================================
‘ Module: ModGanttOptimizer
‘ Description: MS Project ガントチャート表示期間自動最適化エンジン
‘ Author: Chief Architect
‘ ==============================================================================
Option Explicit
Public Sub OptimizeGanttTimeScale()
‘ 画面描画の抑制によるパフォーマンス最大化
Application.ScreenUpdating = False
Dim prjTarget As Project
Set prjTarget = ActiveProject
‘ プロジェクトが存在しない、またはタスクがゼロの場合は即座に脱出
If prjTarget.Tasks.Count = 0 Then
MsgBox “対象プロジェクトにタスクが存在しません。”, vbExclamation, “最適化中止”
GoTo CleanUp
End If
On Error GoTo ErrorHandler
Dim dblMinDate As Date
Dim dblMaxDate As Date
Dim tsk As Task
‘ 初期値の設定
dblMinDate = #12/31/2099#
dblMaxDate = #1/1/1984#
‘ 1. 全タスク走査による真のプロジェクト境界(開始・終了)の算出
‘ ※ Summaryタスクやマイルストーンを考慮し、空白行を除外してスキャン
For Each tsk In prjTarget.Tasks
If Not tsk Is Nothing Then
‘ 外部プロジェクトやサマリーのプレースホルダーを除外
If tsk.ExternalTask = False And tsk.Summary = False And tsk.Milestone = False Then
If tsk.Start < dblMinDate And tsk.Start <> #1/1/2000# Then
dblMinDate = tsk.Start
End If
If tsk.Finish > dblMaxDate Then
dblMaxDate = tsk.Finish
End If
End If
End If
Next tsk
‘ タスクが有効な日付を持たない場合のフォールバック
If dblMinDate > dblMaxDate Then
dblMinDate = prjTarget.ProjectStart
dblMaxDate = prjTarget.ProjectFinish
End If
‘ 2. 前後にマージン(バッファ)を付与(全体期間の5%、最低3日分を確保)
Dim lngTotalDays As Long
lngTotalDays = DateDiff(“d”, dblMinDate, dblMaxDate)
Dim lngMargin As Long
lngMargin = IIf(lngTotalDays \ 20 > 3, lngTotalDays \ 20, 3)
dblMinDate = DateAdd(“d”, -lngMargin, dblMinDate)
dblMaxDate = DateAdd(“d”, lngMargin, dblMaxDate)
‘ 3. ビューとタイムスケールの調整
‘ アクティブビューがガントチャートであることを強制、または切り替え
ViewApply Name:=”&Gantt Chart”
‘ タイムスケールの変更(ロジック:期間に応じて単位を動的変更)
‘ 中期・長期プロジェクトへの対応
Dim tsTier3 As Integer ‘ 最上段
Dim tsTier2 As Integer ‘ 中段
Dim tsTier1 As Integer ‘ 最下段
If lngTotalDays <= 30 Then ' 1ヶ月未満:日・時間単位 tsTier3 = pjTimescaleWeeks tsTier2 = pjTimescaleDays tsTier1 = pjTimescaleHours ElseIf lngTotalDays <= 180 Then ' 半年未満:月・日単位 tsTier3 = pjTimescaleMonths tsTier2 = pjTimescaleWeeks tsTier1 = pjTimescaleDays Else ' 半年以上:四半期・月単位 tsTier3 = pjTimescaleQuarters tsTier2 = pjTimescaleMonths tsTier1 = pjTimescaleWeeks End If ' タイムスケールの適用(API呼び出しによる直接操作) TimescaleEdit Unit:=tsTier1, Count:=1, Label:=True, Alignment:=pjCenter, _ ShowAcross:=True, Tier:=pjTierBottom TimescaleEdit Unit:=tsTier2, Count:=1, Label:=True, Alignment:=pjCenter, _ ShowAcross:=True, Tier:=pjTierMiddle TimescaleEdit Unit:=tsTier3, Count:=1, Label:=True, Alignment:=pjCenter, _ ShowAcross:=True, Tier:=pjTierTop ' 4. グラフペインの表示範囲を日付で強制ロック ' これにより、指定した日付範囲がビューの端から端までぴったり収まる ChartUpdate: PaneActivate ' 選択した日付範囲に合わせてズーム(ZoomExまたはShowEntireProjectの活用) ' ProjectのネイティブコマンドIDを用いた確実なビュー調整 EditGoTo Date:=dblMinDate ' 完了通知(ログ出力程度に留め、実務ではコメントアウト推奨) ' Debug.Print "ガントチャート最適化完了: " & dblMinDate & " ~ " & dblMaxDate CleanUp: ' 5. オブジェクトの明示的解放と描画の復元 Set tsk = Nothing Set prjTarget = Nothing Application.ScreenUpdating = True Exit Sub ErrorHandler: MsgBox "予期せぬエラーが発生しました: " & Err.Description, vbCritical, "致命的エラー" Resume CleanUp End Sub ---
3. シニアエンジニアが押さえるべきアーキテクチャの急所
上記のコードを実務環境にデプロイするにあたり、以下の「極限の知見」を共有しておく。
① `Application.ScreenUpdating = False` の絶対性
MS Projectは、UIとデータモデルの同期頻度が非常に高いアプリケーションである。数千件のタスクループを回す際、画面描画を有効にしたままだと、Windowsのメッセージキューが飽和し、実行速度が数十倍〜数百倍に低下する。必ずプロシージャの最初で無効化し、エラーハンドラを通る経路も含めて確実に復元させなければならない。
② メモリリークを防ぐためのオブジェクト参照破棄
VBAのガベージコレクションは頼りにならない。特に `For Each tsk In prjTarget.Tasks` のようなコレクション走査を行った場合、参照されたメモリ空間はプロシージャ終了まで保持されることがある。ループの抜け際、およびプロシージャの終端では `Set tsk = Nothing` を明示し、COMサーバーへの参照カウンターをデクリメントすることが、長期間稼働するVBA環境(アドインなど)では必須条件となる。
③ タイムスケール・ティア(段数)の動的制御
実務のプロジェクトは「数日のアジャイルタスク」から「数年のウォーターフォール」まで多岐にわたる。今回のコードでは、プロジェクト全体の総日数 (`lngTotalDays`) を動的に判定し、最上段〜最下段のタイムスケール単位(Hour / Day / Week / Month / Quarter)をアルゴリズム的に切り替えている。これにより、ユーザーが手動でズームイン・ズームアウトを繰り返す無駄な時間を完全に排除した。
—
結語
VBAはレガシーな言語と揶揄されることがあるが、それは使い手の技量に依存しているに過ぎない。オブジェクトのライフサイクルを理解し、APIとアプリケーションの挙動を完全に掌握したコードは、現代のモダンな言語で作られたラッパーアプリケーションにも匹敵するパフォーマンスと実用性を発揮する。
このガントチャート自動最適化ロジックを企業のグローバル標準テンプレート(Global.mpt)に組み込むことで、組織全体のプロジェクト管理の質を次の次元へと引き上げてほしい。
