Visio VBAの深淵へ:LayerMembershipを支配し、図面を「意思」で操る
こんにちは。Visioの自動化という荒野を突き進む皆さん、ようこそ。
「マクロの記録」ボタンを押して生成された、冗長で意味の分からないコードに絶望したことはありませんか? Visioの真の力は、そこにはありません。Visioの真の力は、オブジェクトモデルを理解し、その背後にある「関係性」を操作することにあります。
今回は、複雑な図面を制御するための鍵、「レイヤー」の操作に焦点を当てます。特に、`Shape.LayerMembership`という、一見すると厄介なプロパティを飼いならす方法を伝授しましょう。
—
1. なぜ「レイヤー」なのか?
プラント図やネットワーク図では、数百から数千のシェイプが重なり合います。これらを手作業で選択するのは地獄です。
「特定のレイヤー(例:『配管ライン』や『未接続ノード』)に属する図形だけを一括で色変えしたい」「特定のエリアのシェイプだけを非表示にしたい」。これらを実現するのがレイヤー操作の醍醐味です。
ここで躓く人が多い!「LayerMembership」の正体
初心者が最も混乱するのは、`Shape.LayerMembership`が「配列(Array)」を返すという点です。
「レイヤー名は文字列なのに、なぜ配列なんだ?」と思うでしょう。理由は単純。1つのシェイプは、複数のレイヤーに同時に所属できるからです。
—
2. 実践:レイヤー情報を取得し、制御する
まずは、現在選択しているシェイプが「どのレイヤーに属しているのか」を判定し、特定のレイヤー名が含まれているかを確認するコードを見てみましょう。
Sub InspectShapeLayers()
Dim shp As Visio.Shape
Dim doc As Visio.Document
Dim lyr As Visio.Layer
Dim layerNames As Variant ‘ レイヤー情報の受け皿
Dim i As Long
‘ 選択中のシェイプを取得
Set shp = ActiveWindow.Selection(1)
‘ LayerMembershipは、そのシェイプが属するレイヤーの「インデックス番号」を配列で返す
layerNames = shp.LayerMembership
‘ インデックスから名前を特定する
If Not IsEmpty(layerNames) Then
For i = LBound(layerNames) To UBound(layerNames)
‘ レイヤーのインデックスからオブジェクトを参照
Set lyr = ActivePage.Layers.Item(layerNames(i))
Debug.Print “このシェイプは次のレイヤーに所属しています: ” & lyr.Name
Next i
Else
Debug.Print “どのレイヤーにも所属していません。”
End If
End Sub
このコードの要点
1. `IsEmpty`の確認: レイヤーに属していないシェイプに対して配列を操作しようとするとエラーになります。必ずチェックを入れましょう。
2. `LBound`と`UBound`: 配列の境界を動的に取得するのはVBAの鉄則です。
3. インデックスと名前の変換: `LayerMembership`が返すのは「順序番号」です。実際のレイヤー名を得るには、`ActivePage.Layers.Item(index)`というステップが必要です。
—
3. 条件付き制御:特定のレイヤーのシェイプを一括抽出
次は、ページ上の全シェイプを走査し、「『非表示』という名前のレイヤーに属しているものだけを赤くする」という、実戦的な手法を紹介します。
Sub ApplyConditionalFormattingByLayer()
Dim shp As Visio.Shape
Dim pg As Visio.Page
Dim lyr As Visio.Layer
Dim targetLayerName As String
targetLayerName = “非表示” ‘ 制御対象のレイヤー名
Set pg = ActivePage
‘ ページ内の全シェイプを走査
For Each shp In pg.Shapes
‘ 指定したレイヤー名が含まれるか判定
If IsShapeInLayer(shp, targetLayerName) Then
‘ 条件に合致したシェイプの塗りつぶしを赤に
shp.Cells(“FillForegnd”).Formula = “RGB(255,0,0)”
End If
Next shp
End Sub
‘ レイヤー判定を分離して再利用性を高める(これがエンジニアの作法です)
Function IsShapeInLayer(shp As Visio.Shape, layerName As String) As Boolean
Dim i As Integer
Dim layerIndices As Variant
layerIndices = shp.LayerMembership
If IsEmpty(layerIndices) Then Exit Function
For i = LBound(layerIndices) To UBound(layerIndices)
If shp.Document.Pages(1).Layers.Item(layerIndices(i)).Name = layerName Then
IsShapeInLayer = True
Exit Function
End If
Next i
End Function
—
4. 陥りやすい罠と解決策
- 罠1:ドキュメント構造の複雑さ
- レイヤーは`Page`オブジェクトに属します。`ActivePage`を明示的に指定しないと、予期せぬページを操作するリスクがあります。
- 罠2:パフォーマンスの低下
- `For Each`で全シェイプを回す際、シェイプ数が多いと処理が重くなります。`Selection`オブジェクトを使って、事前に必要なシェイプを絞り込んでから処理する癖をつけましょう。
- 罠3:レイヤー削除時のエラー
- レイヤーを削除した直後にインデックスを参照するとクラッシュします。レイヤーを扱う際は、常にその存在確認を行うのが「大人のコード」です。
—
最後に:自動化の先にあるもの
Visio VBAは、単に作業を楽にするための道具ではありません。「図面という静的なデータ」に「ロジックという命」を吹き込むための魔法です。
今回紹介した`LayerMembership`の操作は、ほんの入り口に過ぎません。しかし、この「オブジェクトの関係性を紐解く力」さえ身につければ、どんなに複雑な図面であっても、あなたの意のままに操ることができます。
まずはこのコードをコピーして、あなたの図面で動かしてみてください。エラーが出たら、それが成長の証です。分からないことがあれば、いつでもまた聞きに来てくださいね。
皆さんの開発環境が、よりスマートで創造的なものになりますように。応援しています!
