こんにちは! Visioの図面整理や、引き継いだレガシーシステムの保守に頭を悩ませていませんか?
「マクロの記録」で自動化の第一歩を踏み出したものの、いざ現場の古い図面を開いてみると、どこにどんなカスタムプロパティ(図形データ)が埋め込まれているのか、なぜかエラーを吐く数式がどこに潜んでいるのか……サッパリ分からない。そんな絶望感を味わったことがある方も多いはずです。
Visioの図形(Shape)は、単なる「絵」ではありません。内部に「ShapeSheet(シェープシート)」という巨大な多次元データベースを抱えた、いわば小さな生き物のようなものです。
今回は、そのShapeSheetの全セクション・全セルを総ナメにし、隠されたプロパティや数式を丸裸にする「形状解析デバッグツール」の作り方を解説します。
ここをクリアすれば、あなたのVisio VBAスキルは「初級者」を完全に脱却し、どんなレガシー図面も意図通りにコントロールできる「アーキテクト」の領域に到達します。一緒にマスターしていきましょう!
—
1. なぜ「ShapeSheetの全走査」が必要なのか?
Visioの図面を長く運用していると、こんな問題に直面します。
- 前任者が何を思ったか、あちこちに古いカスタムプロパティ(Propセクション)を散らかしている。
- 存在しないセルを参照している「#REF!」エラーの数式が、どの図形のどこにあるのか分からない。
- 自動化スクリプトを書いたのに、一部の図形だけ予期せぬ挙動をする。
Visioの画面上からこれらを一つずつ確認するのは、砂漠で針を探すようなものです。だからこそ、VBAの力を使ってプログラムに全セクションを強制走査させ、イミディエイトウィンドウにログを出力させるツールが必要になります。
Visioオブジェクトモデルの階層構造を理解する
Visioの構造は、ロシアの人形「マトリョーシカ」のように階層化されています。
Application (Visioアプリ全体)
┗ Documents (開いている図面たち)
┗ Pages (ページたち)
┗ Shapes (図形たち) ──★ここが今回のターゲット!
┗ Sections (ShapeSheetのカテゴリ群: Prop, LineFormatなど)
┗ Rows (行)
┗ Cells (セル: 具体的な値や数式)
この階層の最深部にある `Section` や `Cell` まで安全に潜っていく方法を、コードを書きながら見ていきましょう。
—
2. 【実践】ShapeSheet全走査デバッグツールのコード
以下のコードを、VisioのVBAエディタ(`Alt + F11` で起動)の標準モジュールに貼り付けてください。
選択している図形(またはページ上の全図形)のShapeSheetをくまなくスキャンし、設定されている数式や値をイミディエイトウィンドウに吐き出すプロシージャです。
Option Explicit
‘ ==============================================================================
‘ 処理名 : InspectSelectedShapeShapeSheet
‘ 概要 : 選択中の図形のShapeSheetを全セクション・全セル走査し解析する
‘ ==============================================================================
Sub InspectSelectedShapeShapeSheet()
Dim vsoShape As Visio.Shape
‘ 1. エラーハンドリングの基本:選択図形がない場合のガード
If ActiveWindow.Selection.Count = 0 Then
MsgBox “解析したい図形を1つ以上選択してください。”, vbExclamation, “デバッグツール”
Exit Sub
End If
Set vsoShape = ActiveWindow.Selection(1)
Debug.Print “==================================================”
Debug.Print “【解析開始】図形名: ” & vsoShape.Name & ” (ID: ” & vsoShape.ID & “)”
Debug.Print “==================================================”
‘ 2. セクション走査の実行
Call TraverseSections(vsoShape)
Debug.Print “==================================================”
Debug.Print “【解析完了】イミディエイトウィンドウを確認してください。”
Debug.Print “==================================================”
End Sub
‘ ==============================================================================
‘ 内部関数 : TraverseSections
‘ 概要 : Shapeが持つすべてのSectionをループで総当たりする
‘ ==============================================================================
Private Sub TraverseSections(ByVal shp As Visio.Shape)
Dim secIndex As Integer
Dim vsoSection As Visio.Section
Dim intSecCount As Integer
intSecCount = shp.SectionCount
‘ セクションが存在しない場合は抜ける
If intSecCount = 0 Then Exit Sub
‘ 注意:Visioのセクションインデックスは 0 から始まるものと定数がある
For secIndex = 0 To intSecCount – 1
Set vsoSection = shp.Section(secIndex)
‘ セクションが有効(使われている)場合のみ処理
If Not vsoSection Is Nothing Then
Call TraverseRows(shp, vsoSection, secIndex)
End If
Next secIndex
End Sub
‘ ==============================================================================
‘ 内部関数 : TraverseRows
‘ 概要 : 各Section内のRow(行)を走査する
‘ ==============================================================================
Private Sub TraverseRows(ByVal shp As Visio.Shape, ByVal sec As Visio.Section, ByVal secIndex As Integer)
Dim rowIndex As Long
Dim vsoRow As Visio.Row
Dim intRowCount As Long
intRowCount = sec.Count
If intRowCount = 0 Then Exit Sub
For rowIndex = 0 To intRowCount – 1
On Error Resume Next
Set vsoRow = sec.Item(rowIndex)
If Err.Number = 0 And Not vsoRow Is Nothing Then
Call TraverseCells(shp, secIndex, rowIndex)
End If
On Error GoTo 0
Next rowIndex
End Sub
‘ ==============================================================================
‘ 内部関数 : TraverseCells
‘ 概要 : 各Row内のCell(セル)を走査し、数式と値をログ出力する
‘ ==============================================================================
Private Sub TraverseCells(ByVal shp As Visio.Shape, ByVal secIndex As Integer, ByVal rowIndex As Long)
Dim cellIndex As Integer
Dim vsoCell As Visio.Cell
Dim intCellCount As Integer
On Error Resume Next
intCellCount = shp.CellCountZC(secIndex, rowIndex)
If Err.Number <> 0 Then
On Error GoTo 0
Exit Sub
End If
On Error GoTo 0
For cellIndex = 0 To intCellCount – 1
On Error Resume Next
Set vsoCell = shp.CellsSRC(secIndex, rowIndex, cellIndex)
If Err.Number = 0 And Not vsoCell Is Nothing Then
‘ 未使用や空のセルでなければ出力(パフォーマンスとログの可読性を考慮)
If vsoCell.Formula <> “” Then
Debug.Print ” [Sec:” & secIndex & ” | Row:” & rowIndex & ” | Cell:” & cellIndex & “] ” & _
“名前: ” & vsoCell.Name & ” | ” & _
“数式: ” & vsoCell.Formula & ” | ” & _
“値: ” & vsoCell.ResultIU
End If
End If
On Error GoTo 0
Next cellIndex
End Sub
—
3. コードのポイントと、陥りやすい「Visio特有の罠」
このコードには、レガシー図面を相手にするプロフェッショナルならではの「知見」がいくつも詰まっています。初学者がハマりがちなポイントを交えて解説します。
① `On Error Resume Next` の正しい使い方
「エラー制御は悪」と教わった方も多いかもしれませんが、Visio VBAのShapeSheet操作においては、エラーハンドリングは必須の防壁です。
なぜなら、すべてのセクションや行が完全にデータを持っているわけではなく、存在しないインデックスにアクセスしようとするとVisioは容赦なく実行時エラーを吐いて強制終了するためです。
「見つからないエラーは無視して、次の安全なオブジェクトへ進む」という構え(防衛的プログラミング)が、巨大な図面をクラッシュさせずに走査する秘訣です。
② `Formula` と `ResultIU` の使い分け
ShapeSheetのセルには、「数式(Formula)」と「計算結果(Result)」という2つの顔があります。
- `vsoCell.Formula`:`=Width2` のような入力されている数式文字列を取得します。
- `vsoCell.ResultIU`:その数式が計算された結果(内部単位系:Internal Units)を取得します。
デバッグの際は、ユーザーが意図しないハードコーディングや、壊れた数式(`#REF!`など)を発見したいので、必ず両方をセットで監視できるようにしておきましょう。
—
4. 応用:隠れたエラー数式(#REF!)を自動修正する
上記のコードをベースに少し応用すれば、図面全体から特定の不要なプロパティセクションをごっそり削除したり、エラー数式を強制的にクレンジングしたりする「自動修復スクリプト」へと進化させることができます。
レガシー図面の保守は地味で泥臭い作業に見えますが、オブジェクトモデルの本質を理解し、こうした強力なツールを自作できるようになると、Visio開発のスピードは文字通り「100倍」に跳ね上がります。
「ここをもっとこうしたい」「このセクションだけをピンポイントで狙いたい」といった疑問があれば、いつでも先輩エンジニアに聞いてくださいね。あなたのVBAライフを応援しています!
