Visio VBAを掌握する極限の知見:文書メタデータの双方向同期と堅牢なプロパティ管理
開発現場において、Visio図面は単なる「絵」ではない。それは設計情報であり、ステータスであり、時には外部データベースと連携する重要なデータソースそのものだ。
しかし、多くの現場で見かけるのは、「図面枠内のタイトルや版数を手動で書き換え、プロパティ情報は放置されている」という悲惨な状況である。図面内のテキストと、ファイルの `DocumentProperties` が乖離した瞬間、その図面はレガシーなゴミクズへと格下げされる。
今回は、Visioのドキュメントプロパティ(`BuiltInDocumentProperties` および `CustomDocumentProperties`)を完全に掌握し、図面枠のシェイプテキストと双方向で完全同期させるプロダクションコードを授けよう。中途半端なサンプルコードに絶望したエンジニア諸君、ここで真のデータ駆動型Visio自動化を学んでほしい。
—
1. なぜ「同期」は破綻するのか?(オブジェクトモデルの罠)
Visio VBAでプロパティを扱う際、多くの初心者がハマる罠がある。それは、「イベントのループ(無限連鎖)」と「オブジェクトのライフサイクルの無理解」だ。
図面枠のテキストを変更したときにプロパティを更新し、逆にプロパティを変更したときに図面枠を更新しようとすると、変更イベントが無限に発火し、Visioがフリーズするか、意図しない上書きが発生する。
この地獄を回避するためには、以下の原則を厳守しなければならない。
1. SSoT(Single Source of Truth:単一の真実)の決定: メタデータ(プロパティ)を主とするか、図面枠(UI)を主とするか。本稿では「DocumentPropertiesを真のデータソースとし、図面枠はビュー(表示)に過ぎない」というアーキテクチャを採用する。
2. 変更フラグ(セマフォ)によるイベント制御: 更新処理中であることを示すフラグを立て、イベントの再入を完全にブロックする。
—
2. プロダクションコード:完全同期エンジン
以下のコードは、エラーハンドリング、トランザクション的思考、そして保守性を極限まで高めた実務仕様のモジュールだ。適当な標準モジュールに貼り付けて即座に検証してほしい。
Option Explicit
‘ ==============================================================================
‘ 致命的なバグを防ぐためのセマフォフラグ
‘ ==============================================================================
Private m_IsSyncing As Boolean
‘ ==============================================================================
‘ 定数定義:同期対象のプロパティ名と図面枠上のシェイプ名(またはCell名)
‘ ==============================================================================
Private Const PROP_TITLE As String = “DocumentTitle” ‘ Custom Property名
Private Const PROP_VERSION As String = “DrawingVersion” ‘ Custom Property名
Private Const TARGET_PAGE_NAME As String = “表紙・図面枠” ‘ 対象ページ名
Private Const TARGET_SHAPE_NAME As String = “TitleBlock” ‘ 対象シェイプ名
”’
”’
Public Sub SyncPropertiesToDrawing()
If m_IsSyncing Then Exit Sub
m_IsSyncing = True
Dim doc As Visio.Document
Set doc = ActiveDocument
On Error GoTo ErrorHandler
‘ 1. プロパティ値の取得(存在しない場合はデフォルト値を設定)
Dim titleVal As String
Dim versionVal As String
titleVal = GetCustomProperty(doc, PROP_TITLE, “無題の図面”)
versionVal = GetCustomProperty(doc, PROP_VERSION, “Rev.1.0”)
‘ 2. 図面枠シェイプへの反映
Dim shp As Visio.Shape
Set shp = GetTargetShape(doc)
If Not shp Is Nothing. Then
‘ シェイプのUser定義セクションや特定シェイプデータに書き込む例
‘ ここではシンプルにCharacterプロパティ(テキスト)を更新
‘ 実務ではシェイプ内の特定サブシェイプやCharacter(1).Textを操作する
shp.Cells(“Prop.Title”).FormulaU = “””” & titleVal & “”””
shp.Cells(“Prop.Version”).FormulaU = “””” & versionVal & “”””
‘ 図面上のテキスト表示も強制更新
‘ shp.Text = “タイトル: ” & titleVal & vbCrLf & “版数: ” & versionVal
End If
CleanUp:
m_IsSyncing = False
Exit Sub
ErrorHandler:
MsgBox “プロパティからの同期中にエラーが発生しました: ” & Err.Description, vbCritical, “Sync Error”
Resume CleanUp
End Sub
”’
”’
Public Sub SyncDrawingToProperties()
If m_IsSyncing Then Exit Sub
m_IsSyncing = True
Dim doc As Visio.Document
Set doc = ActiveDocument
On Error GoTo ErrorHandler
Dim shp As Visio.Shape
Set shp = GetTargetShape(doc)
If shp Is Nothing Then Err.Raise 1000, , “同期対象の図面枠シェイプが見つかりません。”
‘ シェイプ側から値を取得(例:シェイプデータ(Prop)から取得)
Dim titleVal As String
Dim versionVal As String
titleVal = shp.Cells(“Prop.Title”).ResultStr(&H20) ‘ visNone = &H20
versionVal = shp.Cells(“Prop.Version”).ResultStr(&H20)
‘ CustomDocumentProperties へ書き込み
SetCustomProperty doc, PROP_TITLE, titleVal
SetCustomProperty doc, PROP_VERSION, versionVal
Debug.Print “Properties updated successfully at ” & Now
CleanUp:
m_IsSyncing = False
Exit Sub
ErrorHandler:
MsgBox “図面枠からの同期中にエラーが発生しました: ” & Err.Description, vbCritical, “Sync Error”
Resume CleanUp
End Sub
‘ ==============================================================================
‘ ヘルパー関数群(カプセル化の徹底)
‘ ==============================================================================
Private Function GetCustomProperty(doc As Visio.Document, propName As String, defaultValue As String) As String
Dim prop As Office.DocumentProperty
On Error Resume Next
Set prop = doc.CustomDocumentProperties(propName)
On Error GoTo 0
If prop Is Nothing Then
‘ プロパティが存在しない場合は作成してデフォルトを返す
doc.CustomDocumentProperties.Add propName, False, msoPropertyTypeString, defaultValue
GetCustomProperty = defaultValue
Else
GetCustomProperty = CStr(prop.Value)
End If
End Function
Private Sub SetCustomProperty(doc As Visio.Document, propName As String, propValue As String)
Dim prop As Office.DocumentProperty
On Error Resume Next
Set prop = doc.CustomDocumentProperties(propName)
On Error GoTo 0
If prop Is Nothing Then
doc.CustomDocumentProperties.Add propName, False, msoPropertyTypeString, propValue
Else
prop.Value = propValue
End If
End Sub
Private Function GetTargetShape(doc As Visio.Document) As Visio.Shape
Dim pg As Visio.Page
On Error Resume Next
Set pg = doc.Pages(TARGET_PAGE_NAME)
On Error GoTo 0
If pg Is Nothing Then
‘ ページが見つからない場合はアクティブページをフォールバックとする
Set pg = doc.ActivePage
End If
Dim shp As Visio.Shape
On Error Resume Next
Set shp = pg.Shapes(TARGET_SHAPE_NAME)
On Error GoTo 0
Set GetTargetShape = shp
End Function
—
3. 実務運用における設計上の注意点とアーキテクチャ
このコードを実際の業務システム(PLM連携や自動図面生成バッチ)に組み込むにあたり、チーフアーキテクトとして以下の要件を付言する。
1. 標準プロパティ(BuiltInDocumentProperties)の限界
Visioの `BuiltInDocumentProperties`(タイトル、作者、会社など)は、Office共通の規格に準拠しているため一見便利に見える。しかし、型制約が厳しく、VisioのバージョンやOSの言語環境によってプロパティインデックスがズレるという致命的な仕様がある。
そのため、業務システム連携においては、今回コードで示したような `CustomDocumentProperties`(カスタムプロパティ)を主軸に据える のがプロの選択である。
2. 外部データベース(SQL Server / SharePoint等)との連携
この `CustomDocumentProperties` の真価は、Visio本体を開くことなく、外部のC#プログラムやVBScriptからCOMコンポーネント経由で一括読み書きできる点にある。
例えば、夜間バッチでサーバー上の数百枚のVSDXファイルを開き、図面を開くオーバーヘッドなしでプロパティ層だけを書き換える、あるいは設計管理DBから最新の版数をドキュメントプロパティに流し込むといった自動化が極めて容易になる。
3. イベントハンドリングの高度化(DocumentBeforeSave へのフック)
「ユーザーが保存ボタンを押した瞬間」に自動で `SyncDrawingToProperties` を走らせたい場合、`ThisDocument` クラスモジュールに以下のイベントを実装する。
‘ ThisDocument モジュールに記述
Private Sub Document_BeforeSave(ByVal Doc As Visio.Document)
‘ 保存直前に図面枠の変更をプロパティに強制同期
SyncDrawingToProperties
End Sub
これにより、ユーザーが「プロパティを更新し忘れた」というヒューマンエラーをシステム側で完全に根絶できる。
—
最後に:コードを書き捨てるな、仕組みを遺せ
VBAはレガシー言語とやゆされることがある。しかし、Visioの深部にアクセスし、図面データの整合性を担保する上で、これほど軽快で強力なツール他にはない。
今回提供した双方向同期のロジックは、そのまま貴社の開発現場の品質向上に直結するはずだ。手作業による泥臭い図面管理から脱却し、メタデータ駆動の洗練された自動化基盤を構築してほしい。エンジニアの腕の見出しどころは、まさにこういう細部の設計にある。
