Visio VBAを掌握する極限の知見:DocumentSheetで図面全体のグローバル状態を完全支配せよ
こんにちは。チーフアーキテクトの私だ。
これまで数々の巨大なVisio自動化プロジェクトを統括してきたが、現場で開発者からもっとも頻繁に持ち込まれる相談の一つが、「図面全体で共有したい設定値や管理番号をどこに保持すべきか」という問題だ。
標準モジュールにグローバル変数(`Public g_Config As String` など)を定義していないだろうか?
あるいは、外部のINIファイルやJSON、果てはレジストリやDBに設定を逃がしていないか?
ちょっと待て。その設計、今すぐ見直したほうがいい。
Visio図面を別名保存したり、メールで別担当者に送付したりした瞬間、外部ファイルやメモリ上のグローバル変数はロストし、リンクは切れ、整合性は崩壊する。「図面に関するデータは、図面ファイルそのものに内包させる」。これが、堅牢なVisioアドイン・VBA開発における鉄則だ。
今回は、Visioのオブジェクトモデルの隠れた要(かなめ)、`Document.DocumentSheet` を用いて、図面全体のグローバル変数を完全にコントロールし、バグの起きない堅牢な設定管理基盤を構築する極意を伝授しよう。
—
なぜ `Document.DocumentSheet` なのか?
Visioのオブジェクト階層において、`Document` オブジェクトには、その図面ファイル固有のグローバルなShapeSheetである `DocumentSheet` が内包されている。
[Application]
┗ [Document] (このファイル全体)
┣ [DocumentSheet] ★ここ!図面固有のグローバルShapeSheet(非表示のマスターシェイプ的コンテナ)
┣ [Pages] (ページ群)
┗ [Masters] (マスター群)
通常のシェイプやページとは異なり、`DocumentSheet` はキャンバス上に描画される図形ではない。しかし、実体は「Shapeオブジェクト」そのものであるため、ShapeSheetのセル(User定義セルやスクラッチパッド)を完全に保持できる。
外部ファイルや標準モジュール変数と比較した圧倒的な優位性
1. ファイルのポータビリティ(可搬性): `.vsd` / `.vsdm` ファイルをメールで送受信しようが、共有サーバーに移動しようが、設定値は図面の中に完全に封印されている。ファイルと一緒に移動するため「設定ファイルのパスが変わった」という事故が起きない。
2. 図面クローズ時のメモリリーク回避: 標準モジュールのグローバル変数はVisioが終了するまでメモリに残り続け、複数図面を開いた際に値が混濁するリスクがある。一方、`DocumentSheet` なら図面ごとのスコープに完全にカプセル化される。
3. UI(ShapeSheet)からの視認性・デバッグ性: いざとなれば、Visioの開発者モードからドキュメントのシェイプシートを開き、保持されている値を目視で確認・手動修正できる。このデバッグ性の高さはプログラマにとって最強の武器になる。
—
堅牢な設計:Userセルを用いたカスタムプロパティの抽象化
`DocumentSheet` をグローバル変数ストレージとして使う場合、生のセルに直接書き込むのは素人のやることだ。保守性を高めるためには、「Userセル(User-defined cells)」を活用し、変数名(キー)と値(バリュー)の構造を綺麗にマッピングする設計にすべきである。
たとえば、以下のキーを `DocumentSheet` 内の `User` セルにバインドするとしよう。
- `Config_ProjectName` : プロジェクト名
- `Config_Version` : 図面バージョン
- `Config_AuthorID` : 最終更新者ID
これをVBAから安全に読み書きするための「プロダクションコード」を以下に提示する。
—
実装:コピペで使えるプロダクションコード
以下のコードは、エラーハンドリングを完備し、存在しないカスタムセルの自動生成(遅延初期化)まで考慮した、実務レベルのモジュールだ。適当な標準モジュールに貼り付けて利用してほしい。
Option Explicit
‘ =========================================================================
‘ 模範的モジュール: DocumentSheetを用いたグローバル設定管理
‘ =========================================================================
‘ 設定値の書き込み(存在しない場合は自動生成)
Public Sub SetGlobalConfig(ByVal key As String, ByVal value As String)
On Error GoTo ErrorHandler
Dim doc As Visio.Document
Set doc = ActiveDocument ‘ または対象のDocumentオブジェクトを指定
Dim vsoShp As Visio.Shape
Set vsoShp = doc.DocumentSheet
Dim cellName As String
cellName = “User.” & key
Dim vsoCell As Visio.Cell
‘ セルが存在するかチェックし、なければ追加
If Not vsoShp.CellExists(cellName, visExistsLocally) Then
‘ セルが存在しない場合、数式として文字列を設定(ダブルクォーテーションで囲む)
vsoShp.AddNamedRow visSectionUser, key, 0
Set vsoCell = vsoShp.CellsU(cellName & “.Value”)
Else
Set vsoCell = vsoShp.CellsU(cellName & “.Value”)
End If
‘ 値の設定(VisioのShapeSheetでは文字列はダブルクォーテーションで囲む必要がある)
vsoCell.FormulaU = “””” & Replace(value, “”””, “”””””) & “”””
Exit Sub
ErrorHandler:
MsgBox “グローバル設定の書き込みに失敗しました [” & key & “]: ” & Err.Description, vbCritical, “設計エラー”
End Sub
‘ 設定値の読み込み
Public Function GetGlobalConfig(ByVal key As String, Optional ByVal defaultValue As String = “”) As String
On Error GoTo ErrorHandler
Dim doc As Visio.Document
Set doc = ActiveDocument
Dim vsoShp As Visio.Shape
Set vsoShp = doc.DocumentSheet
Dim cellName As String
cellName = “User.” & key & “.Value”
‘ セルが存在しない場合はデフォルト値を返す
If vsoShp.CellExists(cellName, visExistsLocally) Then
GetGlobalConfig = vsoShp.CellsU(cellName).ResultStr(&H20) ‘ visString
Else
GetGlobalConfig = defaultValue
End If
Exit Function
ErrorHandler:
‘ 異常時はデフォルト値をフォールバックとして返す
GetGlobalConfig = defaultValue
End Function
‘ 【使用例】イミディエイトウィンドウで試すプロシージャ
Public Sub Demo_Usage()
‘ 1. 書き込み
Call SetGlobalConfig(“ProjectName”, “次世代プラント自動化基盤”)
Call SetGlobalConfig(“SchemaVersion”, “2.1.0”)
‘ 2. 読み込み
Dim pName As String
Dim sVer As String
pName = GetGlobalConfig(“ProjectName”)
sVer = GetGlobalConfig(“SchemaVersion”, “1.0.0”) ‘ 存在しない場合のデフォルト値指定
Debug.Print “プロジェクト名: ” & pName
Debug.Print “スキーマバージョン: ” & sVer
MsgBox “DocumentSheetへの書き込み・読み込みに成功しました!”, vbInformation
End Sub
—
開発現場で陥る「罠」とチーフアーキテクトからの警告
この手法は極めて強力だが、現場のエンジニアが陥りがちな罠がいくつか存在する。ここをクリアできていないと、スケールした際に痛い目をみる。
1. `ResultStr` と `.FormulaU` の罠
ShapeSheetへの書き込み時、文字列を代入するには `FormulaU` プロパティに対して `”文字列”` のようにダブルクォーテーションをエスケープして渡す必要がある。生の値(例: `vsoCell.Formula = “hoge”`)を突っ込むと、Visioは「hogeという名前の関数、または他のセル名」を探しに行き、エラー `#NAME?` を吐き出す。
上記のサンプルコードでは、`”””” & Replace(…) & “””` によってこの問題を綺麗にクリアしている。
2. 読み取り専用(ReadOnly)ドキュメントの配慮
共有サーバー上のマスター図面などが「読み取り専用」で開かれている場合、`SetGlobalConfig` 内の `AddNamedRow` や `FormulaU` の書き込みで実行時エラー(トラップ可能なエラー)が発生する。
プロダクション環境では、処理の冒頭で `If doc.ReadOnly Then …` のガード節を入れるか、エラーハンドラで優しくユーザーに通知する設計を必ず組み込むこと。
3. マクロ有効図面(.vsdm)形式の強制
当然だが、コードや `DocumentSheet` を永続化するには、ファイルを Macro-Enabled Drawing (.vsdm) として保存させる必要がある。従来の `.vsdx` ではVBAコード自体が保存されないため、保存フォーマットのバリデーションチェックを保存時イベント(`DocumentSaved` 等)にフックさせるのもアーキテクトとしての手腕の見せ所だ。
—
総括
グローバル変数を標準モジュールのメモリ上に漂わせる時代は終わった。
プロフェッショナルなVisio自動化エンジニアであれば、「データは常にドキュメント(DocumentSheet)と運命を共にするべきだ」という思想のもと、堅牢で自律完結したファイル設計を行うべきである。
この手法をあなたのプロジェクトに導入すれば、ファイル管理のトラブルや環境依存のバグは劇的に激減する。
さあ、今すぐ標準モジュールの見苦しいグローバル変数を消去し、`DocumentSheet` による高貴な状態管理へ移行したまえ。
