【入門編】Shape.Parentプロパティによる構造逆引き:グループ・コンテナ・ページへの階層追跡 – Visio VBA解析バイブル

スポンサーリンク

こんにちは!Visioの自動化の世界へようこそ。
マクロの記録ボタンを押して出てきたコードを眺めるだけの日々は、今日で終わりにしましょう。

今回は、Visio VBAにおいて避けて通れない、しかしマスターすれば「おっ、できるな!」と周囲を一目置かせることができる極上のテーマ「Shape.Parentプロパティによる構造逆引き:グループ・コンテナ・ページへの階層追跡」を解説します。

「今クリックしたこの小さな四角形は、どのグループの中にいるんだっけ?」「いや、もしかしてコンテナの中? それとも直接ページに置いてある?」
そんな迷子になりがちな階層構造を、VBAのコードでスマートに、かつ安全に遡る方法を優しく、そして本質的なところまで深く伝授します。

ここをクリアすれば、Visio VBAのオブジェクトモデルの基礎はもうバッチリですよ。一緒に扉を開けていきましょう!

1. なぜ「Parent(親)」を辿る必要があるのか?

Visioで複雑な図面を描くとき、私たちはシェイプを「グループ化」したり、便利な「コンテナ」機能に入れたりします。

VBAで自動化処理を書くとき、通常は「ページ(Page)から順番に中のシェイプを探していく(トップダウン)」というアプローチをとります。しかし、実務の現場ではその逆、つまり「選択した特定のシェイプから、それがどの親に属しているかを突き止めたい(ボトムアップ)」というニーズが圧倒的に多いのです。

例えば、こんな場面です。

  • 「選択されたパーツが特定のグループ内にある場合だけ、処理の色を変えたい」
  • 「コンテナの中にドロップされたシェイプの親コンテナのタイトルを取得したい」

ここで鍵を握るのが、すべてのシェイプが持っている `Parent` プロパティ です。

2. Parentプロパティの正体と、陥りやすい「罠」

Visioの `Shape` オブジェクトにある `Parent` プロパティは、その名の通り「親」を返します。しかし、ここがVisio VBAの奥深い(そして少し厄介な)ところなのですが、親の「種類」によって返ってくるオブジェクトの型が変わるのです。

  • シェイプがグループの「中」にある場合 ➔ 親は `Shape` オブジェクト(親グループ)
  • シェイプがページの「直下」にある場合 ➔ 親は `Page` オブジェクト
  • (コンテナの場合も、構造的にはグループやページに関連づけられています)

もし、この型を意識せずにコードを書くと、次のような恐ろしいエラー(実行時エラー)に直面します。

> 「実行時エラー ‘438’: オブジェクトは、このプロパティまたはメソッドをサポートしていません。」

初心者が一番ハマるのがこの罠です。「親を取ったんだから、また `Parent` を呼べばいいや」と安易にループを回すと、親が `Page` になった瞬間にメソッドやプロパティの食い違いでVBAが盛大にクラッシュします。

この安全ではない迷宮から抜け出すための武器が、VBAの `TypeOf … Is …` 演算子 です。

3. 実践!安全に階層を遡る再帰的ロジック

それでは、現在選択しているシェイプ(Selection)から出発し、親が一体何者なのかを判定しながら、ページに辿り着くまで階層を上に遡る実用的なコードを見てみましょう。

以下のコードをVBE(Visual Basic Editor)の標準モジュールに貼り付けて実行してみてください。

Sub TraceShapeParentHierarchy()
Dim vsoSelection As Visio.Selection
Dim vsoShape As Visio.Shape
Dim currentParent As Object
Dim depth As Integer

‘ 1. 現在選択されているものを取得
Set vsoSelection = ActiveWindow.Selection

If vsoSelection.Count = 0 {
MsgBox “シェイプが選択されていません。”, vbExclamation, “構造逆引き”
Exit Sub
}

‘ 最初の選択シェイプをターゲットにする
Set vsoShape = vsoSelection(1)

Debug.Print “=== 階層追跡開始: ” & vsoShape.Name & ” ===”

‘ 2. 最初の親を取得
Set currentParent = vsoShape.Parent
depth = 1

‘ 3. 親が「ページ(Page)」に到達するまでループを回す
Do While Not TypeOf currentParent Is Visio.Page

If TypeOf currentParent Is Visio.Shape Then
‘ 親がシェイプ(=グループまたは別の入れ子シェイプ)の場合
Debug.Print ” [階層 ” & depth & “] グループ/親シェイプ: ” & currentParent.Name

‘ さらにその上の親へバトンタッチ
Set currentParent = currentParent.Parent

Else
‘ 万が一想定外のオブジェクトだった場合の安全弁
Debug.Print ” [階層 ” & depth & “] その他の親オブジェクト”
Exit Do
End If

depth = depth + 1

‘ 無限ループ防止の安全装置(念のため)
If depth > 100 Then
Debug.Print “階層が深すぎます。ループを強制終了します。”
Exit Do
End If

Loop

‘ 4. 最終的に辿り着いたページの名前を出力
Debug.Print ” [ゴール] 所属ページ: ” & currentParent.Name
Debug.Print “=== 階層追跡終了 ===”

MsgBox “イミディエイトウインドウ(Ctrl + G)を確認してください。” & vbCrLf & _
“選択シェイプの所属階層を出力しました!”, vbInformation, “完了”

End Sub

コードのここがポイント!

1. `TypeOf currentParent Is Visio.Page` の判定
ループの条件にこれを使うことで、「親がページに到達したらループを抜ける」という明確なゴールを設定できます。
2. `Object` 型の変数受け渡し
`Parent` プロパティが返すオブジェクトの型は刻一刻と変わるため(`Shape` だったり `Page` だったり)、受け受ける変数をあえて汎用的な `Object` 型(または `As Variant`)にしておくのが、型ミスマッチを防ぐプロのテクニックです。
3. イミディエイトウインドウへの出力 (`Debug.Print`)
処理の結果を `MsgBox` だけでなく `Debug.Print` で出力することで、複雑な入れ子構造(グループの中にグループがある状態)でも、どの順番で階層を駆け上がったのかが一目瞭然になります。

4. チーフアーキテクトからのアドバイス:コンテナとグループの境界線

Visioの「コンテナ機能」は、見た目はグループに似ていますが、内部構造の持ち方が少し異なります。
コンテナ内のシェイプの `Parent` を辿ると、コンテナシェイプ自体に行き着くケースもあれば、直接ページに行き着くケースなど、Visioのバージョンや作成方法によって挙動が微かに違うことがあります。

だからこそ、今回紹介した `TypeOf` を使った型安全なチェック が不可欠なのです。「決め打ち」でコードを書くのではなく、「今、手元にある親は何者なのだろう?」とプログラム自身に判定させながら進む。この防衛的なプログラミングスタイルこそが、現場で絶対に壊れないマクロを作る秘訣です。

まとめ

今回は `Shape.Parent` プロパティを駆使して、シェイプの所属階層をボトムアップで逆引きする手法を解説しました。

  • 選択されたシェイプの親は、`Shape` の場合もあれば `Page` の場合もある。
  • そのままゴリ押しで `Parent` を呼ぶと型エラーの罠に落ちる。
  • `TypeOf … Is …` を使って、親の正体を確かめながらループで這い上がれ!

この構造逆引きテクニックが手に入れば、図面内の複雑なアセンブリ構造を解析する自動化ツールも自由自在に作れるようになります。ぜひ、ご自身のVisio環境で試してみてくださいね。

それでは、次回の極限の知見でお会いしましょう。あなたのVisioライフが快適な自動化で満たされますように!

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