大容量図面を爆速化!Visio VBAの「DeferRecalc」で描画・計算を支配する技術
「うわっ、この図面、シェイプが数千個もあるのに、マクロを動かすとカクカクして全然進まない…」
Visio VBAを使い始めたばかりの皆さん、あるいはマクロの記録から一歩踏み出したいと思っている皆さん。そんな悩みに直面したことはありませんか?
特に、たくさんのシェイプ(図形)に一括で何かしらの処理を施すとき、Visioがその都度、シェイプの配置やサイズ、連結線などを再計算しようとして、処理が重くなってしまうことがあります。これは、Visioが描画を正確に保とうとする親切心ゆえなのですが、大量のシェイプを相手にする時には、この「親切心」が逆にボトルネックになってしまうんです。
でも、安心してください。
この「描画・計算の遅延」という厄介な現象を、まるで魔法のように解消するテクニックがあるんです。それが、今回ご紹介する `Application.DeferRecalc` という、プロフェッショナルがこっそり使う最適化手法です。
このテクニックをマスターすれば、大容量の図面であっても、驚くほどスムーズに、そして高速に処理できるようになります。まるで、重い荷物を一時的に預けて、身軽になってから一気に作業を片付けるようなイメージですね。
この記事では、
- Visio VBAの基本的なオブジェクト(Application, Document, Page, Shape)って何?
- `DeferRecalc` って具体的にどういう仕組みなの?
- どうやってコードに書けばいいの?
- どんな時に役立つの?
- うっかりハマりやすい罠は?
といった疑問に、図解も交えながら、優しく、そして丁寧に解説していきます。
ここをクリアすれば、Visio VBAの基本と、より実践的な高速化テクニックの扉が、あなたにも開かれますよ。さあ、一緒にVisio VBAの世界を深く探求していきましょう!
—
1. Visio VBAの基本:Application, Document, Page, Shape の世界
まず、Visio VBAを理解するための、基本的な「部品」たちを見ていきましょう。これらは、Visioの図面を構成する階層構造そのものです。
1.1. Application:Visioそのもの、すべての親玉

`Application` オブジェクトは、まさにVisioアプリケーションそのものを表します。VBAマクロからVisioを操作する際には、必ずこの `Application` オブジェクトが起点となります。
- 役割: Visioの起動・終了、開いているドキュメントの管理、オプション設定など、Visio全体に関わる操作を行います。
- VBAでの扱い: 通常、VBAエディタでコードを書く場合、`Application` オブジェクトは明示的に指定しなくても、暗黙的に参照されます。しかし、例えば別のVisioインスタンスを操作する場合など、明示的に指定することも可能です。
- 親: なし(最上位)
1.2. Document:図面ファイルそのもの

`Document` オブジェクトは、Visioで開いている個々の図面ファイル(.vsd, .vsdx など)を表します。一つのVisioアプリケーションには、複数の `Document` が開かれている可能性があります。
- 役割: 図面内のページ管理、図面固有の設定(単位、グリッド設定など)、図面全体の保存・印刷などを行います。
- VBAでの扱い: 現在アクティブな(一番手前に表示されている)図面は、`ActiveDocument` プロパティで取得できます。特定の図面を操作したい場合は、`Documents` コレクションから名前やインデックスで指定します。
- 親: `Application`
1.3. Page:図面の中の「ページ」

`Page` オブジェクトは、`Document` の中に存在する個々のページを表します。フローチャートや組織図などで、複数のページに分かれている場合、それぞれのページが `Page` オブジェクトになります。
- 役割: ページ上に配置されているシェイプの管理、ページのサイズや背景設定、印刷範囲の管理などを行います。
- VBAでの扱い: `Document` オブジェクトの `Pages` コレクションから、名前やインデックスで取得します。現在アクティブなページは `ActivePage` プロパティで取得できます。
- 親: `Document`
1.4. Shape:描画の最小単位、シェイプそのもの

`Shape` オブジェクトは、Visioの図面における最も基本的な描画要素です。線、四角形、円、テキストブロック、さらには複雑なカスタムシェイプまで、すべて `Shape` オブジェクトとして扱われます。
- 役割: シェイプの形状、位置、サイズ、色、テキスト、接続点、そしてシェイプシートのデータなど、そのシェイプに関するあらゆる情報を持ち、表示・操作します。
- VBAでの扱い: `Page` オブジェクトの `Shapes` コレクションから、名前やインデックス、あるいは特定のIDで取得します。
- 親: `Page`
これらのオブジェクトは、`Application` → `Document` → `Page` → `Shape` という親子関係で構成されており、VBAでVisioを操作する上で、この構造を理解していることが非常に重要です。
—
2. なぜ、大容量図面で処理が遅くなるのか? ~シェイプシートの再計算の影~
さて、いよいよ本題です。たくさんのシェイプを扱うと、なぜ処理が遅くなるのでしょうか?
その最大の原因は、シェイプシートの自動再計算にあります。
2.1. シェイプシートとは? ~シェイプの「取扱説明書」~
皆さんは「シェイプシート」という言葉を聞いたことがありますか?
Visioの各シェイプは、その見た目や振る舞いを決定する、いわば「取扱説明書」のようなものを持っています。それがシェイプシートです。

シェイプシートには、以下のような情報が「セル」と呼ばれる単位で格納されています。
- Shape Transformセクション: `PinX`, `PinY` (中心座標), `Width`, `Height` (幅・高さ) など、シェイプの配置やサイズに関する情報。
- Line Infoセクション: 線の色、太さ、スタイルなど。
- Fill Infoセクション: 塗りつぶしの色、パターンなど。
- Text Blockセクション: テキストのフォント、サイズ、配置など。
- Geometryセクション: シェイプの実際の形状を定義する頂点情報など。
- Connection Pointsセクション: 接続線が接続できる点の位置。
- User-defined Cellsセクション: ユーザーが独自に定義したデータ。
2.2. 自動再計算の「親切心」が仇となる時
Visioは、図面の整合性を保つために、非常に賢く設計されています。
例えば、あるシェイプの位置 (`PinX`, `PinY`) を変更すると、そのシェイプに接続されている別のシェイプが、自動的に位置を調整したり、連結線が追従したりすることがあります。これは、シェイプシート内の数式(例えば、`=GUARD(Sheet.1!PinX + 10)` のような)によって管理されています。

この「自動再計算」は、手動で図面を編集する際には非常に便利です。しかし、VBAで数千、数万といった大量のシェイプに対して、一度に位置やサイズを変更するような処理を行う場合、Visioはその都度、関連するシェイプシートの再計算を実行しようとします。
これが、「カクカク」「重い」「処理が終わらない」といった現象を引き起こす、まさにボトルネックなのです。
—
3. `Application.DeferRecalc` による描画・計算保留テクニック
では、このボトルネックをどう解消するのか?
そこで登場するのが、`Application.DeferRecalc` プロパティです。
3.1. `DeferRecalc` の仕組み:一時停止と一括処理
`Application.DeferRecalc` は、Visioの描画と計算の自動更新を一時的に停止させるためのプロパティです。
- `Application.DeferRecalc = True` に設定すると、Visioは描画やシェイプシートの計算を「保留」します。
- この状態でVBAコードを実行し、大量のシェイプに対する処理(位置変更、サイズ変更、シェイプシートの値変更など)をまとめて行います。
- 処理がすべて完了したら、`Application.DeferRecalc = False` に設定して、保留されていた描画と計算を一度に実行させます。

この「一時停止 → まとめ処理 → 一括実行」のサイクルが、Visioの無駄な再計算を劇的に減らし、結果として処理速度を飛躍的に向上させるのです。
3.2. VBAコードでの実装例
では、実際に `DeferRecalc` を使ったVBAコードを見てみましょう。
ここでは例として、アクティブなページにあるすべてのシェイプの `Width` (幅) を2倍にする処理を考えます。
Sub OptimizeShapeProcessing()
Dim pag As Visio.Page
Dim shp As Visio.Shape
Dim originalDeferRecalcState As Boolean ‘ 元々のDeferRecalcの状態を保存
‘ 1. 現在アクティブなページを取得
Set pag = Visio.ActivePage
‘ 2. 既存のDeferRecalcの状態を確認・保存
‘ 他のマクロやVisioの動作でDeferRecalcが有効になっている場合があるため、
‘ 後で元に戻せるように状態を保存しておきます。
originalDeferRecalcState = Visio.Application.DeferRecalc
‘ 3. DeferRecalcを有効にする (計算と描画を保留)
‘ ここから、Visioは描画や再計算を一時停止します。
Visio.Application.DeferRecalc = True
‘ 画面のちらつき防止のため、画面更新も一時的に停止するとさらに効果的です。
Visio.Application.ScreenUpdating = False
‘ 4. 大量シェイプに対する処理をループで実行
‘ ここでは例として、全シェイプの幅を2倍にする処理を記述しています。
On Error GoTo ErrorHandler ‘ エラー発生時の処理を追加
For Each shp In pag.Shapes
‘ シェイプの幅を2倍にする
‘ ShapeSheetのWidthセルに直接書き込む場合:
‘ shp.Cells(“Width”).FormulaU = shp.Cells(“Width”).FormulaU 2
‘ -> 注意: FormulaUは数式を文字列で扱うため、単純な数値計算だとエラーになることがある。
‘ -> より安全なのは、’Result’プロパティで数値を直接取得・設定すること。
‘ Resultプロパティを使って安全に幅を2倍にする
shp.Cells(“Width”).Result(Visio.visUnitsCentimeters) = _
shp.Cells(“Width”).Result(Visio.visUnitsCentimeters) 2
‘ 必要に応じて、他のシェイプシートのセルもここで一括変更できます。
‘ 例: 特定のシェイプのUser-defined Cell “MyValue” を更新
‘ shp.CellsU(“User.MyValue”).FormulaU = “NewValue”
Next shp
‘ 5. 処理完了後、DeferRecalcを無効に戻す (保留していた計算と描画を実行)
‘ ここまでの変更がまとめてVisioに適用されます。
Visio.Application.DeferRecalc = False
‘ 画面更新も再開します。
Visio.Application.ScreenUpdating = True
‘ 6. 元々のDeferRecalcの状態に戻す (もし元々Trueだった場合)
‘ ただし、通常は False に戻すのが一般的です。
‘ Visio.Application.DeferRecalc = originalDeferRecalcState
MsgBox “シェイプの幅の変更処理が完了しました。”, vbInformation
Exit Sub ‘ 正常終了
ErrorHandler:
‘ エラーが発生した場合の処理
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical
‘ エラー発生時でも、DeferRecalcとScreenUpdatingを元に戻すことが重要
Visio.Application.DeferRecalc = False
Visio.Application.ScreenUpdating = True
‘ Visio.Application.DeferRecalc = originalDeferRecalcState ‘ 元の状態に戻す場合
End Sub
3.3. コードのポイント解説
- `Dim pag As Visio.Page`, `Dim shp As Visio.Shape`:
- `Page` オブジェクト(ページ)と `Shape` オブジェクト(シェイプ)を格納するための変数を宣言しています。Visioのオブジェクトを扱う際には、このように型を明示するのが基本です。
- `Set pag = Visio.ActivePage`:
- 現在アクティブな(一番手前に表示されている)ページを `pag` 変数に代入しています。
- `originalDeferRecalcState = Visio.Application.DeferRecalc`:
- これは非常に重要なポイントです。もしかしたら、Visioの他の設定や、既に実行されている他のマクロによって `DeferRecalc` がすでに `True` になっているかもしれません。これを保存しておかないと、自分のマクロが終わった後に `DeferRecalc` が `False` に戻ってしまい、意図しない動作を引き起こす可能性があります。
- `Visio.Application.DeferRecalc = True`:
- ここからが `DeferRecalc` の本領発揮です! Visioの自動計算と画面更新を一時的にストップさせます。
- `Visio.Application.ScreenUpdating = False`:
- `DeferRecalc` とは少し異なりますが、画面の描画処理自体も一時停止させることで、ちらつきを防ぎ、さらにパフォーマンスを向上させることができます。大量のシェイプを操作する際には、この両方を併用するのが定石です。
- `For Each shp In pag.Shapes … Next shp`:
- アクティブなページ上のすべてのシェイプを一つずつ取り出し、ループ処理を行います。
- `shp.Cells(“Width”).Result(Visio.visUnitsCentimeters) = …`:
- シェイプの幅 (`Width` セル) を取得し、その値の2倍を新しい幅として設定しています。
- `Result(Visio.visUnitsCentimeters)` のように `Result` メソッドを使うことで、Visioの内部単位(この場合はセンチメートル)で数値として直接やり取りできます。`FormulaU` プロパティは数式を文字列で扱うため、単純な数値計算では予期せぬエラーが発生することがあります。この `Result` メソッドを使うのが、シェイプシートの数値セルを操作する上で最も安全で推奨される方法です。
- `On Error GoTo ErrorHandler`:
- コードの途中でエラーが発生した場合に、処理を中断して `ErrorHandler` ラベルにジャンプするように設定しています。大量のシェイプを処理する際は、予期せぬエラー(例えば、一部のシェイプが特殊な状態にあるなど)が発生する可能性もゼロではありません。
- `Visio.Application.DeferRecalc = False`:
- ループ処理がすべて完了したら、ここで保留されていたすべての計算と描画が一気に実行されます。
- `Visio.Application.ScreenUpdating = True`:
- 画面更新を再開します。
- `Exit Sub` / `ErrorHandler:`:
- 正常に終了した場合と、エラーが発生した場合で、後処理(特に `DeferRecalc` と `ScreenUpdating` の状態を元に戻すこと)を確実に行うための構造です。エラー発生時でもこれらの設定を元に戻さないと、Visioが予期せぬ状態のまま固まってしまう可能性があります。
3.4. どんな時に使うべきか?
`DeferRecalc` は、以下のような状況で絶大な効果を発揮します。
- 多数のシェイプの位置、サイズ、角度などを一括で変更する処理:
- 例えば、図面上の全シェイプを特定のレイアウトに整列させる、グリッドに合わせて配置する、一律に拡大・縮小するなど。
- 多数のシェイプのシェイプシートのセル値を一括で更新する処理:
- 特定のカスタムプロパティ(User-defined Cells)の値を更新する、数式を書き換えるなど。
- 多数のシェイプを新規作成し、配置する処理:
- ループで大量のシェイプを作成する場合、作成ごとに再計算が発生するため、`DeferRecalc` でまとめて処理すると効果的です。
- 連結線が多数存在する図面での処理:
- シェイプの移動などが連結線の再計算を誘発しやすいため、`DeferRecalc` の効果がより顕著になります。
3.5. 陥りやすいエラーと注意点
`DeferRecalc` は強力なテクニックですが、いくつか注意点があります。
- エラーハンドリングの徹底:
- 前述の通り、ループ処理中にエラーが発生した場合、`DeferRecalc` が `True` のままVisioが終了してしまう可能性があります。必ず `On Error GoTo` などでエラーハンドリングを行い、エラー発生時でも `DeferRecalc = False` および `ScreenUpdating = True` を実行するようにしましょう。
- `ScreenUpdating` との併用:
- `DeferRecalc` は計算と描画の「ロジック」を保留しますが、画面の「ちらつき」自体を完全に防ぐわけではありません。画面のちらつきを防ぎ、パフォーマンスを最大化するためには、`Application.ScreenUpdating = False` と併用することを強く推奨します。
- `DeferRecalc` の状態を元に戻す:
- マクロの開始時に `DeferRecalc` の状態を保存し、終了時に元に戻す(あるいは `False` に戻す)ことを忘れないでください。これにより、他の処理やVisio自体の予期せぬ動作を防ぎます。
- シェイプシートの数式との関係:
- `DeferRecalc` を `True` にしている間は、シェイプシート内の数式は評価されません。もし、あるシェイプの数式の結果に依存する別のシェイプの数式がある場合、意図した通りに動作しない可能性があります。基本的には、すべての操作をまとめてから `DeferRecalc = False` に戻すという流れで利用します。
- デバッグの難易度:
- `DeferRecalc` を有効にしている間は、ブレークポイントを設定しても、その時点でのシェイプの状態が確定していなかったり、ステップ実行が期待通りに動かなかったりすることがあります。デバッグは `DeferRecalc = False` の状態で行うか、慎重に行う必要があります。
—
4. まとめ:`DeferRecalc` でVisio VBAマスターへの道を駆け上がろう!
いかがでしたでしょうか?
`Application.DeferRecalc` は、大量のシェイプを扱うVisio VBAプログラミングにおいて、まさに「知らなければ損をする」レベルの強力な最適化テクニックです。
- Visioのオブジェクトモデル(`Application` → `Document` → `Page` → `Shape`)の基本を理解する。
- 大量のシェイプ処理で遅くなる原因は、シェイプシートの自動再計算にあることを知る。
- `Application.DeferRecalc = True` で計算と描画を一時保留し、
- `Application.ScreenUpdating = False` で画面更新も停止させ、
- 処理をまとめて実行してから、
- 最後に `Application.DeferRecalc = False` と `Application.ScreenUpdating = True` で元に戻す。
この一連の流れをマスターすれば、あなたがこれまで「重くて触れない…」と思っていた大容量図面も、まるで魔法のように軽快に操作できるようになります。
最初は少し戸惑うかもしれませんが、今回ご紹介したコード例を参考に、ぜひご自身のマクロに `DeferRecalc` を組み込んでみてください。きっと、その劇的な効果に驚かれるはずです。
ここをクリアすれば、Visio VBAの基本はもちろん、より高度なパフォーマンスチューニングの扉も開かれます。
これからも、皆さんのVisio VBAライフが、より豊かで、より効率的なものになるよう、応援しています!
Happy Visio VBA Coding!
