Visio VBAを掌握する極限の知見:Application.Settingsによる作図環境の完全統制
開発プロジェクトのリーダーである私たちが、Visioを使った自動化ツールや図面生成スクリプトを組むとき、最も頭を悩ませる問題の一つが「環境依存のバグ」だ。
「開発者のPCでは完璧に整列した図形が、ユーザーのPCで実行すると微妙にズレる」
「コネクタのルーティングが意図しない挙動を起こす」
「グリッドスナップのせいで、計算づくで配置した座標が勝手に丸められる」
これらはすべて、Visioのアプリケーション全体、あるいはドキュメント個別に保持されている作図補助設定(スナップ・グリッド)の差異が原因である。個々のユーザーが手動で設定を変更している環境において、コード側が無防備に図形を配置すれば、出力結果が崩壊するのは当然だ。
今回は、`Application.Settings`(およびDocumentレベルの設定)をコードから動的に書き換え、「どの環境で実行しても100%同じ精度と挙動を担保する」ための堅牢な一括セットアップ手法を伝授する。
—
1. なぜ「環境依存のスナップ設定」は悪なのか?
初学者は、`ActivePage.DrawRectangle` や `Cells(“PinX”).FormulaU` を使って図形を配置すれば、意図した座標に置かれると錯覚する。しかし、Visioのエンジンはデフォルトで「スナップ(Snap)」や「グルー(Glue)」を有効にしている。
もしユーザーのVisioで「グリッドにスナップ」が有効になっていた場合、コードから指定したミリ単位の正確な座標が、最寄りのグリッド交点に強制吸着(スナップ)され、丸められてしまう。これでは高精度なレイアウトエンジンを自作した意味がない。
したがって、自動化マクロの実行開始時には、作図環境の設定を一時的にコード側で強制ロック(あるいは最適化)し、処理完了後に元の状態へ復元するという設計が必須となる。
—
2. 堅牢な設計:状態の退避・変更・復元(Safe Execution Pattern)
プロダクションコードにおいて、勝手にアプリケーションの設定を変更し、そのまま放置して終了するマクロは「行儀が悪い」の一言に尽きる。ユーザーが作業していたスナップ設定を破壊するためだ。
必ず以下のライフサイクルを守ること。
1. 現在の設定を退避(Backup)
2. 自動化に最適な設定へ強制上書き(Override)
3. 高速化のための画面描画停止(Optimization)
4. メインの作図処理(Execution)
5. 設定の復元と画面描画の再開(Restore & Cleanup)
特に、エラーハンドリング(`On Error Goto`)を組み込み、処理途中で予期せぬエラーが発生した場合でも、確実に設定が復元される構造にしなければならない。
—
3. 実装コード:作図補助設定の一括セットアップモジュール
以下のコードは、実務の現場でそのままコピペして利用できるプロダクションクオリティのテンプレートだ。`Application.Settings` を駆使して、スナップ・グリッド・定規の単位をプログラム側で完全にコントロールする。
Option Explicit
‘ メインのエントリーポイント
Public Sub ExecutePreciseDrawingTask()
Dim orgSnapEnabled As Boolean
Dim orgGridEnabled As Boolean
Dim orgSnapToGlue As Boolean
Dim orgScreenUpdating As Boolean
‘ 1. エラーハンドリングの準備(異常終了時も確実に環境を復元するため)
On Error GoTo ErrorHandler
‘ 2. 描画高速化のため、画面更新を一時停止
orgScreenUpdating = Application.ScreenUpdating
Application.ScreenUpdating = False
‘ 3. 現在のアプリケーション設定を退避
With Application.Settings
orgSnapEnabled = .SnapEnabled
orgGridEnabled = .GridEnabled
orgSnapToGlue = .GlueEnabled
End With
‘ ==========================================
‘ 4. 自動化用の厳格な設定へ強制上書き
‘ ==========================================
With Application.Settings
‘ スナップを完全無効化(座標の丸め込みを防ぐ)
.SnapEnabled = False
.GridEnabled = False
.GlueEnabled = False
‘ 必要に応じてグリッド間隔をミリメートル単位で強制定義する場合
‘ (※ドキュメント側の設定に依存するため、ActiveDocument側も制御)
End With
‘ ドキュメント単位の緻密な設定(必要に応じ追加)
Call SetDocumentScaleAndGrid(ActiveDocument)
‘ ==========================================
‘ 5. メインの作図・レイアウト処理(ここに実処理を書く)
‘ ==========================================
Call PerformComplexLayoutOperations
‘ 正常終了時のクリートアップ
GoTo Finally
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “自動化処理エラー”
Finally:
‘ ==========================================
‘ 6. 設定の復元と画面描画の再開
‘ ==========================================
With Application.Settings
.SnapEnabled = orgSnapEnabled
.GridEnabled = orgGridEnabled
.GlueEnabled = orgSnapToGlue
End With
Application.ScreenUpdating = orgScreenUpdating
‘ 変更を確実に反映
ActiveWindow.Redraw
Exit Sub
End Sub
‘ ドキュメント固有のスケールとグリッドを強制するヘルパー関数
Private Sub SetDocumentScaleAndGrid(ByVal targetDoc As Visio.Document)
Dim pag As Visio.Page
Set pag = targetDoc.Pages(1) ‘ 例として1ページ目
‘ ページの図面尺度を 1:1 に固定(ミリメートル基準の計算を正確にするため)
‘ PageScale / DrawingScale の設定
pag.PageSheet.CellsU(“PageScale”).FormulaU = “1 mm”
pag.PageSheet.CellsU(“DrawingScale”).FormulaU = “1 mm”
‘ グリッドの間隔を厳密に制御したい場合(例: 5mmグリッド)
pag.PageSheet.CellsU(“XGridSpacing”).FormulaU = “5 mm”
pag.PageSheet.CellsU(“YGridSpacing”).FormulaU = “5 mm”
End Sub
‘ ダミーのメイン処理
Private Sub PerformComplexLayoutOperations()
‘ ここに数千個のシェイプを正確な座標で配置するコードが入る
Dim shp As Visio.Shape
Set shp = ActivePage.DrawRectangle(10, 10, 50, 30)
shp.CellsU(“PinX”).FormulaU = “100 mm”
shp.CellsU(“PinY”).FormulaU = “150 mm”
End Sub
—
4. チーフアーキテクトからの実務アドバイス:見落としがちな罠
① `Application.Settings` と `Page.PageSheet` のスコープ違いを理解せよ
- `Application.Settings`: Visioアプリ全体(グローバル)の設定。UI上のスナップボタンの状態や、新規ドキュメント作成時の振る舞いに影響する。
- `Page.PageSheet` (Shapesheet): 個別ドキュメント・個別ページ固有の設定。図面の縮尺やグリッドの物理間隔はこちらに保持される。
「コードを書いたのにグリッド幅が変わらない」と悩むエンジニアのほとんどは、グローバル設定ばかりいじって、ドキュメント側のシェイプシート(`PageSheet`)の数式(Formula)を書き換えていないことが原因だ。両者を明確に区別してコードを記述すること。
② データベースや外部ファイル(JSON/XML)との連携
高度な企業向け自動化ツールでは、スナップ間隔やレイアウトの基準値を外部のJSON設定ファイルやDBから動的に読み込ませるケースが多い。
その場合、ハードコーディングされた数値を埋め込むのではなく、以下のように設定読込レイヤーを一枚噛ませるのがプロの設計だ。
‘ 外部設定ファイルからグリッド間隔を読み込んで適用するイメージ
Dim gridInterval As Double
gridInterval = LoadConfigFromDatabase(“GridIntervalMM”) ‘ 例: 10.0
pag.PageSheet.CellsU(“XGridSpacing”).FormulaU = gridInterval & ” mm”
この設計にしておけば、顧客の要件変更で「やっぱりグリッド幅を5mmから10mmに変えたい」と言われたときも、VBAコードを一文字も修正せず、設定値の変更だけで対応できる。
—
総括
プロのVisio VBA開発者と、単なるマクロの記録者の違いは、「実行環境のコントロール権を完全に握っているか否か」に尽きる。
`Application.Settings` を動的に制圧し、環境依存のノイズを完全に排除した上で図形を配置する。この設計思想をあなたのプロジェクトに導入すれば、「動かない図面」という無駄なトラブルは永遠に撲滅される。今すぐ既存のコードベースを見直し、この堅牢なライフサイクルパターンを組み込んでほしい。
