【実務直結】Visio VBAを掌握する極限の知見:ページサイズ変更に伴う「図面枠」の自動リサイズと再配置
図面自動生成ツールやドキュメント管理システムを構築する際、避けて通れないのが「ページサイズの動的変更と図面枠(タイトルブロック)の追従」だ。
「A4からA3へ拡大したら、図面枠が左上にポツンと取り残された」「右側のマージンからはみ出した」。
そんな笑えないバグに直面した開発者は多いはずだ。マウス操作であればVisioの機能がよしなにやってくれることも、VBAで `Page.PageSheet` を叩く瞬間、その幻想は木端微塵に砕け散る。
今回は、Visio VBAの内部アーキテクチャを知り尽くしたチーフアーキテクトの視点から、「1ピクセルの狂いもなくページ端に吸着し、変形に完全追従する堅牢な図面枠コントロールロジック」を授けよう。
—
1. なぜ素朴なVBAコードでは破綻するのか?
多くの開発者が陥るアンチパターンは、ページサイズ変更後に「図面枠のシェイプを特定し、幅(Width)と高さ(Height)を直接代入する」というアプローチだ。
【やってはいけない非効率な設計】
1. Page.PageSheetのWidth/Heightを変更する
2. 図面枠シェイプの PinX / PinY / Width / Height をゴリゴリ計算して代入する
このやり方では、以下の致命的な問題が発生する。
- イベントの非同期性と計算順序の崩壊: Visioのセル評価エンジン(ShapeSheet Formula)が計算を完了する前にVBAが座標を上書きし、レイアウトが崩壊する。
- 図面枠内のテキスト(改版履歴や図面番号)の歪み: 単にシェイプを引き延ばすと、中に配置されたテキストブロックやグリッド線まで縦横比無視でストレッチされてしまう。
- 基準点(PinPos)の迷子: シェイプの回転中心がどこにあるかによって、リサイズ時にあらぬ方向へ飛んでいく。
究極の解決策:「ShapeSheet数式リンク」と「スマートグループ化」
Visio VBAで最もエレガントかつバグらない方法は、図面枠シェイプのプロパティ(Width, Height, PinX, PinY)に、直接ページ自身のサイズセル(PageWidth, PageHeight)への参照数式を流し込むことだ。
値をハードコーディングするのではなく、「ページの幅が変わったら、お前の幅もそれに追従しろ」という数式(Formula)をShapeSheetに埋め込む。これこそが、Visioのネイティブな挙動を味方につけた唯一無二の正解である。
—
2. 堅牢なプロダクションコード
以下のコードは、アクティブページのサイズを変更し、同時にページ内に存在する図面枠(特定のマスターシェイプ、またはタグ付けされたグループ)を完璧に追従・リサイズさせる実務直結のモジュールだ。
Option Explicit
‘ ==============================================================================
‘ 担当: チーフアーキテクト
‘ 概要: ページサイズを動的に変更し、図面枠(タイトルブロック)を数式リンクで完全追従させる
‘ ==============================================================================
Public Sub ResizePageAndFrame(ByVal targetWidth As Double, ByVal targetHeight As Double)
Dim vsoPage As Visio.Page
Dim vsoPageSheet As Visio.Shape
Dim vsoFrameShape As Visio.Shape
‘ エラーハンドリングの標準装備
On Error GoTo ErrorHandler
Set vsoPage = ActivePage
Set vsoPageSheet = vsoPage.PageSheet
‘ 1. トランザクションの開始(VisioのUndoスタックをまとめ、パフォーマンスを最大化)
Application.BeginUndoScope “ページサイズ変更と図面枠の自動追従”
‘ 2. ページサイズの変更(単位は内部単位:インチ。ミリメートルから変換する場合は mm / 25.4)
‘ ※PageWidth / PageHeight セルに直接値を書き込む
vsoPageSheet.CellsSRC(visSectionObject, visRowPageLayout, visPLSzPageWidth).FormulaU = targetWidth & ” in”
vsoPageSheet.CellsSRC(visSectionObject, visRowPageLayout, visPLSzPageHeight).FormulaU = targetHeight & ” in”
‘ 3. 図面枠シェイプの取得(ここでは例として、User.IsTitleBlock = 1 というカスタムセルのあるシェイプを探索)
Set vsoFrameShape = FindTitleBlockShape(vsoPage)
If Not vsoFrameShape Is Nothing Then
‘ 4. ShapeSheet数式バインドによる完全追従ロジック
‘ 【極意】数値ではなく「PageWidth」「PageHeight」という変数(数式)をバインドする
‘ 幅・高さのバインド
vsoFrameShape.CellsU(“Width”).FormulaU = “ThePage!PageWidth”
vsoFrameShape.CellsU(“Height”).FormulaU = “ThePage!PageHeight”
‘ ピン位置(基準点)を左下(PinPos = 0)に固定し、X=0, Y=0 に配置
vsoFrameShape.CellsU(“PinPos”).FormulaU = “0” ‘ 0: 左下
vsoFrameShape.CellsU(“PinX”).FormulaU = “0”
vsoFrameShape.CellsU(“PinY”).FormulaU = “0”
Else
MsgBox “警告: ページ内にタイトルブロック(図面枠)が見つかりませんでした。”, vbExclamation, “レイアウトエンジン”
End If
‘ 5. トランザクションの正常終了
Application.EndUndoScope True
Exit Sub
ErrorHandler:
‘ 異常終了時はUndoスコープをロールバック
Application.EndUndoScope False
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “VBA Runtime Error”
End Sub
‘ ——————————————————————————
‘ 補助関数: ページ内から図面枠シェイプを特定する
‘ ——————————————————————————
Private Function FindTitleBlockShape(ByVal pg As Visio.Page) As Visio.Shape
Dim shp As Visio.Shape
For Each shp in pg.Shapes
‘ カスタムプロパティやセルの存在チェック(安全な判定)
On Error Resume Next
If shp.CellExists(“Prop.IsTitleBlock”, VisExistsFlags.visExistsAnywhere) Then
If shp.Cells(“Prop.IsTitleBlock”).ResultInt(visSectionProp, 0) = 1 Then
Set FindTitleBlockShape = shp
Exit Function
End If
End If
On Error GoTo 0
Next shp
‘ 見つからない場合は、レイヤー名や名刺代わりとして最初のグループを返すフォールバックも可
If pg.Shapes.Count > 0 Then
Set FindTitleBlockShape = pg.Shapes(1) ‘ ※実務では必ず固有の識別子を持たせること
End If
End Sub
—
3. 実務・データベース連携における設計上の注意点
このコードを実際の業務システム(Excelマクロからの呼び出し、あるいはC#からのCOM Interop連携)に組み込む場合、以下の「プロの知見」を忘れてはならない。
1. 単位系の罠(インチとミリメートル)
Visioの内部計算エンジンは常に「インチ(Inches)」で駆動している。データベースやUIから「A3サイズ(420mm × 297mm)」を受け取った場合、そのまま渡すとミドルウェア側で大惨事になる。必ず `420 / 25.4` のようにインチ換算を挟むか、Visioの `VisUnitCodes.visMillimeters` を用いたパース処理を実装すること。
2. 図面枠内の「変形させてはいけない要素」の保護
タイトルブロックの「社名ロゴ」や「承認印欄」は、ページ全体を引き延ばしたときに一緒に縦横比が崩れては困る。これらを防ぐには、図面枠自体を「グループシェイプ」にし、子シェイプ側のLockWidth / LockHeight(幅・高さの固定)をあらかじめ有効にしておくこと。
親グループだけが `ThePage!PageWidth` に追従して拡大し、中のテキストやロゴは右下や所定の位置にピン留めされ続ける——これがプロのレイアウト設計だ。
3. トランザクション(UndoScope)の絶対遵守
Visio VBAは処理の途中でエラーが起きたとき、中途半端な状態でドキュメントが汚染される。`BeginUndoScope` と `EndUndoScope` のペアを必ず構築し、例外発生時に変更がアトミックにロールバックされる担保を入れよ。
—
総括
ページサイズの動的変更と図面枠の追従は、一見地味な処理に見えて、実はVisioの数式エンジン(ShapeSheet)の理解度がモロに露呈する領域だ。
「力技でVBAから座標を計算して代入する」というアマチュアの思考を捨て、「Visioのセルに数式をバインドし、レイアウト計算そのものをVisioに委譲する」。このアーキテクチャを採用した瞬間から、あなたの作る業務自動化ツールは、プロフェッショナルクオリティへと昇華する。
現場の生産性を極限まで高めるコードを、ぜひあなたのソリューションに組み込んでほしい。
