こんにちは!Visio VBAの世界へようこそ。
日々の業務で「大量の図形から、特定のカテゴリ(例えば『配線』や『注釈』)だけを一時的に非表示にしたい」「間違えて動かさないように一括でロックしたい」と思ったことはありませんか?
マクロの記録から一歩踏み出し、Visioのオブジェクト構造を直接コントロールできるようになると、こうした複雑な図面操作が一瞬で、しかも確実に実行できるようになります。
今回は、図形(Shape)がどのレイヤーに所属しているかをスマートに判定・制御する`Shape.LayerMembership`というプロパティにスポットを当てます。ここをクリアすれば、Visio VBAの基本はバッチリですよ!先輩エンジニアと一緒に、一歩ずつ紐解いていきましょう。
—
1. Visioの「レイヤー」は、他のCADや描画ソフトと何が違う?
コードを書く前に、Visioにおけるレイヤーの「ユニークな仕組み」を脳内に描いてみましょう。ここを理解しておくことが、バグを防ぐ最大の近道です。
一般的なIllustratorやCADソフトでは、「図形は必ずどこか1つのレイヤーに属する(階層構造)」というルールが多いですよね。しかし、Visioは違います。
- 多対多の関係(マルチ・メンバーシップ):
Visioの1つの図形(Shape)は、「複数のレイヤーに同時に所属する」ことができます。もちろん、「どのレイヤーにも所属しない」ことも可能です。
【一般的なソフト】
[レイヤーA] ── (図形1)
[レイヤーB] ── (図形2)
【Visio】
[レイヤーA] ──┐
├─ (図形1) ── 同時に両方に所属できる!
[レイヤーB] ──┘
[レイヤーC] ──── (図形2)
(レイヤーなし) ── (図形3) ── どこにも属さないことも可能
この「柔軟すぎる仕組み」をVBAからコントロールするための強力な武器が、`Shape.LayerMembership` プロパティです。
—
2. `Shape.LayerMembership` の正体
`Shape.LayerMembership` は、その図形が所属しているすべてのレイヤー名を、セミコロン(`;`)で区切った1つの文字列として返してくれるプロパティです。
例えば、ある図形が「電気配線」レイヤーと「要メンテ」レイヤーの両方に所属している場合、このプロパティは次のような文字列を返します。
“電気配線;要メンテ”
もしどのレイヤーにも属していなければ、空の文字列(`””`)が返ってきます。
なぜこれが便利なのか?
「この図形は、特定のレイヤーに含まれているか?」を判定したいとき、わざわざ図形の中のレイヤーコレクションをループで回して名前を比較する必要がありません。
文字列の中に特定のレイヤー名が含まれているかを調べるだけ(`InStr` 関数などを使用)で、一瞬で判定できるからです。
—
3. 【実践コード】特定のカテゴリの図形を一括制御する
それでは、具体的なコードを見てみましょう。
今回は、「”Draft”(下書き)というレイヤーに所属している図形だけを、一括で非表示&ロックする」という実用的なマクロを作成します。
このコードは、プロの現場でもそのまま使えるように、「画面更新の停止による高速化」や「グループ図形への対応(再帰処理)」といった実務的なプロ仕様の処理を組み込んでいます。
Option Explicit
‘ =================================================================
‘ メイン処理:特定のレイヤーに属する図形を制御する
‘ =================================================================
Public Sub ControlDraftLayers()
Dim targetLayerName As String
targetLayerName = “Draft” ‘ 対象にしたいレイヤー名
‘ 1. パフォーマンス向上のため、画面更新と自動計算を一時停止
Dim originalScreenUpdating As Boolean
Dim originalDeferRecalc As Boolean
originalScreenUpdating = Application.ScreenUpdating
originalDeferRecalc = Application.DeferRecalc
Application.ScreenUpdating = False
Application.DeferRecalc = True
On Error GoTo ErrorHandler
Dim pg As Visio.Page
Set pg = ActivePage
If pg Is Nothing Then
MsgBox “アクティブなページが見つかりません。”, vbExclamation
GoTo CleanUp
End If
‘ 2. まず、ページ内に該当するレイヤーが存在するかチェックし、設定を変更する
Dim ly As Visio.Layer
Dim targetLayer As Visio.Layer
Set targetLayer = GetOrCreateLayer(pg, targetLayerName)
‘ レイヤー自体のプロパティを制御して、一括で「非表示」&「ロック」にする
‘ CellsCプロパティを使用して、レイヤーのセルを直接書き換えます
targetLayer.CellsC(visLayerVisible).ResultIU = 0 ‘ 0 = 非表示 / 1 = 表示
targetLayer.CellsC(visLayerLock).ResultIU = 1 ‘ 0 = アンロック / 1 = ロック
‘ 3. ページ内のすべての図形を走査し、対象レイヤーに属する図形に処理を行う
‘ (グループ化された図形の内部までくまなくチェックするために再帰関数を呼び出します)
Dim shp As Visio.Shape
For Each shp In pg.Shapes
ProcessShape shp, targetLayerName
Next shp
MsgBox “レイヤー ‘” & targetLayerName & “‘ の図形を一括制御しました。”, vbInformation, “処理完了”
CleanUp:
‘ 4. 停止していた画面更新と自動計算を元に戻す(超重要!)
Application.ScreenUpdating = originalScreenUpdating
Application.DeferRecalc = originalDeferRecalc
Exit Sub
ErrorHandler:
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical, “エラー”
Resume CleanUp
End Sub
‘ =================================================================
‘ 再帰関数:図形が対象レイヤーに属しているかを判定し、個別処理を行う
‘ =================================================================
Private Sub ProcessShape(ByVal shp As Visio.Shape, ByVal layerName As String)
‘ LayerMembershipプロパティを取得
Dim membership As String
membership = shp.LayerMembership
‘ セミコロン区切りの文字列から、対象のレイヤー名が含まれているか判定
‘ 前後にセミコロンを付与して部分一致の誤判定(例: “Drafting” に “Draft” がヒットする等)を防ぐプロの技
If InStr(1, “;” & membership & “;”, “;” & layerName & “;”, vbTextCompare) > 0 Then
‘ — [ここに個別で行いたい処理を書きます] —
‘ 例:対象図形の塗りつぶし色を一時的にグレーにする等の個別制御も可能
‘ shp.Cells(“FillForegnd”).FormulaU = “THEMEVAL() + 1”
‘ 今回はデバッグ出力にヒットした図形名を表示
Debug.Print “ヒットした図形: ” & shp.Name & ” (所属: ” & membership & “)”
End If
‘ グループ図形(サブシェイプ)が存在する場合、再帰的に内部も処理する
If shp.Shapes.Count > 0 Then
Dim subShp As Visio.Shape
For Each subShp In shp.Shapes
ProcessShape subShp, layerName
Next subShp
End If
End Sub
‘ =================================================================
‘ サポート関数:指定した名前のレイヤーを取得、なければ新規作成する
‘ =================================================================
Private Function GetOrCreateLayer(ByVal pg As Visio.Page, ByVal layerName As String) As Visio.Layer
On Error Resume Next
Set GetOrCreateLayer = pg.Layers.Item(layerName)
On Error GoTo 0
‘ 存在しない場合は新規作成
If GetOrCreateLayer Is Nothing Then
Set GetOrCreateLayer = pg.Layers.Add(layerName)
End If
End Function
—
4. プロが教える!ここが差がつく「極限の知見」
上のコードには、一般的な入門書には書かれていない「実務で絶対に壊れないマクロ」にするためのエッセンスが散りばめられています。
① `InStr` の前後にセミコロンを結合するテクニック
コード内の以下の部分に注目してください。
If InStr(1, “;” & membership & “;”, “;” & layerName & “;”, vbTextCompare) > 0 Then
単に `InStr(membership, layerName)` と書いてしまうと、もし探したいレイヤー名が `”Draft”` なのに、図形が `”Drafting”`(製図)という別のレイヤーに所属していた場合、誤って「含まれている」と判定されてしまいます。
検索対象とキーワードの両方を `;` で挟み込むことで、完全一致でのレイヤー名判定を高速に行うことができます。
② 再帰処理(Recursive Process)でグループの壁を越える
Visioの図面は、複数の図形が「グループ化」されていることが日常茶飯事です。
単に `For Each shp In Page.Shapes` を回すだけでは、グループの「外枠」しか取得できず、グループの内部に隠れている図形を見落としてしまいます。
関数の中で自分自身を呼び出す「再帰処理(`ProcessShape`)」を実装することで、どれだけ深く階層化されたグループ図形であっても、漏れなくすべての図形をチェックできます。
③ パフォーマンスの劇的向上(ScreenUpdating & DeferRecalc)
図形が数百、数千と増えてくると、VBAの処理速度は著しく低下します。
マクロの開始時に画面描画(`ScreenUpdating`)を止め、Visio内部の数式再計算(`DeferRecalc`)を保留にすることで、処理速度が数倍〜数十倍に跳ね上がります。最後に必ず元の状態に戻す(`CleanUp`)のが、ユーザーに優しいプロの設計です。
—
5. 陥りやすいエラーと回避策
エラー1: 「レイヤーが存在しない」エラー
図形を特定のレイヤーに所属させようとしたり、レイヤーの設定を変更しようとした際、そのページに該当するレイヤーが存在しないとエラーになります。
- 対策: コード内の `GetOrCreateLayer` 関数の表すように、「なければ作る」という安全弁を入れておくのがベストプラクティスです。
エラー2: ロックされたレイヤーの図形を編集しようとして止まる
レイヤーをロック(`visLayerLock = 1`)した状態で、VBAからその図形のテキストや色を変更しようとすると、Visioが「変更できません」と怒ってエラーを吐きます。
- 対策: 図形の中身を書き換える処理を行う場合は、必ず「ロックを解除」してから処理を行い、最後に「再ロック」する手順を踏んでください。
—
まとめ
今回のテーマである `Shape.LayerMembership` を使った一括制御、いかがでしたでしょうか?
1. Visioのレイヤーは「多対多」の柔軟な関係
2. `LayerMembership` はセミコロン区切りの文字列で判定しやすい
3. グループ図形に負けない「再帰処理」と、誤判定を防ぐ「セミコロン挟み撃ちテクニック」
この3つを押さえれば、Visio VBAの図面操作は格段にプロっぽく、そして強固なものになります。
「マクロの記録」で作ったコードは、言わば『一本道のレール』。
しかし、今回学んだオブジェクトモデルへのアプローチを身につければ、どんな複雑な図面でも自由自在に操る『魔法のタクト』を手に入れたも同然です。
ここをクリアすれば、Visio VBAの基本はバッチリですよ。ぜひあなたの実務の図面に組み込んで、その圧倒的なスピードを体感してみてくださいね!
