こんにちは!いつも業務自動化や図面管理の設計、お疲れ様です。
VisioのVBAを触り始めると、誰もが一度は「マクロの記録」の限界にぶつかります。特に、図面の「表示・非表示」や「印刷対象の切り替え」を自動化したくなったとき、Visio特有の「レイヤー(Layer)構造」の壁が立ちはだかります。
IllustratorやCADなどを触ったことがある方ほど、Visioのレイヤー仕様に触れたときに「えっ、そうなっているの!?」と驚かれることが多いです。なぜなら、Visioでは「1つの図形(シェイプ)が、同時に複数のレイヤーに所属できる」という、極めてユニークで強力な構造を持っているからです。
この記事では、この「複数レイヤーへの重複所属」をVBAから自在にコントロールするための本質的な知識と、すぐに実務で使える堅牢なコードを優しく丁寧に解説します。
ここをクリアすれば、Visio VBAのオブジェクトモデルの理解は一気にプロレベルへ到達しますよ。一緒にマスターしていきましょう!
—
1. なぜVisioのレイヤーは「複数所属」できるのか?
一般的なグラフィックソフトでは、レイヤーは「階層構造(フォルダのようなもの)」であり、図形は必ずどこか1つのレイヤーにのみ存在します。
しかし、Visioは違います。Visioにおけるレイヤーは、図形に対する「タグ(属性情報)」に近い存在です。
【一般的なソフト(排他)】
レイヤーA ── [図形1]
レイヤーB ── [図形2]
【Visio(重複所属が可能)】
レイヤーA(電気系統) ──┐
├─ [モーター図形] (両方の性質を持つ)
レイヤーB(機械配置) ──┘
例えば、工場のレイアウト図をイメージしてください。
「モーター」という図形は、「電気系統レイヤー」に属して配線図の一部として見せたいと同時に、「機械配置レイヤー」にも属して物理的な位置を示したいはずです。
Visioなら、このモーター図形を両方のレイヤーに所属させることができます。そして、「電気系統だけを非表示にする」「機械配置だけを印刷する」といった制御が、図形をコピーすることなくスマートに実現できるのです。
—
2. VBAオブジェクトモデルでの位置づけ
この強力な機能をVBAで制御するために、まずは登場人物(オブジェクト)を整理しましょう。
| オブジェクト / プロパティ | 役割 |
| :— | :— |
| `Page.Layers` | ページ内に存在するすべてのレイヤーのコレクション。 |
| `Layer` | 個別のレイヤー。名前(`Name`)や、表示・非表示(`CellsC`プロパティなど)を制御します。 |
| `Shape.LayerCount` | そのシェイプが現在いくつのレイヤーに所属しているかを返す(読み取り専用)。 |
| `Shape.Layer(Index)` | そのシェイプが所属しているレイヤーをインデックス(1から開始)で取得する。 |
ここで重要なのは、「図形側から見ると、自分がどのレイヤーにいるかが分かり、レイヤー側から見ると、自分の中にどの図形がいるかが分かる」という双方向の関係性です。
—
3. レイヤーへの「追加」と「削除」の基本API
「シェイプをレイヤーに所属させるのだから、`Shape.Layer = …` のように代入するのかな?」と思いがちですが、ここは少しコツが必要です。
実は、所属のコントロールは「レイヤー(`Layer`)オブジェクトのメソッド」を使って行います。
レイヤーにシェイプを追加する:`Layer.Add`
‘ layerTarget というレイヤーに、shp というシェイプを所属させる
‘ 第2引数の「0」は、既存の所属レイヤーを維持(追加)することを意味します
layerTarget.Add shp, 0
※第2引数に `1`(または `True`)を渡すと、「他のレイヤーの所属をすべて解除して、このレイヤーだけに所属させる」という挙動(排他処理)になります。今回は重複所属させたいので `0` を指定します。
レイヤーからシェイプを抜く:`Layer.Remove`
‘ layerTarget から shp の所属を解除する
‘ 第2引数の「0」は、シェイプ自体は消さずに所属だけを解除することを意味します
layerTarget.Remove shp, 0
—
4. 【実践コード】単一シェイプを複数レイヤーに所属・制御する
それでは、実際に動くVBAコードを見てみましょう。
このコードは、選択したシェイプを「電気系統」と「機械配置」という2つのレイヤーに同時に所属させ、それぞれの所属状態をイミディエイトウィンドウに美しく出力するものです。
Sub ManageMultipleLayers()
Dim vsoApp As Visio.Application
Dim vsoPage As Visio.Page
Dim vsoSelect As Visio.Selection
Dim targetShape As Visio.Shape
Dim layerElectric As Visio.Layer
Dim layerMachinery As Visio.Layer
Dim i As Integer
‘ 1. アクティブなページと選択されたシェイプを取得
Set vsoApp = ActiveWindow.Application
Set vsoPage = vsoApp.ActivePage
Set vsoSelect = vsoApp.ActiveWindow.Selection
‘ 何も選択されていない場合は処理を抜ける(親切なエラー回避)
If vsoSelect.Count = 0 Then
MsgBox “シェイプを1つ選択してから実行してください。”, vbExclamation, “確認”
Exit Sub
End If
Set targetShape = vsoSelect(1)
‘ 2. 「電気系統」と「機械配置」レイヤーを取得、なければ新規作成
‘ (Page.Layers.Add は、同名レイヤーがすでにあればそれを返してくれます)
Set layerElectric = vsoPage.Layers.Add(“電気系統”)
Set layerMachinery = vsoPage.Layers.Add(“機械配置”)
‘ 3. 【核心】選択したシェイプを両方のレイヤーに所属させる
‘ 第2引数を「0」にすることで、既存の所属を保持したまま「追加」します
layerElectric.Add targetShape, 0
layerMachinery.Add targetShape, 0
‘ 4. 所属状況を確認する(LayerCount と Layer プロパティの活用)
Debug.Print “— シェイプのレイヤー所属状況 —”
Debug.Print “シェイプ名: ” & targetShape.Name
Debug.Print “所属しているレイヤー数: ” & targetShape.LayerCount
‘ 1 から LayerCount までループして、所属しているレイヤー名を特定
For i = 1 To targetShape.LayerCount
‘ Shape.Layer(i) で、i番目に所属しているLayerオブジェクトが取得できます
Debug.Print ” – 所属レイヤー(” & i & “): ” & targetShape.Layer(i).Name
Next i
Debug.Print “———————————-”
MsgBox “重複レイヤーの設定が完了しました!” & vbCrLf & _
“所属レイヤー数: ” & targetShape.LayerCount, vbInformation, “成功”
End Sub
コードの解説
- `vsoPage.Layers.Add(“レイヤー名”)`: 非常に便利なメソッドです。もし指定した名前のレイヤーがすでにページ内に存在していれば、新しく作らずにその既存レイヤーを返してくれます。
- `targetShape.LayerCount`: ループの最大値として使用しています。現在このシェイプがいくつのレイヤーに紐づいているかを動的に取得できるため、安全なループ処理が可能です。
—
5. 陥りやすい3つの落とし穴とプロの回避策
Visioのレイヤー操作で多くの開発者が頭を抱える「罠」とその対策を、あらかじめ共有しておきますね。
落とし穴①:`Shape.Layer(i)` のインデックスは「動的」に変化する
`LayerCount` を使ってループを回しながら、条件に合うレイヤーから `Layer.Remove` で所属を解除しようとすると、エラーになったり処理が飛ばされたりします。
- 原因: `Remove` した瞬間に `LayerCount` が減少し、内部のインデックス(何番目がどのレイヤーか)が前に詰められるためです(VBAのコレクション削除時によくある罠です)。
- 回避策: 削除処理を行う場合は、後ろのインデックスから逆順にループ(`For i = targetShape.LayerCount To 1 Step -1`)するか、一旦削除対象のレイヤーオブジェクトを配列などに退避させてから、一括で `Remove` を実行してください。
落とし穴②:グループシェイプと子シェイプの「レイヤー継承」
Visioで複数の図形をグループ化(Group)した場合、レイヤーの挙動が複雑になります。
- 問題: グループの「親」をレイヤーAに所属させても、中の「子」シェイプたちがそれぞれ異なるレイヤーに所属していると、表示・非表示の挙動が直感とズレることがあります。
- 解決の鍵: 基本的に、親グループのレイヤー設定は子シェイプに影響(オーバーライド)しますが、確実に制御したい場合は、子シェイプも含めて再帰的にレイヤーを設定する必要があります。実務では「グループ全体を一つのレイヤーで管理するのか、中身を個別に制御するのか」の設計を最初に決めましょう。
落とし穴③:非表示レイヤーの「選択」とパフォーマンス
大量のシェイプをループで処理してレイヤーに振り分ける際、画面の描画が追いつかずに処理が重くなることがあります。
- プロの対策: 処理の高速化のために、マクロの開始時に画面更新を止め、終了時に戻すおまじないを入れましょう。
‘ 開始時
vsoApp.ScreenUpdating = False
‘ (レイヤー処理)
‘ 終了時
vsoApp.ScreenUpdating = True
—
まとめ
お疲れ様でした!
Visioの「複数レイヤーへの重複所属」という概念と、それをVBAから自在に操る `Layer.Add`、`Shape.LayerCount`、`Shape.Layer(i)` の使い方はバッチリ掴めましたでしょうか?
CADのように厳密でありながら、データベースのタグのように柔軟なVisioのレイヤー構造。これをVBAで制御できるようになると、以下のような高度なシステムが作れるようになります。
1. 「基本レイアウト」に「空調設備」「配線」「セキュリティ」を重ね合わせ、チェックボックスで表示を切り替える図面
2. 特定のレイヤーに属するシェイプだけを自動集計して、Excelに見積書を出力するシステム
マクロの記録から一歩踏み出し、オブジェクトのライフサイクルと関係性を意識したコードが書けるようになれば、Visioの自動化開発は一気に面白くなります。
もしコードを試す中で分からないことが出てきたら、いつでも聞いてくださいね。あなたの業務自動化がより素晴らしいものになるよう、応援しています!
