【実務・中級編】Pageオブジェクトの座標系を完全理解:図面の描画領域とページサイズをVBAで動的制御する – Visio VBA解析バイブル

スポンサーリンク

【Visio VBA極限の知見】第1回:Pageオブジェクトの座標系を完全理解し、用紙サイズと描画領域を完全に支配する

世の中の多くのVisio VBAの解説記事は、「マクロの記録」をそのまま貼り付けただけの、実務では使い物にならないコードで溢れている。
特に `Page` オブジェクト、そしてその背後にある「座標系」「描画単位」「縮尺」の概念を正確に理解していないがために、プログラムを実行するたびに図形があらぬ方向へ飛んでいったり、意図しない用紙サイズで印刷されて発狂した経験はないだろうか?

私はこれまで、何十もの巨大なプラント図、ネットワーク構成図、そして数千要素におよぶ自動レイアウトツールをVisioで構築してきた。
その経験から断言する。Visioの座標系を制する者が、Visio自動化を制する。

今回は、Visio特有の「見えない罠」を論理的に解体し、実務の現場で絶対に破綻しない堅牢な動的制御コードを伝授する。

1. Visio座標系の核心:なぜ「インチ」と「内部単位」を混同してはいけないのか?

Visioの内部エンジンは、徹底して「インチ(Inches)」で世界を構築している。
画面上でミリメートル(mm)やキロメートル(km)が表示されていようとも、VBAが触るオブジェクトのプロパティ(`PinX`, `PinY`, `Width`, `Height` など)のデフォルト値は、すべて「内部インチ」だ。

さらに厄介なのが、Page.PageSheet(ページのシェイプシート)における座標の原点(`LocPinX`, `LocPinY`)と、ページ左下を原点とするデカルト座標系の関係である。

破壊的なバグを生む「縮尺(Scale)」の罠

図面プロパティで「1:100」のような縮尺を設定した瞬間、開発者の脳内はバグの温床と化す。

  • 物理的な用紙サイズ(例:A4なら 8.27 × 11.69 インチ)
  • 描画領域の論理サイズ(縮尺が適用された世界)

これらをコード上で混同すると、「図形をページの中心に置いたはずなのに、はるか彼方の宇宙空間に配置された」という現象が起きる。実務ツールでは、まずページの縮尺と描画単位をコード側で明示的に固定・取得することから始めなければならない。

2. 実務で必須:用紙サイズ・向き・余白を動的制御する設計思想

自動化スクリプトにおいて、「ユーザーが手動で設定した用紙サイズに依存する」という設計は悪である。
データ量や出力要件に応じて、VBA側から強制的に用紙サイズ(A3、A4、カスタムなど)、向き(縦・横)、そして描画領域を完璧にコントロールできなければ、プロダクションコードとは言えない。

Visioで用紙サイズを変更するには、`Page.PageSheet` の以下のセルを書き換える必要がある。

  • `PageWidth`(ページの幅)
  • `PageHeight`(ページの高さ)
  • `PrintLandscape`(印刷の向き:0=縦, 1=横)

しかし、ただ値を代入するだけでは不十分だ。Visioの単位変換関数 `Visio.Application.ConvertResult` を使い、ミリメートルからインチへの変換をプログラム内部で担保する必要がある。

3. 【プロダクションコード】用紙サイズ自動最適化 & 座標系マスタクラス

以下のコードは、指定した用紙サイズ(A4/A3)と向きを動的に設定し、図面の中心座標を正確に計算してグリッド状に図形を配置していく、実務仕様のVBAモジュールだ。

コピペしてそのまま動作し、かつエラーハンドリングとオブジェクトの解放(メモリリーク対策)まで考慮されたプロフェッショナルコードである。

Option Explicit

‘ =========================================================================
‘ 模範的なVisio Page制御モジュール
‘ Theme: 用紙サイズの動的変更と安全な座標計算
‘ =========================================================================

Public Sub InitializeAndLayoutPage()
Dim vsoPage As Visio.Page
Dim vsoPageSheet As Visio.Shape

‘ エラーハンドリングの標準装備
On Error GoTo ErrorHandler

‘ 1. アクティブページの取得
Set vsoPage = ActivePage
Set vsoPageSheet = vsoPage.PageSheet

‘ 2. 画面描画の停止(パフォーマンス劇的向上のお作法)
Application.ScreenUpdating = False

‘ 3. 用紙サイズを「A4・横(Landscape)」に動的設定
‘ Visio内部単位(インチ)に換算して設定する
‘ A4横サイズ: 幅 297mm = 11.6925インチ, 高さ 210mm = 8.2675インチ
Call SetPageSize(vsoPageSheet, 297, 210, True)

‘ 4. ページの中央座標を取得し、図形を配置するテスト
Call DrawCenteredBox(vsoPage)

‘ 5. 描画再開
Application.ScreenUpdating = True
MsgBox “ページの最適化とレイアウトが完了しました。”, vbInformation, “成功”
Exit Sub

ErrorHandler:
Application.ScreenUpdating = True
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
End Sub

‘ ————————————————————————-
‘ 補助プロシージャ: mm単位で用紙サイズと向きを安全に設定する
‘ ————————————————————————-
Private Sub SetPageSize(ByVal pageSheet As Visio.Shape, ByVal widthMm As Double, ByVal heightMm As Double, ByVal isLandscape As Boolean)
Dim widthInches As Double
Dim heightInches As Double

‘ mmをVisioの基本単位であるインチに変換 (1 inch = 25.4 mm)
widthInches = widthMm / 25.4
heightInches = heightMm / 25.4

‘ シェイプシートの数式を直接書き換え
If isLandscape Then
‘ 横向きの場合、幅と高さを入れ替える必要がある用紙の場合考慮
pageSheet.CellsU(“PageWidth”).FormulaU = widthInches & ” in”
pageSheet.CellsU(“PageHeight”).FormulaU = heightInches & ” in”
pageSheet.CellsU(“PrintLandscape”).FormulaU = “1” ‘ 1: 横
Else
pageSheet.CellsU(“PageWidth”).FormulaU = heightInches & ” in”
pageSheet.CellsU(“PageHeight”).FormulaU = widthInches & ” in”
pageSheet.CellsU(“PrintLandscape”).FormulaU = “0” ‘ 0: 縦
End If
End Sub

‘ ————————————————————————-
‘ 補助プロシージャ: ページ中央に図形を正確に配置するロジック
‘ ————————————————————————-
Private Sub DrawCenteredBox(ByVal targetPage As Visio.Page)
Dim vsoShape As Visio.Shape
Dim pageWidth As Double
Dim pageHeight As Double
Dim centerX As Double
Dim centerY As Double

‘ ページの現在の幅と高さをインチで取得
pageWidth = targetPage.PageSheet.CellsU(“PageWidth”).ResultIU
pageHeight = targetPage.PageSheet.CellsU(“PageHeight”).ResultIU

‘ 幾何学的な中心を計算
centerX = pageWidth / 2
centerY = pageHeight / 2

‘ 簡易的な矩形を描画 (幅 4インチ, 高さ 2インチの四角形)
Set vsoShape = targetPage.DrawRectangle(0, 0, 0, 0) ‘ 一旦仮作成

‘ シェイプのサイズと位置を設定(PinX/PinYは図形の中心基準)
vsoShape.CellsU(“Width”).FormulaU = “4 in”
vsoShape.CellsU(“Height”).FormulaU = “2 in”
vsoShape.CellsU(“PinX”).FormulaU = centerX & ” in”
vsoShape.CellsU(“PinY”).FormulaU = centerY & ” in”

vsoShape.Text = “動的中央配置”

‘ オブジェクトの解放
Set vsoShape = Nothing
End Sub

4. チーフアーキテクトからの実務アドバイス:データベース・外部ファイル連携時の注意点

Excel、Access、あるいはWeb APIから取得したデータを基にVisio図面を自動生成するシステムでは、以下の鉄則を守らなければならない。

1. `Application.ScreenUpdating = False` は絶対に忘れるな
形状を1つ追加・移動するたびにVisioが画面を再描画していると、処理速度が数十倍〜数百倍遅くなる。数千個のシェイプを扱うシステムでは致命傷になるため、処理の最初で止め、最後で復元する。
2. 単位(`in`, `mm`, `pt`)をFormulaUに明記せよ
`CellsU` を使用して数式を代入する際は、必ず文字列として単位(例: `”100 mm”` や `”4 in”`)を付与すること。単位を省略するとVisioの現在のデフォルト単位(ユーザーが設定で変えてしまうもの)に依存してしまい、別環境で実行した瞬間にレイアウトが崩壊する。
3. オブジェクトの解放(`Set … = Nothing`)
VBAのガベージコレクションは甘い。特にループ処理の中で `Page` や `Shape` オブジェクトを変数に格納し続けると、メモリリークを起こしてVisioがフリーズする。ループ内でのオブジェクト生成・破棄のライフサイクルには細心の注意を払うこと。

座標系と用紙サイズの概念を完全に手の内に収めれば、Visioは単なる「お絵描きツール」から、「完全無欠な自動ドキュメント生成エンジン」へと変貌する。
次のステップでは、このページ上に動的にマスターシェイプを配置し、コネクタで美しく結線するアルゴリズムの極意を伝授しよう。

タイトルとURLをコピーしました