【入門編】【上級者向け】ページ内の全シェイプを「グループ化」したまま、レイヤー属性を継承させる再帰処理 – Visio VBA解析バイブル

スポンサーリンク
スポンサーリンク

マクロの記録を卒業しよう。Visioの「階層構造」を支配する再帰処理の極意

こんにちは。エンジニアの世界へようこそ。
これまで「マクロの記録」機能を使って、Visioでの作業を自動化してきた人も多いでしょう。「ボタンを押せば、決まった図形が描画される」。それだけでも、十分な一歩です。

しかし、あなたがもし「複雑な構成図」や「大規模なシステム設計図」を扱いたいと願うなら、マクロの記録という「点」の操作から、オブジェクトの構造を捉える「線」の操作へとステップアップする必要があります。

今日のテーマは、Visio VBAにおける最も美しく、そして最も強力な武器の一つである「再帰処理(Recursion)」を用いた、レイヤー属性の継承です。

「グループ化したのに、中の図形だけレイヤー設定がバラバラで、表示・非表示の制御がうまくいかない……」

そんな現場の悲鳴を解決する、プロフェッショナルなロジックを一緒に学んでいきましょう。ここをクリアすれば、Visio VBAの基本はバッチリですよ。

1. なぜ「普通のループ」ではダメなのか?

Visioの図面は、単なる図形の集まりではありません。「階層構造(Hierarchy)」を持っています。

例えば、あなたが「部品A」というグループを作ったとします。その中には「ネジ」や「ボルト」といった子シェイプが含まれています。さらに、その「部品A」を別の「装置ユニット」というグループの中に入れたかもしれません。

このとき、以下のような状況を想像してください。

  • 親グループ(装置ユニット)に「レイヤー:電気系」を設定したい。
  • しかし、中の子シェイプ(ネジ)には、以前の設定で「レイヤー:機械系」が残っている。

ここで、単純に「ページ上の全ての図形に対してループを回す」という命令を出しても、グループ化された中身(子シェイプ)までは、標準的なループでは届かないことが多いのです。

「箱の中に箱があり、その中にまた箱がある」

このマトリョーシカのような構造を、一番奥の小さな部品まで辿り着いて操作するには、「自分自身を呼び出す関数」、すなわち再帰処理が必要になります。

2. 魂のコード:再帰によるレイヤー継承ロジック

それでは、実際に使えるコードを見てみましょう。
このコードの目的は、「選択したグループ内の全てのシェイプに対し、そのグループが持つレイヤー属性を強制的に適用する」ことです。

Option Explicit

''' <summary>
''' メインルーチン:選択されたシェイプに対して処理を開始する
''' </summary>
Sub ApplyLayerInheritanceToSelection()
    Dim sel As Visio.Selection
    Dim targetShape As Visio.Shape
    
    ' 選択範囲を取得
    Set sel = ActiveWindow.Selection
    
    ' 何も選択されていない場合は終了
    If sel.Count = 0 Then
        MsgBox "対象となるグループシェイプを選択してください。", vbExclamation
        Exit Sub
    End If
    
    ' 画面更新を停止してパフォーマンスを向上(プロの作法です)
    Application.ScreenUpdating = False
    
    On Error GoTo ErrorHandler
    
    ' 選択された各シェイプに対して処理を実行
    For Each targetShape In sel
        ' 選択されたものがグループの場合のみ処理を開始
        If targetShape.Type = visTypeGroup Then
            ' 最初のレイヤーを取得(ここではグループの第1レイヤーを継承対象とする例)
            ' ※実務では、特定のレイヤー名を指定するロジックに書き換えることも可能です
            If targetShape.LayerCount > 0 Then
                Dim masterLayer As Visio.Layer
                Set masterLayer = targetShape.Layer(1)
                
                ' 再帰処理の開始
                Call RecursiveLayerApply(targetShape, masterLayer)
            End If
        Else
            MsgBox "グループシェイプを選択してください。", vbInformation
        End If
    Next targetShape

    Application.ScreenUpdating = True
    MsgBox "レイヤーの継承処理が完了しました!", vbInformation
    Exit Sub

ErrorHandler:
    Application.ScreenUpdating = True
    MsgBox "エラーが発生しました: " & Err.Description, vbCritical
End Sub

''' <summary>
''' 再帰関数:シェイプの階層を深く潜り、レイヤーを適用し続ける
''' </summary>
''' <param name="shp">現在調査中のシェイプ</param>
''' <param name="targetLayer">適用したいマスターレイヤー</param>
Private Sub RecursiveLayerApply(shp As Visio.Shape, targetLayer As Visio.Layer)
    Dim childShape As Visio.Shape
    Dim i As Integer
    
    ' --- ステップ1: 現在のシェイプにレイヤーを追加 ---
    ' すでにそのレイヤーに含まれているか確認してから追加(二重登録エラー防止)
    If Not IsShapeInLayer(shp, targetLayer) Then
        shp.AddLayer targetLayer
    End If
    
    ' --- ステップ2: もしこのシェイプが「グループ」なら、中身を探索する ---
    If shp.Type = visTypeGroup Then
        ' グループ内の子シェイプを一つずつ走査
        For Each childShape In shp.Shapes
            ' 自分自身(RecursiveLayerApply)を再び呼び出す(これが再帰の核心!)
            Call RecursiveLayerApply(childShape, targetLayer)
        Next childShape
    End If
    
    ' ステップ2の終了:子シェイプへの探索が終わったら、一つ上の階層に戻る
End Sub

''' <summary>
''' 補助関数:シェイプが特定のレイヤーに所属しているか判定する
''' </summary>
Private Function IsShapeInLayer(shp As Visio.Shape, targetLayer As Visio.Layer) As Boolean
    Dim i As Integer
    IsShapeInLayer = False
    
    ' シェイプが持つレイヤーのインデックスをチェック
    For i = 1 To shp.LayerCount
        If shp.Layer(i).Name = targetLayer.Name Then
            IsShapeInLayer = True
            Exit Function
        End If
    Next i
End Function

3. コードの「心臓部」を徹底解剖

このコードがなぜ動くのか、エンジニアの視点で分解してみましょう。

① 再帰の仕組み(RecursiveLayerApply)

この関数の面白いところは、「自分自身の名前を、自分の中で呼んでいる」点です。

1. `RecursiveLayerApply` が呼ばれる。
2. そのシェイプにレイヤーを付ける。
3. 「お前はグループか?」と問いかける。
4. もしグループなら、その中にある子シェイプに対して、もう一度 `RecursiveLayerApply` を呼び出す。

これが繰り返されることで、どれだけ深い階層(グループの中のグループの中のグループ……)であっても、最後の一片の図形に辿り着くまで処理が止まりません。これを「深さ優先探索」と呼びます。

② 二重登録の回避(IsShapeInLayer)

Visio VBAにおいて、すでに所属しているレイヤーに対して `AddLayer` を実行しようとすると、エラーが発生してマクロが止まってしまいます。
実務では、「今の状態を確認してから、必要な操作だけを行う」というガード(条件分岐)を入れるのが、堅牢なコードを書くための鉄則です。

③ パフォーマンスへの配慮

`Application.ScreenUpdating = False`。
これは、処理中に画面の描画を更新しないようにする命令です。大量の図形を操作する場合、描画を待っていると処理が非常に遅くなります。これを一行入れるだけで、処理速度は劇的に変わります。

4. 陥りやすいエラーと対策

上級者への道には、いくつか落とし穴があります。

  • 無限ループの恐怖:

通常、Visioのシェイプ構造は「親 $\rightarrow$ 子」の一方向ですが、もし複雑なオブジェクトモデルを自作している場合、循環参照が起きるとプログラムが止まらなくなります。Visio標準のシェイプであれば基本的には安全ですが、常に「終了条件(グループでないなら止まる)」を意識しましょう。

  • 保護されたシェイプ:

図形に「保護(Protection)」がかかっている場合、レイヤーの変更ができずエラーになります。実務では `shp.Protection = visProtNone` を検討するか、エラーハンドリングを丁寧に記述する必要があります。

最後に:エンジニアとしての視点

「マクロの記録」は、いわば「答え」を書き写す作業です。
しかし、今回学んだ「再帰処理」は、「構造を理解し、ルールを定義する」作業です。

図形がどう並んでいるか、その親子関係はどうなっているか。
オブジェクトの構造を頭の中で3Dモデルのように捉えられるようになると、Visio VBAは単なる自動化ツールではなく、あなたの思考をダイレクトに形にする「魔法の杖」に変わります。

一歩ずつ、着実に。
このコードが、あなたの開発現場での強力な武器になることを願っています。応援していますよ!

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