こんにちは、現場の最前線で自動化の限界に挑み続けるチーフアーキテクトです。
Visio VBAの世界へようこそ。マクロの記録を卒業し、「自分のコードで図形を自在に操りたい」と考え始めたあなた、素晴らしい一歩を踏み出しましたね。
Visioは他のOffice製品(ExcelやWord)に比べて、オブジェクトの階層構造が非常にユニークです。特に「Shape(図形)」は、単にページの上に置かれているだけではありません。グループ化され、コンテナに入れられ、複雑な入れ子構造(ネスト)を形成します。
「このシェイプの親は誰? ページなの? それとも別のグループ図形?」
この問いにスマートに答えることができれば、あなたはVisio VBAの中級者への扉を開いたと言っても過言ではありません。今回は、どんなに深い階層に隠れたシェイプからでも、確実に「ページ」や「ドキュメント」まで辿り着くための、再帰的な階層追跡クラスの実装を伝授します。
—
1. Visioの「親子関係」はマトリョーシカである
まず、Visioのオブジェクトモデルを整理しましょう。基本は以下の4段階です。
1. Application: Visioそのもの
2. Document: 図面ファイル(.vsdx)
3. Page: 図面内の各ページ
4. Shape: 図形、線、テキストボックス、そしてグループ
ここで初心者が最も躓くのが、「Shapeの親はPageだとは限らない」という事実です。
グループ化された図形の場合、その親(Parent)は「Page」ではなく、自分を包んでいる「親のShape」になります。さらに、Visio特有の「コンテナ」機能を使っている場合、論理的な所属関係はより複雑に見えます。
このマトリョーシカのような構造を、迷子にならずに遡る技術が必要です。
—
2. なぜ `Shape.Parent` は扱いづらいのか?
コードを書くとき、`ActiveWindow.Selection(1).Parent` と書くと、何が返ってくるでしょうか?
実は、VBAの定義上、`Parent` プロパティは Object型 を返します。
- ページ直下のシェイプなら `Visio.Page` が返る。
- グループ内のシェイプなら `Visio.Shape` が返る。
相手によって型が変わるため、安易に `Set myPage = myShape.Parent` と書くと、グループ化された図形を触った瞬間に「型が一致しません」というエラーでプログラムが止まってしまいます。
—
3. 【実践】階層を遡る「共通クラス」を実装する
それでは、どんな階層からでも安全に情報を抜き出すための共通クラスを作成しましょう。このクラスをプロジェクトに入れておけば、これからの開発が劇的に楽になります。
クラス名: `ShapeNavigator`
VBAの「クラスモジュール」を挿入し、名前を `ShapeNavigator` にして以下のコードを貼り付けてください。
‘——————————————————————-
‘ クラス名: ShapeNavigator
‘ 概要: シェイプの階層構造を解析し、上位オブジェクトを特定する
‘——————————————————————-
Option Explicit
‘ 指定したシェイプが所属する「Page」オブジェクトを確実に取得する
Public Function GetParentPage(ByVal targetShape As Visio.Shape) As Visio.Page
Dim parentObj As Object
Set parentObj = targetShape.Parent
‘ 親がPage型になるまで再帰的に遡る
If TypeOf parentObj Is Visio.Page Then
Set GetParentPage = parentObj
ElseIf TypeOf parentObj Is Visio.Shape Then
‘ 親がShape(グループ図形)なら、さらにその親を探しに行く
Set GetParentPage = GetParentPage(parentObj)
Else
Set GetParentPage = Nothing
End If
End Function
‘ 指定したシェイプが所属する「Document」を確実に取得する
Public Function GetParentDocument(ByVal targetShape As Visio.Shape) As Visio.Document
Dim pg As Visio.Page
Set pg = GetParentPage(targetShape)
If Not pg Is Nothing Then
Set GetParentDocument = pg.Document
Else
Set GetParentDocument = Nothing
End If
End Function
‘ 最上位のグループ(ページ直下の図形)を特定する
‘ 深い階層の図形を操作していても、その「親分」を捕まえるための関数
Public Function GetTopLevelShape(ByVal targetShape As Visio.Shape) As Visio.Shape
Dim parentObj As Object
Set parentObj = targetShape.Parent
‘ 親がPageなら、自分自身が最上位のShapeである
If TypeOf parentObj Is Visio.Page Then
Set GetTopLevelShape = targetShape
ElseIf TypeOf parentObj Is Visio.Shape Then
‘ 親がShapeなら、まだ上にいける
Set GetTopLevelShape = GetTopLevelShape(parentObj)
Else
Set GetTopLevelShape = targetShape
End If
End Function
—
4. コードの解説:ここが「プロ」の視点
再帰(Recursion)の魔術
`GetParentPage` 関数の中で、自分自身を呼び出している(`Set GetParentPage = GetParentPage(parentObj)`)部分に注目してください。
これは再帰と呼ばれる手法です。「親がページじゃない? なら、親の親を調べてよ」という処理を、ページに辿り着くまで無限に繰り返します。これにより、3階層、4階層と深くネストされたグループ図形でも、確実にページを特定できます。
`TypeOf … Is` による安全な判定
`TypeName(parentObj) = “Page”` といった文字列比較ではなく、`TypeOf` 演算子を使っています。これは実行速度が速く、かつ実行時のスペルミスによるバグを防ぐための鉄則です。
—
5. このクラスをどう使うか?(実行用コード)
標準モジュールに以下のテスト用コードを書いて、適当な図形(特にグループ化した図形の中の小さなパーツ)を選択して実行してみてください。
Sub WhoIsMyParent()
Dim nav As New ShapeNavigator
Dim sel As Visio.Selection
Set sel = ActiveWindow.Selection
If sel.Count = 0 Then
MsgBox “図形を選択してください。”
Exit Sub
End If
Dim target As Visio.Shape
Set target = sel(1)
‘ ナビゲーターを使って親を特定
Dim parentPage As Visio.Page
Dim topShape As Visio.Shape
Set parentPage = nav.GetParentPage(target)
Set topShape = nav.GetTopLevelShape(target)
‘ 結果表示
Dim msg As String
msg = “選択した図形: ” & target.Name & vbCrLf
msg = msg & “所属ページ: ” & parentPage.Name & vbCrLf
msg = msg & “最上位グループ: ” & topShape.Name
MsgBox msg, vbInformation, “階層解析結果”
End Sub
—
6. 陥りやすい罠:コンテナ(Container)の存在
Visio 2010以降には「コンテナ」という機能があります。
ここで注意が必要なのは、「コンテナの中に図形が入っている」のと「グループ化されている」のは、VBA構造上は別物だということです。
- グループ: 物理的な親子関係(Parentが親Shapeになる)。
- コンテナ: 論理的な関係(ParentはPageのまま、`MemberOfContainers` というプロパティで繋がりを持つ)。
今回紹介した `Parent` を辿る手法は、Visioの描画構造の根幹である「グループ化」によるネストを確実に攻略するためのものです。もしコンテナの所属を知りたい場合は、`target.MemberOfContainers` を調べる別のロジックが必要になりますが、まずはこの `Parent` の階層移動をマスターすることが、Visio VBA制覇の第一歩です。
—
最後に:ここをクリアすれば、Visio VBAの基本はバッチリですよ
Visioの図形は、単なる「絵」ではなく、データの階層構造そのものです。
今回実装した `ShapeNavigator` のように、「オブジェクトの繋がりを再帰的に解き明かす」という考え方ができるようになれば、あなたはもうマクロの記録に頼る必要はありません。
複雑な図面から特定の情報を抜き出し、自動でレポートを作成する。そんな魔法のようなツールも、この「階層の理解」の先にあります。
一歩ずつ、楽しみながらコードを書いていきましょう。応援しています!
