【上級】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の基本はバッチリですよ。応援しています!
