Visio VBAを掌握する極限の知見:図形の属性を保持したままマスター置換する『Shape.ReplaceShape』の実務活用
こんにちは。チーフアーキテクトの私だ。
これまで数々の巨大なプラント図、ネットワーク構成図、そして業務プロセスフローの自動化案件を手がけてきたが、現場からもっとも頻繁に持ち込まれる悲鳴のような要望をご存知か?
「社内ニッチなシンボル規格が改定された。全図面の数千個に及ぶ旧図形を、位置・サイズ・接続情報を1ミリも狂わせずに、新標準シンボルへ一瞬で差し替えたい」
手動でこれをやろうものなら、気の遠くなるようなドラッグ&ドロップの作業の果てに、コネクタの切断や位置ズレというヒューマンエラーの山が築かれる。もちろん、旧シェイプを削除して新しいマスターをドロップし、座標を再計算してコネクタを張り直すような稚拙なVBAコードを書く者もいるが……そんなアプローチは、パフォーマンスの観点からも実務の耐用年数としても論外だ。
Visio VBAには、この絶望的な課題を1撃で解決する究極のメソッドが用意されている。それが `Shape.ReplaceShape` だ。
今回は、この隠れた名メソッドの深層を暴き、現場の生産性を爆発的に向上させるプロダクションコードを授けよう。
—
1. なぜ従来の置換ロジックは破綻するのか?
素人が図形の置換を自動化しようとすると、決まって以下のアンチパターンに陥る。
1. 完全削除&再生成の呪い
`TargetShape.Delete` を実行したあと、`Page.Drop` で新マスターを配置する。これでは、元のシェイプが持っていた `ID`、カスタムプロパティ(シェイプデータ)、そして何より「苦労してルーティングされたコネクタの接続情報(Glue)」がすべて吹き飛ぶ。
2. 位置・サイズの復元ロジックの肥大化
無理やり座標を合わせようと `PinX`, `PinY`, `Width`, `Height` を後から代号で代入するコードを書くが、マスターの基準点(PinPos)や図形の回転角(Angle)の差異により、図形が明後日の方向に跳ね飛ぶ。
`Shape.ReplaceShape` という「唯一無二の解」
Visioの `Shape.ReplaceShape` メソッドは、オブジェクトのライフサイクルを維持したまま、「実体(マスター)」だけをトランザクション的に入れ替える。
これにより、以下の奇跡的な状態が維持される。
- シェイプの `ID` が保持されるため、それを参照している外部ロジックやマクロが壊れない。
- 接続されているコネクタ(Dynamic Glue / Static Glue)が切断されない。
- シェイプデータ(Prop.)やユーザー定義セル(User.)のデータ構造が引き継がれる。
まさに、業務自動化エンジニアが求めてやまない「非破壊のマスター置換」なのだ。
—
2. 堅牢な設計:マスターの調達とスコープ管理
実務でこの自動化ツールを構築する際、最大のボトルネックになるのは「新しいマスターをどこから取得するか」である。
ドキュメント間でのマスターのコピー(`Document.Drop` や `Master.OpenDrawCopy` 等)は、メモリリークやドキュメントの肥大化を引き起こす温床となる。
今回のプロダクションコードでは、以下の設計思想を徹底する。
- ステンシルの一括ロード: 置換先の新マスターが含まれるステンシルファイル(`.vss` / `.vssx`)をバックグラウンドで安全に開き、メモリ上で参照する。
- フォールバック機構: 図面内に該当する旧マスターが存在しない、あるいは新マスターのキーが見つからない場合のエラーハンドリングを網羅する。
- トランザクション的処理: 予期せぬエラーで図面が破損しないよう、明示的なエラー処理を組み込む。
—
3. 【プロダクションコード】一括置換エンジン
以下のコードは、アクティブページ上の特定の旧マスターを使用している全シェイプを、指定した新マスターへ一瞬で置換する実務用マクロだ。
コピペして標準モジュールに貼り付け、パスを環境に合わせて書き換えるだけで、即座に現場で稼働する。
Option Explicit
‘ ==============================================================================
‘ 業務自動化プロシージャ: 図形の属性・接続を保持したままマスターを一括置換
‘ ==============================================================================
Public Sub ExecuteMasterReplacement()
‘ — 設定エリア(環境に合わせて書き換えてください) —
Const NEW_STENCIL_PATH As String = “C:\VisioStandards\NewStandardSymbols.vssx”
Const OLD_MASTER_NAME As String = “旧ルーター”
Const NEW_MASTER_NAME As String = “新ルーター”
‘ —————————————————————–
Dim targetDoc As Visio.Document
Dim stencilDoc As Visio.Document
Dim targetPage As Visio.Page
Dim shp As Visio.Shape
Dim newMaster As Visio.Master
Dim shpCol As Collection
Set shpCol = New Collection
Dim i As Long
Dim replacedCount As Long
replacedCount = 0
‘ エラーハンドリングの有効化
On Error GoTo ErrorHandler
Set targetDoc = ActiveDocument
Set targetPage = ActivePage
‘ 1. 新マスターを含むステンシルをバックグラウンドで開く
‘ ※既に開いている場合はエラーになるため、開いていない場合のみ処理
Set stencilDoc = Application.Documents.OpenEx(NEW_STENCIL_PATH, visOpenRO + visOpenHidden)
‘ 2. ステンシルから置換先のマスターオブジェクトを取得
On Error Resume Next
Set newMaster = stencilDoc.Masters.ItemU(NEW_MASTER_NAME)
On Error GoTo ErrorHandler
If newMaster is Nothing Then
MsgBox “指定された新マスター ‘” & NEW_MASTER_NAME & “‘ がステンシル内に見つかりません。”, vbCritical, “致命的エラー”
GoTo CleanUp
End If
‘ 3. ページ内の全シェイプをスキャンし、対象となる旧マスターのシェイプを抽出
‘ ※ループ中にコレクションを変更するとインデックスが狂うため、一度コレクションに退避
For Each shp In targetPage.Shapes
If Not shp.Master Is Nothing Then
If shp.Master.NameU = OLD_MASTER_NAME Or shp.Master.Name = OLD_MASTER_NAME Then
shpCol.Add shp
End If
End If
Next shp
If shpCol.Count = 0 Then
MsgBox “置換対象となる旧マスター ‘” & OLD_MASTER_NAME & “‘ を持つ図形は存在しません。”, vbInformation, “処理終了”
GoTo CleanUp
End If
‘ 4. トランザクション処理の開始(パフォーマンス向上とUndoのグループ化)
targetDoc.EventList.Enablable = False
‘ 5. ReplaceShapeメソッドによる非破壊置換の実行
For i = 1 To shpCol.Count
Set shp = shpCol(i)
‘ ————————————————————-
‘ 【核心】ReplaceShape メソッド
‘ 位置・サイズ・コネクタの接続状態を完璧に維持したままマスターを差し替える
‘ ————————————————————-
shp.ReplaceShape newMaster
replacedCount = replacedCount + 1
Next i
‘ 終了処理
targetDoc.EventList.Enablable = True
‘ 完了通知
MsgBox “マスター置換が完了しました。” & vbCrLf & _
“置換総数: ” & replacedCount & ” 個”, vbInformation, “成功”
CleanUp:
‘ リソースの解放
If Not stencilDoc Is Nothing Then
stencilDoc.Close
End If
Exit Sub
ErrorHandler:
targetDoc.EventList.Enablable = True
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “システムエラー”
Resume CleanUp
End Sub
—
4. プロが教える実装上の極意と罠
上記のコードは実務の現場に耐えうる堅牢性を持っているが、Visioのオブジェクトモデルの深淵を知る者として、さらに一歩進んだ注意点を授けておこう。
1. `.Name` と `.NameU` の罠
多言語対応(マルチリンガル)環境のVisioにおいて、UI上の表示名(`.Name`)はOSの言語設定によって変化する。
しかし、内部ユニバーサル名(`.NameU`)は常に英語の不変の識別子だ。実務でマスターやシェイプを特定する際は、必ず `.NameU` を使用すること。コード内の判定ロジックで両方をフォールバックしているのはそのためだ。
2. `EventList.Enablable` による爆速化
数百、数千のシェイプに対して一括置換を行う場合、Visioは図形が変更されるたびに内部イベント(ShapeAdded, ShapeChanged等)を発火させ、画面の再描画やリスナーの処理を行おうとする。
これが実行速度を著しく低下させる原因だ。コード内で `targetDoc.EventList.Enablable = False` を挟んでいるのは、イベント発火を一時停止し、処理速度を限界まで引き上げるためのプロのテクニックである。
3. コネクタの「接続点(Connection Point)」の互換性
`ReplaceShape` は極めて優秀だが、「旧マスターと新マスターの間で、接続点のインデックス構造(または名前)」が大きく異なっている場合、コネクタの向きが意図しない方向にねじれることがある。
新旧のマスターを作成・改修する際は、コネクタの接続点(Connection Point)の命名規則や位置関係を可能な限り一致させておくこと。これが、上流工程における真の「設計の美学」というものだ。
—
総括
Visio VBAにおける業務自動化は、単に「手作業をコードに置き換えること」ではない。アプリケーションの内部構造、オブジェクトのライフサイクル、そしてパフォーマンスのボトルネックを完全に理解し、「システムに負荷をかけず、一瞬で正確に仕事を終わらせる」ことこそがエンジニアの責務である。
今回紹介した `Shape.ReplaceShape` の実務活用術が、君のプロジェクトの生産性を次のステージへ引き上げる強力な武器となることを確信している。
現場のコードを、もっとエレガントに。健闘を祈る。
