【入門編】【実務直結】Visio VBAにおける「ページ削除」時のオブジェクト参照エラーを回避する安全なループ処理 – Visio VBA解析バイブル

スポンサーリンク

こんにちは!Visio VBAの自動化の世界へようこそ。
マクロの記録ボタンを押すだけのステージから抜け出し、自分でコードをコントロールできるようになると、Visioでの図面作成作業は劇的にスピードアップします。

今回は、実務の現場で必ずといっていいほど直面する「ページの削除」に潜む恐ろしい罠と、それをスマートに回避する「究極の安全ループ術」について解説します。

ここをクリアすれば、Visio VBAのオブジェクト管理の基礎はバッチリです!一緒にマスターしていきましょう。

—

1. なぜ、普通のループでページを消すと怒られるのか?

例えば、「図面ファイルの中にある不要なページを、すべて一網打尽に削除したい!」と思ったとします。
プログラミングを少し知っている方なら、次のようなコードを書きたくなるはずです。

❌ やってはいけない「順方向ループ」の例

‘ 【注意】このコードはエラーを引き起こします!
Sub DeleteAllPages_Bad()
Dim i As Long
Dim totalPages As Long

totalPages = ActiveDocument.Pages.Count

‘ 1ページ目から最後のページに向かって削除する
For i = 1 To totalPages
ActiveDocument.Pages(i).Delete
Next i
End Sub

一見、何の問題もなさそうに見えますよね?「1ページ目を消して、2ページ目を消して……」と順番に処理しているだけですから。

しかし、これを実行すると、途中で「インデックスが範囲を超えています」という無慈悲な実行時エラー(エラー番号:-2147352567 など)が発生してマクロが強制終了します。なぜでしょうか?

頭の中で「図面のズレ」をシミュレーションしてみよう

ページが3枚(Page1, Page2, Page3)ある状態を想像してください。

1. `i = 1` のとき: 1ページ目(Page1)を削除します。

  • 残ったページ: Page2, Page3 (合計2ページ)
  • この瞬間、Visioの内部でページの番号(インデックス)が繰り上がります。もともと「2ページ目」だったPage2が、新しい「1ページ目」にスライドします。

2. `i = 2` のとき: ループのカウンターが `2` に進みます。

  • プログラムは「2ページ目を消そう」としますが、現在残っているのは実質2ページ(新1ページ、新2ページ)です。
  • あれ?さっきPage3だったものが「2ページ目」になっています。結果として、スキップされてしまったページが出たり、存在しないインデックスを指定してしまい、盛大にエラーが爆発するのです。

これが、コレクション(オブジェクトの集合体)を前から順番に削除するときに起きる「インデックス・ズレの呪い」です。Visio VBAに限らず、Excel VBAやあらゆるプログラミング言語の鬼門となっています。

—

2. 解決の切り札!「逆順ループ」というアプローチ

この呪いを綺麗にかわすためのセオリーが、「後ろから数えて、後ろから消す(逆順ループ)」という手法です。

コードの書き方はこう変わります。

⭕ 正しい「逆順ループ」の例

Sub DeleteAllPages_Safe()
Dim i As Long
Dim totalPages As Long

‘ 現在の総ページ数を取得
totalPages = ActiveDocument.Pages.Count

‘ 【重要】最後のページから 1ページ目 に向かって、デクリメント(-1ずつ)しながらループする
For i = totalPages To 1 Step -1
‘ 後ろから消していけば、前方のページのインデックス番号は一切変わらない!
ActiveDocument.Pages(i).Delete
Next i

MsgBox “すべての不要ページを安全に削除しました!”, vbInformation
End Sub

なぜ逆順だと安全なのか?

先ほどと同じく、3枚のページ(Page1, Page2, Page3)がある状態を考えてみましょう。

1. `i = 3` のとき: 最後のページ(Page3)を削除します。

  • 残ったページ: Page1, Page2 (合計2ページ)
  • ポイント: 消されたのは一番後ろのページなので、前にある Page1 と Page2 のインデックス番号(1と2)は微動だにしません!

2. `i = 2` のとき: 2ページ目(Page2)を削除します。

  • 残ったページ: Page1 (合計1ページ)
  • やはり前方の Page1 のインデックスは「1」のまま変わりません。

3. `i = 1` のとき: 最後に1ページ目(Page1)を削除して完了です。

このように、「削除によって影響を受けるのは、自分より後ろのインデックスだけ」というコレクションの性質を利用し、影響を受けない後ろから順番に処理していくのが、実務で使えるプロフェッショナルの技術です。

—

3. 【実務直結】条件付きで特定名以外のページを掃除するマクロ

実務では「すべてのページを消す」のではなく、「”Cover” や “Template” という名前のシート以外をすべて一括削除したい」というケースが多いはずです。

先ほどの逆順ループの知見をベースに、実務でそのままコピー&ペーストして使える「特定ページ以外の一括削除マクロ」をプレゼントしましょう。

Sub DeleteUnnecessaryPages()
Dim i As Long
Dim totalPages As Long
Dim targetPage As Visio.Page
Dim excludeName As String

‘ 例外として残したいページの名称(必要に応じて変更してください)
excludeName = “表紙”
財布
‘ 念のため画面描画を停止してパフォーマンスを爆上げする
Application.ScreenUpdating = False

On Error GoTo ErrorHandler

totalPages = ActiveDocument.Pages.Count

‘ 最後のページから逆順にチェックしていく
For i = totalPages To 1 Step -1
Set targetPage = ActiveDocument.Pages(i)

‘ 除外名と一致しない場合のみ削除を実行
‘ ※Visioのページ名には Name ではなく NameU(ユニバーサル名)や Name を使います
If targetPage.Name <> excludeName Then
targetPage.Delete
End If
Next i

ErrorHandler:
‘ 処理が終わったら画面描画を必ず元に戻す
Application.ScreenUpdating = True

If Err.Number <> 0 Then
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical
Else
MsgBox “クリーンアップが完了しました!”, vbInformation
End If
End Sub

このコードのプロ技ポイント

  • `Application.ScreenUpdating = False`: ページが1枚消えるたびにVisioの画面がパカパカ再描画されるのを防ぎ、処理速度を何倍にも跳ね上げます。
  • エラーハンドリング(`On Error GoTo`): 万が一予期せぬエラーでマクロが止まっても、画面描画のロックが解除されたままフリーズするのを防ぐ、実務では必須の防衛策です。

—

まとめ

今回は、Visio VBAにおけるページ削除の罠と、それを華麗に回避する「逆順ループ」の極意を解説しました。

  • コレクションを前から消すと、インデックスがズレてクラッシュする!
  • 迷ったら「後ろから数えて、後ろから消す(`Step -1`)」を思い出せ!
  • 画面描画の停止(`ScreenUpdating`)とセットで使えばプロの仕上がりに!

この原則さえ押さえておけば、ページ数やレイヤー数がダイナミックに変わる巨大な図面自動化ツールを作るときも、怖気づくことはもうありません。

日々の面倒な手作業をコードで自動化し、クリエイティブな仕事に時間を使い倒していきましょう。あなたのVisioライフがより快適になることを応援しています!

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