【実務・中級編】【初心者向け】ページサイズを「カスタムサイズ」に設定し、図面枠をページ中央に配置する初期化マクロ – Visio VBA解析バイブル

スポンサーリンク
スポンサーリンク

「動かないコード」から「揺るがない基盤」へ:Visio VBAにおけるページ初期化の極意

業務自動化の現場において、ツールが「たまたま動く」ことと「業務に耐えうる」ことの間には、深淵な溝がある。

多くの開発者が、Visio VBAでページサイズを変更しようとする際、以下のようなコードを書き殴る。

' 素人が書く、壊れやすいコードの典型例
ActivePage.PageSheet.Cells("Width").Formula = "200 mm"
ActivePage.PageSheet.Cells("Height").Formula = "150 mm"

これでは、設計としては三流だ。単位の解釈が実行環境のロケールに依存するリスク、マジックナンバー(直書き数値)の散布、そしてエラーハンドリングの欠如。これらは、将来的に「Excelから読み込んだ設計値に基づいて、大量の図面を自動生成する」といったスケールアップが必要になった瞬間、致命的なバグとなって牙を剥く。

本記事では、単なる「サイズ変更のやり方」ではない。PageSheetというエンジンの深部を制御し、保守性と堅牢性を担保した「プロダクション・グレード」の初期化ロジックを伝授する。

1. アーキテクチャの視点:PageSheetとは何か

Visioにおいて、ページは単なるキャンバスではない。すべての幾何学的属性(サイズ、余白、印刷設定)は、`PageSheet` という特殊なシート内の「セル(Cell)」によって管理されている。

我々エンジニアが操作しているのは、GUIのボタンではなく、数式(Formula)に基づいたデータモデルである。

なぜ `FormulaU` を使うべきなのか

VBAには `Formula` と `FormulaU` というプロパティが存在する。

  • `Formula`: ユーザーの言語設定(ロケール)に依存した形式。
  • `FormulaU`: ユニバーサル(一貫)な形式。

グローバル展開や、異なる言語環境のPCで動くツールを作るなら、常に `FormulaU` を選択するのが鉄則だ。ローカルな環境で「10,5 mm」と書いたつもりが、他国の環境ではカンマとピリオドの解釈の違いでエラーになる。そんな低レベルなミスは、プロフェッショナルの仕事ではない。

2. 実装:堅牢なページ初期化マクロ

以下に示すコードは、単にサイズを変えるだけではない。引数による制御、エラーハンドリング、そして「図面枠をページ中央に配置する」というロジックを、Visioの計算エンジンに計算させる(ShapeSheet式を利用する)高度な設計を採用している。

Option Explicit

''' <summary>
''' ページをカスタムサイズに設定し、指定された図面枠を中央に配置する。
''' </summary>
''' <param name="targetPage">対象となるVisioのPageオブジェクト</param>
''' <param name="widthMm">幅 (mm)</param>
''' <param name="heightMm">高さ (mm)</param>
''' <param name="frameShapeName">中央配置する図面枠のシェイプ名</param>
Public Sub InitializePageLayout( _
    ByVal targetPage As Visio.Page, _
    ByVal widthMm As Double, _
    ByVal heightMm As Double, _
    ByVal frameShapeName As String)

    On Error GoTo ErrorHandler

    Dim pgSheet As Visio.PageSheet
    Set pgSheet = targetPage.PageSheet

    ' --- 1. ページサイズの変更 ---
    ' FormulaUを使用し、単位を明示的に指定することでロケール依存を排除する
    pgSheet.CellsU("Width").FormulaU = widthMm & " mm"
    pgSheet.CellsU("Height").FormulaU = heightMm & " mm"

    ' --- 2. 図面枠の検索と配置 ---
    Dim frameShape As Visio.Shape
    Set frameShape = targetPage.Shapes(frameShapeName)

    ' 図面枠をページ中央に配置するロジック
    ' VBA側で計算するのではなく、ShapeSheetの数式(FormulaU)に計算を委ねる。
    ' これにより、将来的にページサイズが動的に変わった場合でも、
    ' 図面枠は常に「ページの中央」を維持し続ける「生きている」設計になる。
    
    ' PinX/PinY: シェイプの基準点(中心点)
    ' PageSheet.Width/Height: ページの現在の幅/高さ
    frameShape.CellsU("PinX").FormulaU = "PageSheet.Width*0.5"
    frameShape.CellsU("PinY").FormulaU = "PageSheet.Height*0.5"
    
    ' シェイプ自体の中心がPinに一致するように設定(図面枠が自身の中心で回転・移動できるようにする)
    frameShape.CellsU("LocPinX").FormulaU = "Width*0.5"
    frameShape.CellsU("LocPinY").FormulaU = "Height*0.5"

    Debug.Print "Successfully initialized page: " & targetPage.Name

ExitSub:
    Exit Sub

ErrorHandler:
    ' 実務ではここでログファイルやデータベースへエラーを記録する
    MsgBox "Critical Error in InitializePageLayout:" & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Description: " & Err.Description, vbCritical, "Automation Error"
    Resume ExitSub
End Sub

''' <summary>
''' 実行用エントリーポイント(テスト用)
''' </summary>
Public Sub RunInitializationDemo()
    Dim pg As Visio.Page
    Set pg = ActivePage

    ' 実際の実務では、これらの値はExcelや外部DBから取得する
    Const TARGET_WIDTH As Double = 297#  ' A4横相当
    Const TARGET_HEIGHT As Double = 210#
    Const FRAME_NAME As String = "DrawingFrame" ' あらかじめ配置されている図面枠の名前

    Call InitializePageLayout(pg, TARGET_WIDTH, TARGET_HEIGHT, FRAME_NAME)
End Sub

3. リーダーからの設計講義:なぜこのコードは「強い」のか

このコードには、設計上の重要な意図が3つ組み込まれている。

① 計算の委譲(Delegation of Calculation)

多くの初心者は、`frameShape.CellsU(“PinX”).FormulaU = (widthMm / 2) & ” mm”` と書こうとする。これは間違いだ。
私が書いたコードは `frameShape.CellsU(“PinX”).FormulaU = “PageSheet.Width*0.5″` である。
これは、「計算をVBAで行うのではなく、Visioの内部エンジン(ShapeSheet)に計算の責任を押し付ける」という設計だ。これにより、万が一プログラムの後半でページサイズが変更されたとしても、図面枠は自動的に追従する。これを「宣言的な設計」と呼ぶ。

② 結合度の低減(Loose Coupling)

`InitializePageLayout` プロシージャは、`ActivePage` に依存していない。引数として `targetPage` を受け取る。これにより、このモジュールは「現在開いているページ」だけでなく、「バックグラウンドで生成中の非表示ページ」に対しても全く同じロジックで適用できる。再利用性とテスト容易性が劇的に向上する。

③ 外部連携を見据えた型定義

引数の `widthMm` や `heightMm` を `Double` 型で受け取っている点に注目してほしい。もしこれが文字列(String)で渡されていたら、呼び出し元で数値変換のバリデーションが必要になり、コードの複雑性が増す。
「データを受け取る入り口」で型を厳格に定義することは、バグの混入を防ぐ防波堤となる。

4. 実務における注意点:DB・ファイル連携への橋渡し

このマクロを実用的なツールに昇華させるなら、次に考えるべきは「パラメータのソース」だ。

1. Excel/CSV連携の場合:
セルの値を直接渡すのではなく、必ず `IsNumeric()` 等で数値チェックを行い、負の値やゼロが入っていないかをバリデーションしてから本関数へ渡せ。
2. データベース連携の場合:
DBから取得した値は `Double` 型として適切にキャストし、トランザクション管理下で処理を行え。Visioの操作は重い。大量のページを生成する際は、`Application.ScreenUpdating = False`(Visioでは `Application.EnableEvents` 等の制御)を検討し、パフォーマンスのボトルネックを排除せよ。

結論

Visio VBAの自動化において、最も避けるべきは「動けばいい」という思考停止だ。
`PageSheet` のセルを操作するということは、図面の物理的な構造を書き換えるという重い行為である。

「数式を使い、ロケールを意識し、計算をエンジンに委ねる」

この原則を守るだけで、君が書くコードの品質は、単なるスクリプトから、プロフェッショナルな「ソフトウェア」へと進化する。

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