Visio VBAの深淵へ:再帰関数で「入れ子構造の図形」を支配する技術
こんにちは。自動化の現場でVisioと格闘し続けている皆さんに、一つ、エンジニアとしての「越えるべき壁」についてお話しします。
Visioで「グループ化」を多用する図面を扱っていると、必ず突き当たる壁があります。それは、「グループの中にグループがあり、その中にまたグループがある……」という迷宮のような階層構造です。
マクロの記録で生成される `ActivePage.Shapes(“グループ.1”)` といった決め打ちのコードでは、この迷宮は攻略できません。今日は、Visioのオブジェクトモデルを自在に操るための、「再帰的走査(Recursive Traversal)」という極意を伝授します。
—
1. なぜ「再帰」が必要なのか?
Visioの図形(Shape)は、入れ子構造(親子関係)を持っています。
- 親(Group): `Shapes` コレクションの中に子を持つ。
- 子(Shape): さらにその中に `Shapes` を持つ場合がある。
これを処理するために、forループを何重にも書くのは愚策です。階層が深くなるたびにコードを書き直すことになりますからね。
「自分自身を呼び出す関数」を作ることで、Visioの図形構造の深さが何階層であっても、自動的に、そして漏れなくすべての図形を叩き起こすことができます。これが再帰の魔法です。
—
2. 再帰関数の設計図
まずは、どんな図形に対しても「名前を出力する」というシンプルな再帰処理を見てみましょう。
‘ メイン処理:選択中の図形から探索を開始する
Sub StartTraversal()
Dim shp As Visio.Shape
‘ 選択中の図形がない場合は終了
If ActiveWindow.Selection.Count = 0 Then Exit Sub
‘ 再帰関数を呼び出す
For Each shp In ActiveWindow.Selection
ProcessShape shp
Next shp
End Sub
‘ 再帰関数:図形を受け取り、子がいれば自分自身を呼ぶ
Sub ProcessShape(shp As Visio.Shape)
‘ — ここに「特定のパーツを操作する」ロジックを書く —
Debug.Print “名前: ” & shp.Name & ” / ID: ” & shp.ID
‘ 子図形があるかチェック (Groupの場合は 0 より大きくなる)
If shp.Shapes.Count > 0 Then
Dim subShp As Visio.Shape
‘ 子図形を一つずつ取り出し、再帰的に自分(ProcessShape)を呼び出す
For Each subShp In shp.Shapes
ProcessShape subShp
Next subSh
End If
End Sub
—
3. このコードが持つ「本質」と注意点
このコードのどこが凄いのか、そして初心者が陥りやすい罠を解説します。
① `Shapes` コレクションの扱い
`shp.Shapes` はグループ化された図形の中身を指します。グループ化されていない単体図形の場合、`Shapes.Count` は `0` になります。この「再帰の終了条件」が自然に成立しているのが、このアルゴリズムの美しいところです。
② 陥りやすいエラー:「図形の削除」には要注意
もし、走査中に図形を削除(Delete)しようとすると、`For Each` ループが崩壊し、エラーが発生します。
- 対策: 削除や構造変更を行う場合は、ループを逆順(`For i = shp.Shapes.Count To 1 Step -1`)で回すか、操作対象を一旦配列やコレクションに格納してから処理するようにしましょう。
③ パフォーマンスの「重み」を知る
Visioのオブジェクトモデルは、アクセスのたびにメモリを消費します。数千の図形を再帰処理する場合、`Debug.Print` や `Selection` の操作を繰り返すと処理速度が極端に落ちます。「必要な図形だけを特定し、最小限のアクセスで変更を加える」という意識が、プロフェッショナルへの第一歩です。
—
4. 実践:特定の条件(例:色を変える)を絞り込む
ただ走査するだけでは面白くありません。例えば、「階層の中にある特定の名前の図形だけを赤くする」といった実用的なロジックはこうなります。
Sub HighlightTargetShapes(shp As Visio.Shape)
‘ 特定の条件(例:名前に “Target” を含む)で操作
If InStr(shp.Name, “Target”) > 0 Then
shp.Cells(“FillForegnd”).ResultIU = RGB(255, 0, 0)
End If
‘ 再帰呼び出し
Dim subShp As Visio.Shape
For Each subShp In shp.Shapes
HighlightTargetShapes subShp
Next subShp
End Sub
—
先輩エンジニアからのメッセージ
Visio VBAは、ただの「自動記録」の延長ではありません。オブジェクトの親子関係を理解し、再帰というアルゴリズムを使いこなせば、数千個の図形が配置された複雑な設計図も、一瞬であなたの意のままになります。
最初は再帰の動きをイメージするのが難しいかもしれません。ですが、「目の前の箱を開けて、中にも箱があったら、またその箱を開ける」という単純な動作の繰り返しだと思えば、怖くはありませんよね。
ここをクリアできれば、あなたはもうVBAの初心者ではありません。迷宮のような図面データに立ち向かう準備は整いました。ぜひ、ご自身の業務で試してみてください!
何か行き詰まったら、いつでもまた聞きに来てくださいね。応援しています。
