【入門編】【上級者向け】ページ追加時に「自動採番」を行うためのカウンター管理と、重複ページ名の防止策 – Visio VBA解析バイブル

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

マクロ記録からの脱却: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点を意識するだけで、あなたの書くコードの品質は劇的に向上します。

「マクロの記録」で学んだ書き方をベースに、今回のような「ロジック」を肉付けしていく。その積み重ねが、あなたを伝説的なエンジニアへと導きます。

さあ、次はどのような自動化に挑戦しますか? 応援していますよ!

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