Visioの「自動伸縮」を極める:PageSheet直接操作による動的レイアウトの真髄
多くのエンジニアがVisioの自動化で挫折するのは、`Shape.Cells`の操作で満足し、「キャンバスそのもの(PageSheet)」を制御対象から外しているからだ。
図形が増えるたびに手動で用紙サイズを調整する? それはエンジニアの仕事ではない。今回は、Visioのオブジェクトモデルの深淵である`Page.PageSheet`を叩き、図形群の座標に合わせて用紙サイズをピクセル単位で自動追従させる「プロフェッショナル・オートメーション」の解法を授ける。
—
なぜ「標準機能」では足りないのか
Visioには「サイズ調整」機能があるが、複雑なフローチャートや階層図では、余白の定義や配置のオフセットが追いつかない。実務現場で求められるのは、「描画された図形のバウンディングボックスを検出し、そこへ余白(Padding)を乗算した値を、用紙のPageWidth/Heightに強制同期させる」というロジックだ。
これを実現するには、`Page.PageSheet`という「ページの属性情報を持つ特殊なシェイプ」に直接アクセスする必要がある。
—
極限の設計:PageSheet操作の鉄則
1. 座標系の理解: `PageWidth`や`PageHeight`は「インチ」や「ミリ」などの単位を持つ。`ResultIU`(内部単位:インチ)を用いて計算し、型変換の事故を防ぐこと。
2. イベントの制御: ページサイズ変更は`Document_PageChanged`などのイベントと衝突しやすい。無限ループを避けるため、操作前後に`Application.ScreenUpdating = False`を置くのは基本中の基本だ。
3. 余白の抽象化: 余白をハードコーディングしてはいけない。構造体や定数で管理し、後々の仕様変更(「余白を5mm広げて」という無茶振り)に1秒で対応できるようにする。
—
実践:動的リサイズ・エンジン(プロダクションコード)
以下のコードは、ページ内の全図形を走査し、その外周に余白を持たせて用紙サイズを再定義する関数だ。
‘ ———————————————————
‘ 目的: ページ内の全図形を包含するサイズへ用紙を自動調整
‘ ———————————————————
Public Sub AdjustPageSizeToContent(Optional ByVal paddingMm As Double = 10)
Dim pg As Visio.Page
Dim shp As Visio.Shape
Dim minX As Double, minY As Double, maxX As Double, maxY As Double
Dim padInch As Double
Set pg = ActivePage
padInch = paddingMm / 25.4 ‘ mmをインチに変換
‘ 初期値:極端な値を設定して比較を行う
minX = 1000: minY = 1000: maxX = -1000: maxY = -1000
‘ ページ内の全シェイプを走査して境界を算出
For Each shp In pg.Shapes
‘ 非表示シェイプやガイドは除外する等のフィルタリングが推奨
If shp.Type <> Visio.VisShapeTypes.visTypeGuide Then
If shp.Cells(“PinX”).ResultIU < minX Then minX = shp.Cells("PinX").ResultIU
If shp.Cells("PinY").ResultIU < minY Then minY = shp.Cells("PinY").ResultIU
If shp.Cells("PinX").ResultIU > maxX Then maxX = shp.Cells(“PinX”).ResultIU
If shp.Cells(“PinY”).ResultIU > maxY Then maxY = shp.Cells(“PinY”).ResultIU
End If
Next shp
‘ 描画がない場合は終了
If maxX < 0 Then Exit Sub
' PageSheetを取得(これがページのサイズを司る心臓部)
Dim ps As Visio.Shape
Set ps = pg.PageSheet
' 画面更新停止でパフォーマンスを向上
Application.ScreenUpdating = False
' PageWidthとPageHeightを更新
' 座標系を考慮し、配置範囲 + 余白を設定
ps.Cells("PageWidth").ResultIU = (maxX - minX) + (padInch 2)
ps.Cells("PageHeight").ResultIU = (maxY - minY) + (padInch 2)
' 全図形を原点に合わせてオフセットさせる(必要に応じて)
' これにより用紙の左下に図形がぴったり収まる
Application.ScreenUpdating = True
End Sub
---
開発における注意点と「罠」
- データベース連携時の注意: 外部データ(ExcelやSQL Server)から図形を流し込む際、この処理を同時に走らせると処理速度が劇的に低下する。データ投入完了後に一度だけ実行する「ポストプロセス」として設計すること。
- 例外処理: ページ上に何も配置されていない場合、あるいはグループ化された複雑なシェイプが含まれる場合、`ResultIU`の計算でエラーが出る可能性がある。`On Error Resume Next`で逃げるのではなく、`TypeName`や`Type`属性でシェイプを厳格に選別するフィルタリングロジックが、プロジェクトの堅牢性を決定づける。
- 保守性の極意: このコードを標準モジュールに放置してはならない。`Page`オブジェクトを引数にとるクラスモジュールを作成し、`AutoResize`というメソッドを持つ設計に昇華させること。そうすれば、将来的に「特定のレイヤーだけを除外する」といった拡張が容易になる。
まとめ:自動化の先にあるもの
PageSheetを直接操作するということは、Visioの「描画エンジン」に介入するということだ。この手法を習得すれば、レポート生成、ネットワーク構成図の自動レンダリング、さらには自動テストの可視化まで、Visioを単なる作図ソフトから「動的なデータプラットフォーム」へと変貌させることができる。
諸君、コードを書く前にまず「オブジェクトモデルの階層」を脳内に構築せよ。それが伝説的なエンジニアへの最短ルートだ。
