【入門編】【上級】Visioアドイン開発を見据えたステンシル管理クラスの設計 – Visio VBA解析バイブル

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

【上級】Visio VBAを「ツール」から「システム」へ。ステンシル管理クラスの設計術

こんにちは。今日は、Visio VBAの世界で「マクロの記録」という魔法から卒業し、プロフェッショナルな「ソフトウェア開発」の入り口に立つための、非常に重要な話をしましょう。

皆さんは、マクロの記録を使って「ステンシルを開き、シェイプを配置する」という操作を記録したことはありませんか? そのコードは動きます。しかし、もし「ステンシルファイルの名前が変わったら?」「指定したシェイプがステンシル内に存在しなかったら?」「大量のシェイプを配置する際に動作が重くなったら?」……そういった「現実のトラブル」に直面したとき、記録されたコードは脆くも崩れ去ります。

今日は、アドイン開発(VSTOやCOMアドイン)を見据えた、「ステンシル管理クラス」の設計について伝授します。これをマスターすれば、あなたのコードは単なる「自動化スクリプト」から、堅牢な「業務基盤」へと進化します。

1. なぜ「クラス」が必要なのか? —— オブジェクトのライフサイクルを制御する

まず、概念を整理しましょう。Visioにおけるステンシルの扱いは、以下の3つのステップで構成されます。

1. 開く (Open):ファイルを探し、メモリ上に読み込む。
2. 探す (Search):中にある「マスター(シェイプの雛形)」を見つける。
3. 使う (Use/Drop):ページ上に配置する。

一般的なプログラミング初学者は、これらを標準モジュールにバラバラに書きがちです。しかし、これでは「ステンシルを開きっぱなしにしてメモリを圧迫する」、あるいは「エラーが起きたときにどこで止まったか分からない」といった問題が発生します。

これを解決するのがカプセル化です。
「ステンシルの読み込み、検索、破棄」という一連のライフサイクル(寿命)を一つの「クラス」の中に閉じ込めるのです。これにより、使う側(メイン処理)は「どうやって開くか」を知る必要はなく、「このクラスに頼めばやってくれる」という状態になります。これが、設計の第一歩です。

2. 実装:ステンシル管理クラス `clsStencilManager`

それでは、実際にクラスモジュールを作成していきましょう。VBAエディタで「挿入」→「クラスモジュール」を選択し、名前を `clsStencilManager` に変更してください。

クラスモジュール:`clsStencilManager`

Option Explicit

' =================================================================
' クラス名: clsStencilManager
' 目的: ステンシルの読み込み、マスターの検索、配置をカプセル化する
' =================================================================

Private pStencil As Visio.Stencil      ' 読み込んだステンシルオブジェクト
Private pStencilPath As String         ' ステンシルのファイルパス

' --- プロパティ: ステンシルパスの設定 ---
Public Property Let StencilPath(ByVal Value As String)
    pStencilPath = Value
End Property

' --- メソッド: ステンシルの読み込み ---
' エラーハンドリングを行い、安全にステンシルを開きます
Public Function Load() As Boolean
    On Error GoTo ErrorHandler
    
    ' すでに開いている場合は一度閉じる(二重読み込み防止)
    If Not pStencil Is Nothing Then Call CloseStencil
    
    ' ステンシルのオープン(ファイルが存在するかチェックが必要)
    ' Visio.Application.Documents.OpenEx を使用
    Set pStencil = Visio.Application.Documents.OpenEx(pStencilPath, visOpenDocked)
    
    Load = True
    Exit Function

ErrorHandler:
    Debug.Print "【Error】ステンシルの読み込みに失敗しました: " & Err.Description
    Load = False
End Function

' --- メソッド: マスターシェイプの取得 ---
' 指定した名前のマスターが存在するか探し、オブジェクトを返します
Public Function GetMaster(ByVal MasterName As String) As Visio.Master
    On Error Resume Next ' 見つからない場合のエラーをハンドリング
    
    Dim m As Visio.Master
    ' ステンシル内のマスターをループして検索
    For Each m In pStencil.Masters
        If m.Name = MasterName Then
            Set GetMaster = m
            Exit Function
        End If
    Next m
    
    ' 見つからなかった場合
    Set GetMaster = Nothing
    Debug.Print "【Warning】マスターが見つかりません: " & MasterName
End Function

' --- メソッド: シェイプの配置 (Drop) ---
' 指定したページにマスターを配置します
Public Sub DropMaster(ByVal Page As Visio.Page, ByVal MasterName As String, ByVal x As Double, ByVal y As Double)
    Dim targetMaster As Visio.Master
    Set targetMaster = GetMaster(MasterName)
    
    If targetMaster Is Nothing Then
        Err.Raise vbObjectError + 513, "clsStencilManager", "配置対象のマスターが存在しません。"
    End If
    
    ' 指定した座標にシェイプをドロップ
    Page.Drop targetMaster, x, y
End Sub

' --- メソッド: 終了処理 (重要!) ---
' オブジェクトを解放し、ステンシルを閉じます
Public Sub CloseStencil()
    On Error Resume Next
    If Not pStencil Is Nothing Then
        pStencil.Close
        Set pStencil = Nothing
    End If
    Debug.Print "【Info】ステンシルを正常に閉じました。"
End Sub

' デストラクタの代わり(クラスが破棄される際に呼び出す)
Private Sub Class_Terminate()
    Call CloseStencil
End Sub

3. 実装:メイン処理(標準モジュール)

クラスができたら、次はそれを使う側です。メイン処理は驚くほどスッキリします。

標準モジュール

Option Explicit

Sub MainProcess()
    ' 1. クラスのインスタンス化
    Dim stencilMgr As clsStencilManager
    Set stencilMgr = New clsStencilManager
    
    ' 2. 設定(パスは環境に合わせて書き換えてください)
    ' 本来は設定ファイルや引数から取得するのが理想的です
    stencilMgr.StencilPath = "C:\Users\Public\Documents\Visio Stencils\Basic Shapes.vssx"
    
    ' 3. ステンシルの読み込み
    If Not stencilMgr.Load() Then
        MsgBox "ステンシルが開けませんでした。パスを確認してください。", vbCritical
        Exit Sub
    End If
    
    ' 4. シェイプの配置実行
    On Error GoTo ErrorHandler
    Dim targetPage As Visio.Page
    Set targetPage = ActivePage
    
    ' 「Rectangle」というマスターを座標 (2, 2) に配置
    ' ※マスター名はVisioの内部名(英語名が多い)に注意
    stencilMgr.DropMaster targetPage, "Rectangle", 2, 2
    stencilMgr.DropMaster targetPage, "Ellipse", 4, 4
    
    MsgBox "配置が完了しました!", vbInformation

CleanUp:
    ' 5. 後始末(ここがプロの仕事)
    ' クラスを破棄することで、自動的にCloseStencilが呼ばれる設計にしています
    Set stencilMgr = Nothing
    Exit Sub

ErrorHandler:
    MsgBox "エラーが発生しました: " & Err.Description, vbExclamation
    Resume CleanUp
End Sub

4. 伝説のアーキテクトが教える「ここが勘所」

コードを書き終えたあなたに、さらに高い視点を与えるための「極限の知見」を授けましょう。

① 「Master」と「Shape」を混同しない

ここが最も多いミスです。

  • Master(マスター): ステンシルの中にある「設計図」です。これ自体はページ上の物体ではありません。
  • Shape(シェイプ): マスターを `Drop` した結果、ページ上に現れる「実体」です。

配置した後に色を変えたり動かしたりしたい場合は、`Drop` メソッドの戻り値として `Visio.Shape` オブジェクトを受け取るように設計を拡張してください。

② リソースの解放を「絶対に」忘れない

VBAはガベージコレクション(メモリ自動掃除)がそれほど強力ではありません。特にVisioのような重いアプリケーションでは、ステンシルを開きっぱなしにすると、次にマクロを動かしたときに「ファイルが使用中です」というエラーに泣かされることになります。
今回のコードでは `Class_Terminate` を使い、「クラスが消えるときは必ずステンシルも閉じる」という鉄則を守っています。

③ パフォーマンスの重みを知る

もし1,000個のシェイプを配置する場合、ループの中で毎回 `OpenEx` や `Close` を呼んではいけません。

  • Bad: `ループ開始` → `開く` → `配置` → `閉じる` → `ループ終了`
  • Good: `開く` → `ループ開始` → `配置` → `ループ終了` → `閉じる`

この「I/O(入出力)の回数を減らす」という意識が、爆速のツールを作る鍵です。

まとめ:次の一歩へ

お疲れ様でした。あなたは今、単なる「マクロの記録者」から、「オブジェクト指向に基づいた設計者」への第一歩を踏み出しました。

今回作成した `clsStencilManager` は、そのまま C# や VB.NET を使った Visio アドイン開発へと移植可能な設計になっています。ロジックがクラスにまとまっているため、言語が変わっても「何をすべきか」の構造が崩れないからです。

「動けばいい」の先にある「美しく、壊れにくい」コードを目指してください。そこには、エンジニアとしての真の自由が待っていますよ。

ここをクリアすれば、Visio VBAの基本はバッチリですよ。応援しています!

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