こんにちは! Visioの裏側を自在に操り、日々の退屈な作業をスマートに自動化する――そんな世界へようこそ。
今回は、Visio VBAにおいて避けて通れない、しかし多くの人がハマりがちな「オブジェクトのコンテキスト(所属場所)」、特に`ContainingPage`と`ContainingMaster`という極めて重要なプロパティに焦点を当てます。
マクロの記録から一歩抜け出して、「現場で使える堅牢なマクロ」を書くための必須知識です。ここをクリアすれば、あなたの書くコードの安全性は一気にプロの領域に到達しますよ。それでは、一緒に紐解いていきましょう!
—
1. なぜ「どこにいるか」を意識する必要があるのか?
VisioでVBAを書いていると、図面上の図形(Shape)をゴニョゴニョいじるコードを書きますよね。
「よし、選択されている図形の色を変えよう」「すべての図形にテキストを追加しよう」……最初は順調に動くはずです。
しかし、ある日突然、こんなエラーに遭遇したことはありませんか?
> 「実行時エラー: オブジェクト変数または With ブロック変数が設定されていません。」
あるいは、マスターシェイプ(ステンシルに登録する大元の図形)を編集しているときに、図面全体を対象にするマクロをうっかり実行してしまい、マスターの中身がめちゃくちゃになって泣く泣く作り直した……なんて悲劇も、Visio開発者なら一度は通る道です。
Visioの「2つの世界」
Visioの図形(Shape)は、大きく分けて2つの異なる世界(コンテキスト)のどちらかに存在しています。
1. 実体ページ(Page世界):私たちが普段目にする、図面キャンバスのページ上に配置されている図形。
2. マスターシェイプ(Master世界):ステンシルや文書内マスターの「編集ウィンドウ」の中でひっそりと生きている、いわば“型(テンプレート)”としての図形。
この「今、操作しようとしている図形は、実際にページ上にいるのか? それともマスターの内部にいるのか?」を判定するために使うのが、`ContainingPage` と `ContainingMaster` プロパティです。
—
2. `ContainingPage` と `ContainingMaster` の正体
それぞれのプロパティの動きはとてもシンプルです。
- `Shape.ContainingPage`
- その図形がページ上に存在する場合、所属している `Page` オブジェクトを返します。
- マスターシェイプ内にいる場合は `Nothing`(空っぽ)を返します。
- `Shape.ContainingMaster`
- その図形がマスターシェイプの編集ウィンドウ内に存在する場合、所属している `Master` オブジェクトを返します。
- 通常のページ上にいる場合は `Nothing` を返します。
つまり、これら二つのプロパティのどちらが有効(`Nothing` ではない)かを調べることで、「今、自分が掴んでいる図形がどちらの世界にいるのか」を完璧に見分けることができるのです。
—
3. 【実践】マスター誤爆を防ぐ安全設計コード
百聞は一見に如かず。実際に、この仕組みを使った「安全設計のテンプレートコード」を見てみましょう。
このコードは、現在選択している図形(Selection)を1つずつチェックし、「もしマスターシェイプの編集中であれば処理をスキップ(または警告)し、通常ページ上であれば安全に処理を実行する」というものです。
Sub SafeProcessSelectedShapes()
Dim shp As Visio.Shape
Dim targetPage As Visio.Page
Dim targetMaster As Visio.Master
‘ 1. 図形が選択されているかチェック
If ActiveWindow.Selection.Count = 0 Then
MsgBox “処理対象の図形が選択されていません。”, vbExclamation, “処理中断”
Exit Sub
End If
‘ 2. 選択された図形をひとつずつループ処理
For Each shp In ActiveWindow.Selection
‘ — 【重要】所属コンテキストの判定 —
Set targetPage = shp.ContainingPage
Set targetMaster = shp.ContainingMaster
If Not targetMaster Is Nothing Then
‘ ==========================================
‘ Aの世界:マスターシェイプの編集画面にいる場合
‘ ==========================================
Debug.Print “スキップ: 「” & shp.Name & “」はマスター編集中のため処理しません。”
‘ ※マスター内の図形固有の処理をしたい場合はここに書く
ElseIf Not targetPage Is Nothing Then
‘ ==========================================
‘ Bの世界:通常の実体ページ上にいる場合
‘ ==========================================
Debug.Print “実行: 「” & shp.Name & “」はページ「” & targetPage.Name & “」上に存在します。”
‘ — ここに本来行いたい安全な処理を書く —
‘ 例:図形の塗りつぶしを青色に変更する
shp.Cells(“FillForegnd”).FormulaU = “RGB(0, 120, 215)”
Else
‘ 滅多にないが、宙ぶらりんの状態(グループの途中など)
Debug.Print “警告: 「” & shp.Name & “」のコンテキストが特定できません。”
End If
Next shp
MsgBox “選択図形の処理が完了しました!”, vbInformation, “完了”
End Sub
コードのポイント解説
- `If Not targetMaster Is Nothing Then`
「もしマスターが空っぽ(`Nothing`)じゃなかったら」=つまり「マスターの編集画面を開いている状態」と判定します。
- 二重の安全網
Visioでは、マスターシェイプを開いている最中も `ActiveWindow.Selection` にその図形が含まれることがあります。この判定を挟むことで、「うっかりマスターの型自体を書き換えてしまい、全図形が崩壊した!」という最悪の事故を未然に防ぐことができます。
—
4. 応用編:コンテキストに応じた動的処理の切り替え
実務では、「ページ上にあるときは座標をいじりたいけれど、マスター内にあるときは別のプロパティを調整したい」といったように、場所によって処理を動的に切り替えたい場面が多々あります。
以下のコードは、その典型的なパターンです。
Sub DynamicProcessByContext(shp As Visio.Shape)
‘ ページにいるか、マスターにいるかで処理を分岐
If Not shp.ContainingMaster Is Nothing Then
‘ 【マスター内】マスターとしての定義を調整する処理
MsgBox “この図形はマスター(” & shp.ContainingMaster.Name & “)の内部にあります。”
‘ 例: マスター図形特有のシェイプシート操作など
ElseIf Not shp.ContainingPage Is Nothing Then
‘ 【通常ページ】配置されたインスタンスとしての処理
MsgBox “この図形はページ(” & shp.ContainingPage.Name & “)上に配置されています。”
‘ 例: ページのスケールに合わせた位置調整など
End If
End Sub
このように、渡された `Shape` が自ら「私は今ここにいます」と教えてくれるプロパティを持っているおかげで、関数やサブルーチンを共通化しつつ、安全に挙動を変えることができるのです。
—
5. ここをクリアすれば、Visio VBAは怖くない!
今回は `ContainingPage` と `ContainingMaster` を使って、図形の配置先コンテキストを正確に識別し、マスター編集時の誤動作を防ぐ安全設計について解説しました。
- マクロが予期せぬエラーで止まる原因の多くは、オブジェクトの「居場所」を無視していること。
- `ContainingPage` と `ContainingMaster` を使えば、図形がどこにいるかを完全にコントロールできる。
ここを理解できたあなたは、もう単なる「マクロの記録の延長」ではありません。実務で耐えうる堅牢なシステムを構築できる、立派なVisio VBAエンジニアです。
ぜひ、日々の業務自動化ツールにこの「安全設計」を取り入れてみてくださいね。それでは、次のステップでお会いしましょう!
