【実務・中級編】【初心者向け】ActivePage.CustomNameを使ったページカスタム名の取得と動的変更の基本パターン – CorelDRAW VBA解析バイブル

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

【CorelDRAW VBA 掌握術】インデックスによる脆弱な設計を脱却せよ:`CustomName` を用いた堅牢なページ制御の極意

業務自動化エンジニアとしてプロジェクトを率いる際、私が最も嫌うものがあります。それは「動的な環境変化に耐えられない、脆(もろ)いコード」です。

CorelDRAWの自動化ツールを開発する初心者が、真っ先に陥る罠。それが「ページのインデックス番号(`Pages(1)` など)によるページ指定」です。

もし君が、顧客の現場で稼働するツールを「インデックス番号」で制御しようとしているなら、今すぐその設計を見直すべきだ。ユーザーがページを入れ替えた瞬間、君のツールは全く別のページに対して破壊的な操作を行うことになる。

本記事では、プロフェッショナルが実践する、`CustomName` を利用した「決定論的(Deterministic)」なページ制御の設計思想とその実装方法を伝授する。

1. なぜ `Pages(i)` は「設計ミス」と呼ばれるのか

多くの開発者は、`Pages(1)` や `Pages(2)` といった記述でページを特定しようとする。一見、シンプルで直感的だ。しかし、これは「物理的な位置」に依存した指定であり、「論理的な役割」を無視している。

インデックス指定の致命的なリスク

1. ユーザー操作による不整合: ユーザーがドラッグ&ドロップでページの順番を入れ替えた瞬間、コードが対象とするページがズレる。
2. ページ追加・削除による崩壊: 途中にページが挿入されたり、削除されたりすると、それ以降のインデックスはすべて無効化される。
3. 自動化の「非決定性」: 同じドキュメントであっても、ページの並び順が違うだけで、プログラムの挙動が変わってしまう。これはバグの温床であり、デバッグを困難にする。

プロの設計では、ページを「何番目にあるか」ではなく、「それが何の役割を持つページか」で識別しなければならない。そのための鍵となるプロパティこそが `CustomName` である。

2. `CustomName` による「論理ID」の確立

CorelDRAWにおける `CustomName` は、単なる「ページ名」ではない。それは、そのページに割り当てられた「ユニークな識別子(Primary Key)」として扱うべきものだ。

例えば、「表紙」「仕様書」「図面A」といった名前を `CustomName` に設定しておけば、ページがドキュメント内のどこへ移動しようとも、プログラムは確実にそのページを捕捉できる。

取得と変更の基本パターン

まずは、基本となる操作を理解しよう。

' ページ名を安全に取得する基本構造
Sub GetPageNameExample()
    Dim pg As Page
    Set pg = ActivePage
    
    ' CustomNameを取得
    Debug.Print "現在のページの論理ID: " & pg.CustomName
End Sub

' ページ名を動的に変更する基本構造
Sub RenamePageExample()
    Dim pg As Page
    Set pg = ActivePage
    
    ' ページ名を新しい論理IDに更新
    pg.CustomName = "Target_Page_01"
End Sub

3. 【実践】プロダクションレベルの堅牢な実装例

実務で使えるコードを書くには、単にプロパティを操作するだけでは不十分だ。「指定した名前のページが存在しない場合」や「同名のページが既に存在する場合」といった、例外系への考慮が不可欠である。

以下に、私が設計のベースとして推奨する「ページ管理モジュール」のプロトタイプを示す。これをコピーして、君のプロジェクトの基盤として活用してほしい。

Option Explicit

' =================================================================
' Module: PageManager
' Description: 堅牢なページ識別と操作を行うためのユーティリティ
' =================================================================

''' <summary>
''' カスタム名(論理ID)を指定して、対象のPageオブジェクトを返す
''' </summary>
''' <param name="targetName">検索するカスタム名</param>
''' <returns>見つかったPageオブジェクト。見つからない場合はNothing</returns>
Function FindPageByCustomName(ByVal targetName As String) As Page
    Dim pg As Page
    
    ' 全ページを走査して、カスタム名が一致するものを探す
    ' インデックスではなく、名前という「属性」で検索する
    For Each pg In ActiveDocument.Pages
        If pg.CustomName = targetName Then
            Set FindPageByCustomName = pg
            Exit Function
        End If
    Next pg
    
    ' 見つからなかった場合はNothingを返す(呼び出し側でエラーハンドリングが必要)
    Set FindPageByCustomName = Nothing
End Function

''' <summary>
''' 指定したカスタム名を持つページが存在するかチェックし、なければ作成する
''' </summary>
''' <param name="requiredName">作成または確認したい名前</param>
Sub EnsurePageExists(ByVal requiredName As String)
    Dim pg As Page
    Set pg = FindPageByCustomName(requiredName)
    
    If pg Is Nothing Then
        ' 存在しない場合は新規作成(末尾に追加)
        Set pg = ActiveDocument.AddPages(1)
        pg.CustomName = requiredName
        Debug.Print "New page created: " & requiredName
    Else
        Debug.Print "Page already exists: " & requiredName
    End If
End Sub

''' <summary>
''' ページ名を安全に変更する(重複チェック付き)
''' </summary>
''' <param name="targetName">現在の名前</param>
''' <param name="newName">新しい名前</param>
Sub SafeRenamePage(ByVal targetName As String, ByVal newName As String)
    Dim pg As Page
    Dim checkPg As Page
    
    Set pg = FindPageByCustomName(targetName)
    
    ' 1. 元のページが存在するか?
    If pg Is Nothing Then
        Err.Raise vbObjectError + 513, "PageManager", "指定された元のページ名が見つかりません。"
        Exit Sub
    End If
    
    ' 2. 新しい名前が既に他のページで使用されていないか?
    Set checkPg = FindPageByCustomName(newName)
    If Not checkPg Is Nothing Then
        Err.Raise vbObjectError + 514, "PageManager", "新しい名前 '" & newName & "' は既に使用されています。"
        Exit Sub
    End If
    
    ' 3. リネーム実行
    pg.CustomName = newName
End Sub

' --- テスト用メインルーチン ---
Sub TestPageManager()
    On Error GoTo ErrorHandler
    
    ' 1. 必要なページを準備(存在しなければ作る)
    EnsurePageExists "Sheet_A"
    EnsurePageExists "Sheet_B"
    
    ' 2. ページを操作
    Dim targetPg As Page
    Set targetPg = FindPageByCustomName("Sheet_A")
    
    If Not targetPg Is Nothing Then
        Debug.Print "Target page found: " & targetPg.Name ' 表示名はPage1等
        Debug.Print "Target CustomName: " & targetPg.CustomName
    End If
    
    ' 3. リネームのテスト(成功パターン)
    SafeRenamePage "Sheet_B", "Sheet_C"
    Debug.Print "Rename successful."
    
    ' 4. リネームのテスト(失敗パターン:重複)
    ' SafeRenamePage "Sheet_C", "Sheet_A" ' ここでエラーを投げる設計
    
    Exit Sub

ErrorHandler:
    MsgBox "Error: " & Err.Description, vbCritical, "Runtime Error"
End Sub

4. データベース・外部ファイル連携時の「鉄則」

君がもし、ExcelやSQLデータベースから「どのページにどのデータを流し込むか」という指示書を作成しているなら、注意すべき点が一つある。

「カスタム名は、外部データの主キー(Primary Key)である」

ExcelのA列に `Page_ID` という列を作り、そこに `Sheet_A` などのカスタム名を記載する場合、VBA側でその名前を変更してしまうと、Excelとの紐付けが完全に断絶する。

設計上のアドバイス:

  • 不変性の維持: プログラムが一度設定した `CustomName` は、原則としてそのドキュメントの寿命が尽きるまで変更しない設計にせよ。
  • 命名規則の固定: `Sheet_01`, `Sheet_02` のように、記号やスペースを避けた、プログラム的に扱いやすい命名規則を定義せよ。
  • 同期の保証: もしプログラムの仕様上、ページ名を変更する必要があるなら、必ず同時に外部データ(Excel等)側の識別子も更新するトランザクション的な処理を実装せよ。

結論:エンジニアとしての矜持

`Pages(i)` を使うのは簡単だ。しかし、それは「動けばいい」というレベルのコードに過ぎない。

真に価値のある業務自動化ツールとは、ユーザーがどんなにドキュメントを弄り回そうとも、寸分狂わず、意図した通りのページに対して、意図した通りの処理を完遂するものだ。

`CustomName` を使いこなし、「位置」ではなく「役割」でオブジェクトを捉える。 この設計思想こそが、初級者を脱し、伝説的なアーキテクトへと至る第一歩である。

Good luck, Developer.

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