Visio VBAを掌握する:PageSheet直叩きによる「動的キャンバス・オートメーション」の極意
Visioというソフトウェアは、単なるドローイングツールではない。それは「接続と座標のデータベース」である。
多くの開発者が `Shape.Move` や `Page.Resize` といった高レイヤーのメソッドに甘んじている間に、真のエンジニアは「Cell」という最小単位の原子構造を操作し、Visioの描画エンジンを自在に調教する。
今回は、図形群の座標からページ境界を逆算し、`PageSheet` を直接操作して用紙サイズを過不足なく動的にフィットさせる、極限の自動化手法を伝授する。
—
1. Visioオブジェクトモデルの深淵:PageSheetとは何か
Visioのページは、単なる一枚の紙ではない。裏側では「PageSheet」という名の不可視のShapeが、ページのプロパティ(幅、高さ、余白)を司っている。
`Page.PageSheet` にアクセスし、`Cells(“PageWidth”)` を書き換えることは、Visioの描画エンジンに対して「世界そのものの定義を書き換える」行為に等しい。これを安易に行うと、再計算(Recalculate)のオーバーヘッドでアプリがフリーズする。ゆえに、我々は「最小の操作で、最大の追従性」を実現しなければならない。
2. 動的リサイズのアーキテクチャ設計
実務において、単純に全図形の座標を取得して最大値を代入するだけでは、再描画の遅延や、意図しないレイアウト崩れを招く。以下の3つのフェーズで構築する。
1. 境界算出フェーズ: `SpatialSearch` APIを駆使し、ページ上の全ShapeのExtents(外接矩形)を計算する。
2. メモリ最適化フェーズ: 大量オブジェクトのループ処理におけるメモリリークを防ぐため、オブジェクト参照は明示的にNothingを代入する。
3. PageSheet更新フェーズ: `Formula` プロパティ経由でセル値を更新し、Visioの再計算エンジンを駆動させる。
3. 実装:動的リサイズ・エンジン
以下は、ページ内の全図形を検知し、適切な余白(Margin)を維持したまま用紙サイズを自動追従させるプロトタイプだ。
Option Explicit
‘ 伝説的なアーキテクトによる、高速境界計算エンジンの核心
Public Sub AutoResizePage(Optional ByVal Margin As Double = 0.5)
Dim vsoPage As Visio.Page
Dim vsoShapes As Visio.Shapes
Dim vsoShape As Visio.Shape
Dim dblMinX As Double, dblMinY As Double
Dim dblMaxX As Double, dblMaxY As Double
Dim dblWidth As Double, dblHeight As Double
Set vsoPage = ActivePage
Set vsoShapes = vsoPage.Shapes
If vsoShapes.Count = 0 Then Exit Sub
‘ 初期境界のセット
dblMinX = 999: dblMinY = 999
dblMaxX = -999: dblMaxY = -999
‘ SpatialSearchではなく、全Shapeの有効範囲を反復調査(大規模図面ではSpatialSearchへ移行推奨)
For Each vsoShape In vsoShapes
‘ 非表示レイヤーや特殊Shapeの除外判定をここに実装せよ
If Not vsoShape.OneD Then
dblMinX = Application.Min(dblMinX, vsoShape.Cells(“PinX”).ResultIU – (vsoShape.Cells(“Width”).ResultIU / 2))
dblMinY = Application.Min(dblMinY, vsoShape.Cells(“PinY”).ResultIU – (vsoShape.Cells(“Height”).ResultIU / 2))
dblMaxX = Application.Max(dblMaxX, vsoShape.Cells(“PinX”).ResultIU + (vsoShape.Cells(“Width”).ResultIU / 2))
dblMaxY = Application.Max(dblMaxY, vsoShape.Cells(“PinY”).ResultIU + (vsoShape.Cells(“Height”).ResultIU / 2))
End If
Next vsoShape
‘ PageSheetへの直接介入
Dim vsoPageSheet As Visio.Shape
Set vsoPageSheet = vsoPage.PageSheet
‘ 計算ロジック: 境界 + 余白
dblWidth = (dblMaxX – dblMinX) + (Margin 2)
dblHeight = (dblMaxY – dblMinY) + (Margin 2)
‘ Visioエンジンに対する直接的なセル更新
‘ Formulaで書き込むことで、Visioの単位変換エンジンを信頼する
vsoPageSheet.Cells(“PageWidth”).Formula = dblWidth
vsoPageSheet.Cells(“PageHeight”).Formula = dblHeight
‘ メモリの明示的解放(VBAのガーベジコレクションを待たない)
Set vsoShape = Nothing
Set vsoShapes = Nothing
Set vsoPageSheet = Nothing
Set vsoPage = Nothing
End Sub
4. シニアエンジニアのための運用上の注意
- 再計算のトリガー: `PageSheet` を更新すると、Visioは即座にレイアウトの再計算を行う。大規模な図面の場合、`Application.ScreenUpdating = False` を使用して、描画負荷を一時的に遮断せよ。
- 単位の罠: `ResultIU` は「内部単位(インチ)」を返す。もしドキュメントがミリメートル単位で運用されている場合、値をそのまま数値として代入すると、とてつもない巨大なページが出来上がる。必ず `Application.ConvertResult` を介して単位変換を行うか、Visioが自動解釈する「インチ換算値」を渡す必要がある。
- イベントドリブンへの昇華: この処理を `Document_ShapeAdded` や `Document_ShapeDeleted` イベントに紐付けるのは「愚策」だ。ユーザーが図形をドラッグしている最中にページが激しく伸縮し、UXを破壊する。必ず「ボタン押下」か「バッチ処理」として実装せよ。
結び
Visioの内部セルを直接操作するということは、Visioの「魂」に触れることと同義だ。
リファレンスには載っていない挙動や、特定のバージョンでしか発生しない再計算のラグ。これらを知り尽くした者だけが、真に堅牢な業務自動化を実現できる。
さあ、GUIの制限から脱却せよ。あなたのコードでVisioを再定義するのだ。
