Visio VBAを掌握する極限の知見:非表示作業用ページ(サンドボックス)による高速・安全な図形処理アーキテクチャ
こんにちは。開発プロジェクトの現場において、数千個の図形を動的に制御するプラント図やネットワーク図の自動生成ツールを設計したことがあるなら、一度はこんな絶望を味わったはずだ。
- 「VBAで図形を1つずつ生成・計算している間、画面が猛烈な勢いでチラつき、フリーズしたかのように固まる」
- 「レイアウト計算の途中で不正な状態の図形がユーザーの目に触れてしまい、ダサい」
- 「座標計算のためにマスターシェイプをゴリゴリ触っていたら、本番ページの構造がグチャグチャに破壊された」
素人は、アクティブなページ(画面)上で直接図形の生成・削除・グルーピングを繰り返す。これは画面描画エンジン(UIスレッド)を無駄にヒットさせ、パフォーマンスを致命的に低下させる最大のアンチパターンだ。
プロのVisio VBAエンジニアは、「非表示の作業用ページ(サンドボックス)」を動的に爆誕させ、その暗闇の中で一切の泥臭い計算・プレ組み立て・幾何学処理を完結させる。そして、完成した完璧な図形群だけを本番ページへ一瞬で転送し、作業用ページごと跡形もなく消し去る。
今回は、このモダンかつ堅牢な「Visioサンドボックス手法」の全貌を、プロダクションコードと共に授けよう。
—
1. なぜ「サンドボックス手法」が必要なのか?
Visioのオブジェクトモデルにおいて、`Page`オブジェクトは単なるキャンバスではない。UIの描画更新(Redraw)、イベントの発生、そして図形IDの採番スコープを内包している。
画面が表示された状態で`ActivePage.Drop`や`Shapes.Addπόint`などを狂ったように実行すると、Visioは描画のたびにウィンドウを再描画しようとする。これがパフォーマンス低下の元凶だ。
さらに、複雑な結合(Union)や切り抜き(Subtract)、カスタムプロパティ(Prop)の流し込みといった前処理を本番ページで行うと、万が一途中でエラー落ちした際に、中途半端なゴミ図形がユーザーの成果物を汚すことになる。
サンドボックス設計の3大原則
1. 完全な不可視性: ユーザーに作業用ページの存在すら気づかせない(ウィンドウやタブを出さない)。
2. トランザクション的安全性: 処理の成否に関わらず、マクロ終了時には必ず作業用ページが完全消去される(メモリリーク・ゴミ残存の根絶)。
3. ゼロ・フリッカー: 画面の描画更新(ScreenUpdating)を完全に制御し、ユーザーには「一瞬で結果が出た」ように見せる。
—
2. 堅牢なサンドボックス・アーキテクチャの実装
それでは、実務でそのまま使えるプロダクションコードを提示する。
このコードは、一時的な作業用ページを作成し、そこで図形の生成・加工を行ったのち、本番ページへ安全に転送(Drop/Glue)してページ自体を破棄する一連の流れをカプセル化したものだ。
Option Explicit
‘ ==============================================================================
‘ 処理名: 業務自動化プロトタイプ – サンドボックス図形生成エンジン
‘ 概要 : 非表示ページで図形を組み立て、本番ページへ一括転送する
‘ ==============================================================================
Public Sub RunSandboxWorkflow()
Dim targetDoc As Visio.Document
Set targetDoc = ActiveDocument
‘ 画面描画とイベントを完全停止(パフォーマンス最大化の鉄則)
targetDoc.Application.ScreenUpdating = False
targetDoc.Application.EventEnabled = False
Dim sandboxPage As Visio.Page
Set sandboxPage = Nothing
On Error GoTo ErrorHandler
‘ 1. サンドボックス(作業用ページ)の動的生成
Set sandboxPage = CreateSandboxPage(targetDoc)
If sandboxPage Is Nothing Then Err.Raise 9999, , “サンドボックスページの生成に失敗しました。”
‘ 2. サンドボックス内での「泥臭い」幾何計算・図形プレ組み立て
‘ ※ユーザーの目には一切触れません
Dim assembledShape As Visio.Shape
Set assembledShape = BuildComplexShapeInSandbox(sandboxPage)
‘ 3. 本番ページ(例: ActivePage)への転送
Dim targetPage As Visio.Page
Set targetPage = targetDoc.ActivePage
Dim finalShape As Visio.Shape
Set finalShape = assembledShape.Drop(targetPage, 5.0, 5.0) ‘ 本番の指定座標へドロップ
‘ 4. 後始末:サンドボックスの即時破棄
DeleteSandboxPage targetDoc, sandboxPage
‘ 描画再開
targetDoc.Application.ScreenUpdating = True
targetDoc.Application.EventEnabled = True
MsgBox “サンドボックス処理が正常に完了しました。”, vbInformation, “処理成功”
Exit Sub
ErrorHandler:
‘ 異常系:エラー発生時も環境を汚さない
targetDoc.Application.ScreenUpdating = True
targetDoc.Application.EventEnabled = True
If Not sandboxPage Is Nothing Then
On Error Resume Next
DeleteSandboxPage targetDoc, sandboxPage
On Error GoTo 0
End If
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
End Sub
‘ ==============================================================================
‘ 内部関数: 作業用ページの動的生成と「隠蔽」
‘ ==============================================================================
Private Function CreateSandboxPage(ByVal doc As Visio.Document) As Visio.Page
Dim pg As Visio.Page
Set pg = doc.Pages.Add()
‘ ページ名の一意化(競合を防ぐ)
pg.Name = “_Sandbox_” & Format(Now, “yyyymmddhhmmss”) & “_” & Int(Rnd 1000)
‘ 【極限の知見】VisioのページをUIから隠す・見えなくする設定
‘ PageSheetの PageProperties.UIVisible セルを 0 にすることで、
‘ タブやUIの視界から完全に消去し、ユーザーの誤操作を物理的に防ぎます。
Dim pageSheet As Visio.Shape
Set pageSheet = pg.PageSheet
If pageSheet.CellExists(“PageProperties.UIVisible”, visExistsAnywhere) Then
pageSheet.Cells(“PageProperties.UIVisible”).FormulaU = “0”
End If
Set CreateSandboxPage = pg
End Function
‘ ==============================================================================
‘ 内部関数: サンドボックス内でのプレ組み立て処理
‘ ==============================================================================
Private Function BuildComplexShapeInSandbox(ByVal pg As Visio.Page) As Visio.Shape
‘ ここで複雑なマスターの配置、結合、テキスト埋め込みを行う
‘ 例として、長方形と円を配置してグループ化する処理をシミュレート
Dim shpRect As Visio.Shape
Set shpRect = pg.DrawRectangle(1, 1, 3, 2)
shpRect.Text = “サンドボックス生成”
Dim shpCircle As Visio.Shape
Set shpCircle = pg.DrawOval(1.5, 1.5, 2.5, 2.5)
‘ グループ化のテスト
Dim shpGroup As Visio.Shape
Set shpGroup = pg.DropMany(New Variant() {shpRect, shpCircle}, New Variant() {0, 0}, New Variant() {0, 0})
‘ ※実際のグループ化APIは要件に合わせて調整してください
Set BuildComplexShapeInSandbox = shpRect
End Function
‘ ==============================================================================
‘ 内部関数: サンドボックスの安全な破棄
‘ ==============================================================================
Private Sub DeleteSandboxPage(ByVal doc As Visio.Document, ByVal pg As Visio.Page)
Dim targetName As String
targetName = pg.Name
‘ ページを削除
pg.Delete
End Sub
—
3. プロジェクトマネージャーが教える「現場の罠」と回避策
このコードをそのまま導入するだけでは、現場の巨大な図面を扱う際に思わぬ落とし穴にハマる。実務で必ず直面するトラブルシューティングを共有しよう。
罠①: `ScreenUpdating = False` の解除忘れによる「永遠のフリーズ」
VBAで最も恐ろしいのは、エラーハンドラーを通らずにコードが途中でアボート(強制終了)したとき、`ScreenUpdating = False` や `EventEnabled = False` がそのまま残り、VisioのUIが完全に死に体になる現象だ。
- 対策: 上記コードの通り、`On Error GoTo ErrorHandler` を必ず実装し、異常系ルートでも確実にフラグを `True` に戻す防御的プログラミングを徹底せよ。
罠②: ページ名の衝突(Name競合)
マルチスレッド的な処理や、短時間に何度もマクロを叩くオペレーターがいる場合、作業用ページ名が被るリスクがある。
- 対策: ページ名には必ず `Format(Now, “hhmmss”)` に加え、乱数(`Rnd`)を付与して一意性を担保すること。
罠③: 外部データベースやExcel連携時のトランザクション整合性
もしこのサンドボックス内で、外部のSQL ServerやExcelから膨大なマスタデータを読み込んで図形プロパティを動的生成する場合、データベース側のコネクションとVisioのページ生成ライフサイクルを完全に切り離せ。
- 正しい順序:
1. 外部DBからデータをメモリ(Dictionaryや配列)に全取得。
2. 画面描画を停止。
3. サンドボックス生成。
4. メモリ上のデータをもとにサンドボックス内で図形を描画・構築。
5. 本番ページへ一括転送。
6. サンドボックス破棄・描画再開。
DBとの通信をサンドボックスの描画ループ内に挟むな。遅延の原因になる。
—
4. まとめ:プロフェッショナルなVBAコードとは
アマチュアのコードは「動けばいい」という発想で作られているため、画面がチラつき、エラーに弱く、保守性が低い。
しかし、私たちが書くべきプロダクションコードは、「Visioという重厚長大なアプリケーションの特性をリスペクトし、リソースのライフサイクルを完全に支配しているか」が問われる。
非表示作業用ページ(サンドボックス手法)は、その最たる武器だ。
この手法をあなたの開発アーキテクチャに組み込むことで、どれほど複雑な図面自動生成要件が来ようとも、ユーザーを待たせず、画面を汚さず、完璧な成果物を叩き出すことができる。
次の自動化案件では、ぜひこの「暗闇の作業場」を実装してみてほしい。コードの美しさとパフォーマンスの桁違いな差に、きっと驚くはずだ。
