【Project VBAを掌握する極限の知見】動的フィルター生成の奥義:数千行のタスク迷宮をワンクリックで制圧する
プロジェクトマネージャー諸君、日々の進捗管理でこんな悪夢を見たことはないか?
「数千行に及ぶWBSの中から、『今週が期限かつ、残り工数が残っており、かつ担当者がA氏のタスク』だけを即座に抽出したい」
手動でMS Projectのフィルター画面を開き、条件を設定し、適用する……。これを毎日のように繰り返しているとしたら、それはエンジニアとしての怠慢であり、貴重な時間のドブ捨てだ。MS ProjectのGUIは、時として人間の思考スピードよりも遥かに遅い。
今回は、VBAから`FilterEdit`メソッドを駆使し、「実行瞬間に条件を動的生成して適用する」という極限のテクニックを伝授しよう。リファレンスをなぞっただけのコードとは一線を画す、実務の現場で絶対に耐えうる堅牢なアーキテクチャを解説する。
—
1. なぜ「静的フィルター」では実務で使い物にならないのか?
MS Projectの標準機能には、あらかじめ定義された「フィルター」がある。しかし、実務の現場で求められる条件は千変万化だ。
「ユーザーごと」「動的な日付範囲(例:今日から3日以内)」「複数条件のAND/ORの動的結合」……これらを事前に全て静的フィルターとして定義しておくことは、メンテナンス性の観点から愚策の極みと言える。
ここでVBAの出番だ。「必要な瞬間に、必要な条件のフィルターをメモリ上で構築し、適用し、用が済んだら消去する」。このライフサイクルをコードで完全に制御する。これがプロのプロジェクト自動化エンジニアのやり方である。
—
2. 堅牢な動的フィルター実装の設計思想
MS ProjectのVBAでフィルターを操作する際、最も陥りやすい罠が「既存の同名フィルターとの競合」や「ゴミフィルターの蓄積によるパフォーマンス劣化」だ。
`FilterEdit`メソッドは、グローバルまたはプロジェクト固有のフィルターテーブルに設定を書き込む。そのため、以下の鉄則を守る必要がある。
1. 一意のフィルター名を動的に生成する(競合の排除)
2. 処理の前後で必ず既存の同名フィルターをクリーンアップする(メモリリーク・ゴミデータの防止)
3. エラーハンドリングを厳格に行い、失敗時もビューの状態を復元する
—
3. プロダクションコード例:動的フィルター生成・適用エンジン
以下のコードは、指定した条件(例:「指定日以前に開始予定、かつ未完了」)に合致するタスクを動的に抽出し、画面に適用するプロシージャだ。そのままコピペして、実務のツールに組み込んでほしい。
‘ ==============================================================================
‘ モジュール名: modDynamicFilter
‘ 概要: 指定した条件に基づいてMS Projectのフィルターを動的生成し適用する
‘ 著者: 業務自動化チーフアーキテクト
‘ ==============================================================================
Option Explicit
Public Sub ApplyDynamicTaskFilter()
Dim targetDate As Date
Dim filterName As String
Dim isCustomFilterExists As Boolean
‘ 抽出基準日の設定(例:本日から7日後以降をターゲットにするなど、業務ロジックに応じて変更)
targetDate = Date + 7
‘ 実行時に一意となるフィルター名を定義(プロジェクト固有のプレフィックスを付与)
filterName = “Temp_AutoFilter_” & Format(Now, “yyyymmdd_hhmmss”)
On Error GoTo ErrorHandler
‘ 画面描画を停止し、処理速度を極限まで高める
Appliation.ScreenUpdating = False
‘ 1. 【安全性確保】万が一同名のフィルターが残存している場合は事前に削除
Call RemoveFilterIfExists(filterName)
‘ 2. 【動的生成】FilterEditメソッドによるフィルター定義の構築
‘ 引数の詳細: 名前に加え、タスク用(Task:=True)、既存置換(Create:=True)、条件式を渡す
‘ ※今回は「完了しておらず、かつ指定日より前が開始日のタスク」を抽出条件とする
‘ MS ProjectのFieldNameToFieldConstant関数を併用するのがベストプラクティスだが、
‘ 可読性と確実な定数指定のためにネイティブのフィールド定数(pjTaskStart等)を使用する
FilterEdit _
Name:=filterName, _
Task:=True, _
Create:=True, _
OverwriteExisting:=True, _
Field:=”開始”, _
Test:=”は次より前”, _
Value:=CStr(targetDate), _
Operation:=”And”, _
Value2:=”割合の完了”, _
Test2:=”は等しくない”, _
Value3:=”100%”
‘ 3. 【適用】生成したフィルターをアクティブペインに適用
FilterApply Name:=filterName
‘ 4. 【後始末】一度適用すればフィルター定義は内部保持されるため、
‘ グローバル/プロジェクトのフィルターリストを汚さないよう即座に削除予約、
‘ もしくは次回のクリーンアップ対象とする。
‘ ※実運用ではユーザーがフィルターを解除した後に削除するのが安全。
MsgBox “動的フィルターの適用が完了しました。” & vbCrLf & _
“条件: ” & targetDate & ” 以前に開始、かつ未完了のタスク”, vbInformation, “自動化ツール”
CleanExit:
Application.ScreenUpdating = True
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “致命的エラー”
Resume CleanExit
End Sub
‘ ——————————————————————————
‘ 補助プロシージャ: 既存の同名フィルターの安全な削除
‘ ——————————————————————————
Private Sub RemoveFilterIfExists(ByVal fName As String)
Dim f As Filter
On Error Resume Next
For Each f In ActiveProject.Filters
If f.Name = fName Then
f.Delete
Exit For
End If
Next f
On Error GoTo 0
End Sub
—
4. チーフアーキテクトが教える実装上の急所と注意点
上記のコードを現場に導入するにあたり、以下のポイントを心に刻んでおいてほしい。
① ローカライズ(言語依存)への配慮
MS ProjectのVBAにおいて、`Field` 引数に渡す文字列(例: `”開始”`, `”割合の完了”`)は、実行しているMS Projectの表示言語に依存する。
もしグローバル展開するプロジェクトや、英語OS環境が混在するチームで運用する場合は、日本語の文字列直書きではなく、`pjTaskStart` などのネイティブなフィールド定数(Field Constants)を組み合わせるロジックに拡張すべきだ。これにより、言語環境の違いによる「コンパイルエラー・実行時エラー」を完全に排除できる。
② 画面描画の抑制(`ScreenUpdating`)の重要性
数千行のタスクを持つ巨大なプロジェクトファイルにおいて、フィルターを適用すると、GUIの再描画(ガントチャートの再計算等)が発生し、致命的なパフォーマンス低下を招く。必ず処理の冒頭で `Application.ScreenUpdating = False` をかけ、処理完了後に `True` に戻すこと。これだけで体感速度が何倍も変わる。
③ ユーザーエクスペリエンス(UX)の担保
自動生成されたフィルターは、ユーザーにとっては「突然現れた謎のフィルター」だ。適用時には必ずメッセージボックスや、ステータスバーへのメッセージ出力(`Application.StatusBar = “…”`)を行い、「今、システムがどのような条件でデータを絞り込んでいるのか」を視覚的に伝える配慮を忘れるな。
—
5. おわりに
VBAは、単なる「マクロの記録」の延長ではない。MS Projectという巨大なデータベースエンジンを意のままに操り、人間が手作業で行う苦痛をゼロにするための「最強の武器」である。
今回紹介した動的フィルター生成の技術をあなたのWBS管理ツールに組み込み、チームの生産性を圧倒的な領域へと引き上げてほしい。健闘を祈る。
