こんにちは!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ライフがより快適になることを応援しています!
