【実務・中級編】Shape.Typeプロパティによる図形判定:グループ・コネクタ・ガイド線をVBAで安全にフィルタリング – Visio VBA解析バイブル

スポンサーリンク

Visio VBAを掌握する極限の知見:Shape.Typeによる図形判定と、地雷を踏まない堅牢なフィルタリング設計

業務自動化の現場において、Microsoft Visioはフローチャートやネットワーク図の生成という強力な武器となる。しかし、Visio VBAの自動化において、多くの開発者が最初の数週間で「想定外のオブジェクトによる実行時エラー」の泥沼にハマる。

「ページ内の全シェイプを一括処理するループを組んだら、コネクタで計算が狂った」
「グループ化された図形の配下に潜ろうとして、オブジェクト変数がNothingになり沈黙した」
「ガイド線まで処理対象に含まれてしまい、図面がぐちゃぐちゃになった」

これらはすべて、Visioのオブジェクトモデルの階層構造と、`Shape.Type` プロパティの挙動を甘く見たことに起因する設計ミスだ。

今回は、数千・数万規模の図形を扱うエンタープライズ環境の現場でもビクともしない、安全かつ高速なシェイプ・フィルタリングの極意を伝授する。

1. なぜ `For Each shp In ActivePage.Shapes` だけでは破綻するのか?

Visioのページ(`Page` オブジェクト)に存在するすべての図形を処理しようとするとき、安易に `Shapes` コレクションを全走査するコードを書くのは、地雷原を目隠しで歩くようなものだ。

Visioのキャンバスには、ユーザーが意識的に配置した「通常のマスター図形」だけでなく、以下のモブキャラクターたちが同居している。

1. コネクタ(Connector): 図形と言いながら、実態はルーティング機能を持つ特殊な線。
2. グループ(Group): 複数の図形を束ねたコンテナ。親と子が別々のシェイプとして存在し、誤って親を処理した後に子を二重処理するバグの温床となる。
3. ガイド線・ガイドポイント(Guide): 描画補助のための見えない、あるいは印刷されない特殊オブジェクト。

これらを区別せず、単に `shp.Text = “Processed”` のような操作を行えば、コネクタに文字が入ってレイアウトが崩壊するか、不正なプロパティアクセスで `Error 91: オブジェクト変数または With ブロック変数が見つかりません。` が発生する。

ここで頼るべき羅針盤が、`Shape.Type` プロパティである。

2. Shape.Type の正体と、現場で使うべき定数

`Shape.Type` は、そのシェイプが何者であるかを示す整数値を返す。VBAのコード補完(IntelliSense)では分かりにくいが、Visioのタイプライブラリには明確な定数が用意されている。

| 定数名 | 値 | 意味・実務上のリスク |
| :— | :— | :— |
| `visTypeShape` | 2 | 通常のマスター図形。基本こいつだけを処理対象にしたい。 |
| `visTypeGroup` | 4 | グループ図形。配下に `Shapes` コレクションを持つため再帰処理が必要。 |
| `visTypeForeign` | 3 | CAD図面や画像などの外部オブジェクト。 |
| `visTypeGuide` | 6 | ガイド線・ガイドポイント。絶対に処理してはならない。 |

※なお、コネクタは `Shape.Type` が `visTypeShape` であっても、`shp.Connects` やマスターの種類、あるいは別の判定フラグで除外する必要がある点に注意してほしい(後述のコード参照)。

3. 【プロダクションコード】安全なフィルタリングを実装する

ここに示すのは、実務の現場でそのまま組み込める、堅牢性を極めたVBAモジュールだ。エラーハンドリング、グループの再帰処理、コネクタ・ガイドの完璧な排除を実装している。

Option Explicit

‘ ==============================================================================
‘ モジュール名: modShapeFilterExecutor
‘ 概要: ページ内のシェイプを安全にフィルタリングし、業務ロジックを適用する
‘ ==============================================================================
Public Sub ExecuteSafeShapeProcessing()
Dim vsoPage As Visio.Page
Set vsoPage = ActivePage

‘ 画面描画を停止して圧倒的なパフォーマンス向上を図る(鉄則)
Application.ScreenUpdating = False
Application.ShowChanges = False

On Error GoTo ErrorHandler

Dim shp As Visio.Shape
Dim processedCount As Long
processedCount = 0

‘ ページ内のトップレベルシェイプを走査
For Each shp in vsoPage.Shapes
If IsValidTargetShape(shp) Then
‘ — ここに実際の業務処理(データ連携や属性変更)を記述 —
Call ProcessBusinessLogic(shp)
processedCount = processedCount + 1
End If
Next shp

MsgBox “処理が正常に完了しました。処理件数: ” & processedCount & ” 件”, vbInformation, “完了”

CleanUp:
Application.ScreenUpdating = True
Application.ShowChanges = True
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
Resume CleanUp
End Sub

‘ ==============================================================================
‘ 関数名: IsValidTargetShape
‘ 概要: シェイプが処理対象として安全かつ適切かを判定する防壁関数
‘ ==============================================================================
Private Function IsValidTargetShape(ByVal shp As Visio.Shape) As Boolean
‘ デフォルトは除外
IsValidTargetShape = False

‘ 1. ガイド線・ガイドポイントは即座に除外
If shp.Type = visTypeGuide Then Exit Function

‘ 2. 外部オブジェクト(CADや画像)を除外したい場合のガード
If shp.Type = visTypeForeign Then Exit Function

‘ 3. コネクタの判定
‘ Visioではコネクタも Type = visTypeShape になる場合があるため、
‘ マスター名やコネクタ特有のプロパティ(OneD等)で厳密に弾く
If shp.OneD Then
‘ 1次元シェイプ(線やコネクタ)は除外
Exit Function
End If

‘ 4. グループ図形の扱い
If shp.Type = visTypeGroup Then
‘ 【重要】グループ自体を処理するか、配下のシェイプを個別処理するかは要件次第。
‘ ここでは「グループ親自体は処理せず、配下を再帰的に処理する」設計にする。
‘ グループ内の子シェイプを触る場合は、別プロシージャで再帰呼び出しを実装する。
Exit Function
End If

‘ すべての関門を突破した通常の図形のみ True を返す
IsValidTargetShape = True
End Function

‘ ==============================================================================
‘ プロシージャ名: ProcessBusinessLogic
‘ 概要: フィルタリング済みの安全なシェイプに対する個別処理
‘ ==============================================================================
Private Sub ProcessBusinessLogic(ByRef shp As Visio.Shape)
‘ 例: シェイプのシェイプシートからカスタムプロパティ(Shape Data)を読み書きする
‘ ここにデータベース連携や外部APIからのデータ注入処理を記述する

‘ デバッグ出力例
Debug.Print “処理対象シェイプ ID: ” & shp.ID & “, 名前: ” & shp.Name
End Sub

4. チーフアーキテクトからの実践的アドバイス

① 画面描画のロック(ScreenUpdating)はプログラミングの礼儀

数千個のシェイプをループさせる際、`Application.ScreenUpdating = False` を忘れると、Visioは一瞬一瞬の図形変化を描画しようと迷走し、処理速度が10倍以上低下する。必ずプロシージャの最初で止め、終了時(あるいはエラー時)に復元させろ。

② 1次元シェイプ(`shp.OneD`)の活用

コネクタの判定において、`Shape.Type` だけでは不十分なケースがある。Visioの線やコネクタは `OneD` プロパティが `-1 (True)` になる。これを利用することで、形状に関わらず「線分・コネクタ類」を綺麗にフィルタリングから除外できる。

③ グループの再帰処理(Recursion)の設計思想

もし「グループの中にある図形もすべて書き換えたい」という要件があるならば、`visTypeGroup` を検知した瞬間に、配下の `Shapes` コレクションに対して自分自身の関数(または専用の再帰関数)を呼び出す設計が必要だ。
ただし、多重グループ(グループの中にグループがある構造)は無限ループやスタックオーバーフローのリスクを生むため、階層の深さ(Depth)をトラッキングするガード節を必ず設けること。

5. まとめ

Visio VBAにおけるオブジェクト操作の成否は、「いかにゴミや例外オブジェクトを早い段階で弾くか(Fail-Fastの原則)」にかかっている。

今回紹介した `Shape.Type` の識別と、`OneD` プロパティ、そして明示的なエラーハンドリングと描画停止のコンボをマスターすれば、あなたの書くVisioマクロは、現場のエンジニアたちが舌を巻くほど堅牢で、かつ実用的な自動化ツールへと生まれ変わるはずだ。

設計を制する者が、自動化を制す。次の開発案件から、ぜひこのコードベースを取り入れてほしい。

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