序文:なぜ君のVisioマクロは、数千個のシェイプを前に沈黙するのか
現場のエンジニア諸君、あるいは業務自動化の最前線に立つアーキテクト諸君。
数万行のデータから構成図を自動生成したり、大規模なインフラ構成図を一括更新するツールを開発した際、誰もが一度は「処理が終わらない」という壁にぶつかるはずだ。
100個のシェイプなら一瞬で終わるコードが、3,000個を超えたあたりから指数関数的に重くなり、最終的にVisioが応答なし(Freeze)になる。このとき、初心者は「PCのスペックが足りない」と嘆く。だが、我々プロフェッショナルは知っている。真のボトルネックはハードウェアではなく、Visioの「生真面目すぎる再計算エンジン」にあることを。
今回は、Visio VBAにおけるパフォーマンス最適化の至宝、`Application.DeferRecalc` を軸に、大規模図面を高速に制御するための極限のテクニックを伝授する。
—
1. 内部構造を理解せよ:ShapeSheetは「生きて」いる
Visioのすべてのシェイプの裏側には「ShapeSheet」というスプレッドシートが存在する。座標(PinX/Y)、サイズ(Width/Height)、色、テキスト、これらはすべて相互に数式で依存し合っている。
VBAからシェイプを1つ配置したり、プロパティを1つ変更したりするたびに、Visioは以下の処理を愚直に実行する:
1. プロパティの書き換え
2. 関連するすべてのセルの再計算(依存関係グラフの走査)
3. 画面の再描画(レンダリング)
3,000個のシェイプに設定を流し込む際、この「再計算」を3,000回律儀に実行させてはならない。
`Application.DeferRecalc = True` とは、この再計算エンジンを一時的に停止させ、すべての変更が完了した後に「一度だけ」計算を解決させるコマンドだ。これだけで、処理時間は1/10から、場合によっては1/100以下にまで短縮される。
—
2. 実践:プロダクション・グレードの最適化パターン
単に `DeferRecalc` を `True` にすればいいというものではない。エラーが発生した際に設定が戻らなければ、Visioは「再計算されない死んだアプリケーション」と化し、ユーザーは手動での復旧を余儀なくされる。
プロが書くべきは、堅牢なエラーハンドリングを内包したトランザクション形式のコードである。
プロフェッショナル向け最適化テンプレート
Public Sub BatchProcessLargeDiagram()
Dim vsoApp As Visio.Application
Set vsoApp = Visio.Application
‘ — 1. 実行前のステータス保護 —
‘ 画面更新、イベント、再計算をすべて抑制する「三種の神器」
Dim previousScreenUpdating As Boolean: previousScreenUpdating = vsoApp.ScreenUpdating
Dim previousEventsEnabled As Boolean: previousEventsEnabled = vsoApp.EventsEnabled
Dim previousDeferRecalc As Boolean: previousDeferRecalc = vsoApp.DeferRecalc
On Error GoTo ErrorHandler
‘ 高速化モードへ移行
vsoApp.ScreenUpdating = False
vsoApp.EventsEnabled = False
vsoApp.DeferRecalc = True ‘ これが心臓部
‘ — 2. メイン処理 (例: 大量シェイプ生成) —
Dim i As Long
Dim vsoPage As Visio.Page
Set vsoPage = vsoApp.ActivePage
For i = 1 To 5000
‘ ここに重い処理を記述
‘ 例: データベースから読み込んだ座標に基づいてシェイプを配置
Call DropAndConfigureShape(vsoPage, i)
Next i
Debug.Print “処理完了。再計算を開始します…”
CleanUp:
‘ — 3. ステータスの確実な復元 —
‘ DeferRecalcをFalseに戻した瞬間に、保留されていた計算が一気に走る
vsoApp.DeferRecalc = previousDeferRecalc
vsoApp.EventsEnabled = previousEventsEnabled
vsoApp.ScreenUpdating = previousScreenUpdating
‘ 最後に一度だけ画面を強制リフレッシュ
vsoApp.ActiveWindow.Activate
Exit Sub
ErrorHandler:
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical
Resume CleanUp
End Sub
Private Sub DropAndConfigureShape(vsoPage As Visio.Page, idx As Long)
‘ シェイプ生成の最小単位
Dim vsoShape As Visio.Shape
Set vsoShape = vsoPage.Drop(vsoPage.Document.Masters(“Rectangle”), idx 0.1, idx 0.1)
‘ ShapeSheetへの書き込み。DeferRecalc中なら、ここでの再計算は発生しない。
vsoShape.Cells(“Width”).FormulaForceU = “20mm”
vsoShape.Cells(“Height”).FormulaForceU = “10mm”
vsoShape.Text = “Node ID: ” & idx
End Sub
—
3. アーキテクトの視点:外部データ連携時の注意点
`DeferRecalc` を使う際、さらに上のレベルを目指すなら「データアクセスの局所化」を意識すべきだ。
1. 描画ループの中でSQLを発行するな
ファイルやデータベース(Access/SQL Server等)からデータを読み取って描画する場合、ループの中で1回ずつクエリを投げるのは論外だ。
- 悪い例: `For i = 1 To 1000 -> DB接続 -> 取得 -> 描画 -> 次へ`
- 良い例: `DB接続 -> 全データを配列またはコレクションにロード -> DB切断 -> (DeferRecalc = True) -> 高速描画`
2. 「中間座標の計算」をVBA側で完結させる
ShapeSheetの計算を止めている間、VBA側でシェイプの現在の座標(PinXなど)を参照しても、それは「最新の計算結果」ではない可能性がある。
配置ロジックにおいて、前のシェイプの端に次のシェイプをくっつけるような処理(スナップ計算)をShapeSheetの計算に頼っている場合、`DeferRecalc` 中は期待した値が取れない。
解決策: 座標計算はShapeSheetに依存せず、VBA内の変数(Double型)で管理し、絶対座標として `FormulaForceU` で叩き込むのがアーキテクトの作法である。
—
4. なぜ `ScreenUpdating` だけでは不十分なのか
Excel VBAに慣れた者は `ScreenUpdating = False` だけで満足しがちだが、Visioにおいてそれは「カーテンを閉めているだけ」に過ぎない。カーテンの裏で、Visioは依然としてShapeSheetの数式を解き、ジオメトリを再構成し、接続ポイントの整合性をチェックし続けている。
`DeferRecalc` はカーテンを閉めるだけでなく、「裏方の作業員全員に、合図があるまで手を止めて待機しろ」と命じるものだ。 この違いが、大規模図面における決定的な速度差を生む。
—
結言:最適化とは「愛」である
ツールを利用するユーザーにとって、30秒の待機時間はストレスであり、3分の待機時間は「ツールの放棄」に繋がる。
`DeferRecalc` を適切に制御することは、単なるテクニックではない。ユーザーの時間を尊重し、システムの寿命を延ばすための、設計者の愛である。
君が次に作るVisio自動化ツールが、数千のシェイプを瞬きする間に描き切ることを期待している。それができて初めて、君は「Visioを掌握した」と言えるのだ。
