マクロ記録からの脱却:Visio VBAで「制御されたページ命名」を実装する極限の作法
こんにちは。Visio VBAの世界へようこそ。
「マクロの記録」ボタンを押して、自分の操作をコードに変換する。それは自動化への第一歩として素晴らしい習慣です。しかし、あなたが「業務で使える、堅牢なツール」を作りたいと願うようになったとき、必ずぶつかる壁があります。
それは、「Visioが良かれと思ってやってくれる『お節介な自動制御』」です。
例えば、ページを新しく追加したとき、もし同じ名前のページが既に存在していたらどうなるでしょうか? Visioは気を利かせて、ページ名に勝手に `(1)` や `-1` といった接尾辞を付けてしまいます。
「プロジェクト管理上、ページ名は必ず `Task_001`、`Task_002` という形式でなければならない」
そんな厳格なルールがある現場では、このVisioの親切心は「仕様違反」へと変わります。今回は、このVisioの挙動を完全に掌握し、独自の採番ルールを強制するための「カウンター管理」と「バリデーション(妥当性確認)」の技術を伝授しましょう。
ここをクリアすれば、あなたのVisio VBAは「記録されたコード」から「設計されたプログラム」へと進化します。
1. なぜ「変数」だけでは足りないのか?:状態の永続化
初学者が最初に陥る罠は、カウンター(現在のページ番号)を単なる `Dim i As Integer` という変数で管理しようとすることです。
' ❌ これでは不十分な理由
Dim pageCounter As Integer ' プログラムが終わると、この値は消えてしまう!
VBAの変数は、マクロの実行が終わった瞬間にメモリから消え去ります(これをライフサイクルの終了と呼びます)。次にマクロを動かしたときには、また `0` から始まってしまうのです。
プロの設計では、「ドキュメントそのものに状態を記憶させる」という手法を取ります。Visioには `CustomDocumentProperties` という、ファイル自体に独自の情報を書き込める仕組みが備わっています。これを使えば、ファイルを保存して明日開いたとしても、前回の続きの番号から再開できるのです。
2. 実践:自動採番ページ追加エンジン
それでは、実際に「独自の命名規則を守りつつ、重複を避けてページを追加する」コードを見ていきましょう。
このコードの肝は、「カウンターの取得 → インクリメント(加算) → 名前の生成 → 重複チェック」という一連のロジックです。
Option Explicit
''' <summary>
''' 指定した接頭辞に基づき、自動採番されたページを追加する。
''' Visioの自動接尾辞(1)を防止するためのバリデーションを含む。
''' </summary>
Sub AddSmartNumberedPage()
Dim vsoDoc As Visio.Document
Dim vsoPage As Visio.Page
Dim prefix As String
Dim nextNum As Integer
Dim newPageName As String
Dim propName As String
Set vsoDoc = ActiveDocument
prefix = "Project_Task_" ' ここに好きな接頭辞を設定
propName = "MyCustomPageCounter" ' ドキュメントに保存するプロパティ名
On Error GoTo ErrorHandler
' --- STEP 1: カウンターの取得 ---
' カスタムプロパティから現在の番号を取得。なければ1から開始。
nextNum = GetOrCreateCounter(vsoDoc, propName)
' --- STEP 2: 新しいページ名の生成 ---
' Format関数を使って、Task_001 のようなゼロ埋め形式にする
newPageName = prefix & Format(nextNum, "000")
' --- STEP 3: 重複バリデーション ---
' 万が一、手動操作で同じ名前が作られていた場合の安全策
If PageExists(vsoDoc, newPageName) Then
MsgBox "エラー: ページ名 '" & newPageName & "' が既に存在します。" & vbCrLf & _
"手動でページ名を変更するか、カウンターを調整してください。", vbCritical
Exit Sub
End If
' --- STEP 4: ページの追加と命名 ---
' ページを追加し、そのオブジェクトに対して名前をセットする
Set vsoPage = vsoDoc.AddNewPage
vsoPage.Name = newPageName
' --- STEP 5: カウンターの更新 ---
' 次回のために、カウントを1増やして保存する
UpdateCounter vsoDoc, propName, nextNum + 1
MsgBox "ページ '" & newPageName & "' を正常に追加しました。", vbInformation
Exit Sub
ErrorHandler:
MsgBox "予期せぬエラーが発生しました: " & Err.Description, vbExclamation
End Sub
''' <summary>
''' カスタムプロパティから現在の値を取得する。存在しない場合は初期化する。
''' </summary>
Private Function GetOrCreateCounter(doc As Visio.Document, propName As String) As Integer
Dim prop As Visio.CustomDocumentProperty
On Error Resume Next
Set prop = doc.CustomDocumentProperties.Item(propName)
On Error GoTo 0
If prop Is Nothing Then
' プロパティが存在しない場合は、新しく作成(初期値1)
doc.CustomDocumentProperties.Add propName, visDocumentPropTypeInteger, visDocDefault, 1
GetOrCreateCounter = 1
Else
GetOrCreateCounter = prop.Value
End If
End Function
''' <summary>
''' カスタムプロパティの値を更新する。
''' </summary>
Private Sub UpdateCounter(doc As Visio.Document, propName As String, newValue As Integer)
doc.CustomDocumentProperties.Item(propName).Value = newValue
End Sub
''' <summary>
''' 指定した名前のページが既に存在するかチェックする。
''' </summary>
Private Function PageExists(doc As Visio.Document, targetName As String) As Boolean
Dim vsoPage As Visio.Page
PageExists = False
For Each vsoPage In doc.Pages
If vsoPage.Name = targetName Then
PageExists = True
Exit Function
End If
Next vsoPage
End Function
3. プロの視点:コードの深層解説
このコードがなぜ「単なるマクロ」ではなく「エンジニアリング」なのか。そのポイントを3つに絞って解説します。
① ゼロ埋めによるソート性の確保
`Format(nextNum, “000”)` という記述に注目してください。
単に `Task_1` とすると、ファイルマネージャーやVisioのページリストで並べ替えた際、`Task_1` の次に `Task_10` が来てしまうことがあります(辞書順ソートの罠)。`001` とすることで、常に正しい数字順に並ぶよう、データの持ち方を制御しているのです。
② 防御的プログラミング(Defensive Programming)
`PageExists` 関数によるバリデーションです。
「プログラムが正しく動いていれば重複しないはずだ」と考えるのは素人の思考です。「ユーザーが手動でページ名を変えたかもしれない」「別のマクロが動いたかもしれない」という最悪のシナリオを想定して、事前にチェックを入れる。これが、業務システムを止めないための鉄則です。
③ エラーハンドリングの分離
`On Error GoTo ErrorHandler` を使うことで、エラーが発生した際に「何が起きたのか」をユーザーに優しく伝えつつ、プログラムを安全に終了させています。エラーを無視して無理やり進めるのは、自動化において最も危険な行為です。
まとめ:ここから先へ進むあなたへ
今回学んだ「状態の永続化(CustomDocumentProperties)」と「バリデーション(重複チェック)」は、Visioに限らず、あらゆるプログラミングにおける基礎中の基礎です。
1. 状態をどこに保存するか?(メモリか、ファイルか、データベースか)
2. 想定外の事態にどう備えるか?(バリデーションとエラーハンドリング)
この2点を意識するだけで、あなたの書くコードの品質は劇的に向上します。
「マクロの記録」で学んだ書き方をベースに、今回のような「ロジック」を肉付けしていく。その積み重ねが、あなたを伝説的なエンジニアへと導きます。
さあ、次はどのような自動化に挑戦しますか? 応援していますよ!
