マクロの記録を卒業する。Visio VBAで「マスターシェイプ」をスマートに更新する極限の設計術
「マクロの記録」を使って、Visioの操作を自動化することに成功した皆さん、素晴らしい一歩を踏み出しましたね。
しかし、実務の現場で「図面が大規模化」し、「マスターシェイプ(部品の原型)がアップデートされた」とき、あなたは立ち止まってしまうかもしれません。
「図面の中に配置済みの100個のシェイプを、新しいデザインのマスターに一括で差し替えたい……でも、配置場所や入力済みのデータ(Shape Data)は維持したいんだ」
これを手作業で行うのは、まさに「メンテナンス地獄」です。
今日は、単なるコードの書き方ではなく、オブジェクトのライフサイクルを制御し、図面の整合性を保ちながら最新版へ一括置換する「アーキテクトの思考法」を伝授します。ここをクリアすれば、あなたのVisio VBAは「ツール」から「システム」へと進化します。
1. 本質を理解する:シェイプとマスターの「血縁関係」
まず、Visioの内部構造を正しく理解しましょう。ここを誤解すると、コードは動いても「図面が壊れる」原因になります。
Visioにおけるシェイプには、目に見えない「血縁関係」があります。
1. Stencil(ステンシル): 部品が入っている「カタログ」
2. Master(マスター): カタログに登録されている「部品の設計図」
3. Shape(シェイプ): 図面上に配置された「実体」
図面に配置されたシェイプは、常に特定のマスターへの参照(リンク)を持っています。
「マスターを更新した」としても、すでに図面に置かれたシェイプは「古い設計図」を覚えたままの、いわば「古い設計図から生まれた子」なのです。
私たちがやりたいのは、この「子(Shape)」を、新しい「設計図(Master)」から生まれた「新しい子」に、魂(位置、回転、入力データ)を移し替えて入れ替える作業です。
2. 戦略:単なる「削除と貼り付け」は卒業せよ
初学者が陥りやすい罠があります。それは、「古いシェイプを消して、新しいマスターをそこに貼り付ける」という単純なロジックです。
これでは以下の致命的な問題が発生します:
- Shape Dataの消失: ユーザーが入力した「型番」や「容量」などのカスタムデータが消えてしまう。
- 座標のズレ: 貼り付けた瞬間に、中心点や回転角がリセットされる。
- パフォーマンスの低下: 大量に「消しては貼る」を繰り返すと、Visioの描画処理が追いつかず、動作が極端に重くなる。
プロの設計指針(アルゴリズム)
1. 識別: 現在のシェイプが、ターゲットとするマスターと「名前」が一致するか判定する。
2. 抽出: 古いシェイプから「座標(X, Y)」「回転角」「Shape Data」をメモリに退避させる。
3. 生成: 新しいマスターから、同じ場所に新しいシェイプを生成する。
4. 注入: 退避させていた「魂(データ)」を、新しいシェイプに書き戻す。
5. 破棄: 古いシェイプを安全に削除する。
3. 実装:マスター更新ロジックの決定版
それでは、実際に動くコードを見ていきましょう。このコードは、特定のマスター名を持つシェイプを探し出し、最新のマスターに置き換える実戦的な構成です。
Option Explicit
''' <summary>
''' 図面内の特定のマスターシェイプを最新版に一括置換する
''' </summary>
''' <param name="targetMasterName">更新したいマスターの名前</param>
Sub UpdateMasterShapes()
Dim vsoPage As Visio.Page
Dim vsoShape As Visio.Shape
Dim vsoNewShape As Visio.Shape
Dim vsoMaster As Visio.Master
Dim targetMasterName As String
' --- 設定エリア ---
targetMasterName = "Network_Switch" ' 更新したいマスター名を指定
' ------------------
' パフォーマンス向上のための儀式
' 描画更新を止めることで、処理速度を劇的に上げ、画面のチラつきを防ぎます
Application.ScreenUpdating = False
Application.DeferUpdates = True
Set vsoPage = ActivePage
' ページ内の全シェイプをループ(後ろから回すのが鉄則!)
' ※削除を行う場合、前から回すとインデックスがズレてスキップが発生します
Dim i As Integer
For i = vsoPage.Shapes.Count To 1 Step -1
Set vsoShape = vsoPage.Shapes(i)
' 1. 識別: シェイプのマスター名がターゲットと一致するか
' Masterプロパティを通じて、そのシェイプの「設計図」を確認します
If vsoShape.Master.Name = targetMasterName Then
Debug.Print "Updating: " & vsoShape.Name
' 2. 抽出: 古いシェイプの属性をメモリに保存
Dim posX As Double: posX = vsoShape.X
Dim posY As Double: posY = vsoShape.Y
Dim rot As Double: rot = vsoShape.Rotation
Dim width As Double: width = vsoShape.Cells("Width").ResultIU
Dim height As Double: height = vsoShape.Cells("Height").ResultIU
' 3. 生成: 新しいマスターからインスタンスを作成
' vsoShape.Master を使うことで、同じステンシルの最新版を参照できます
Set vsoNewShape = vsoShape.Master.CreateInstance(visOrientationNone, vsoShape.Application)
' 4. 注入: 属性の復元
vsoNewShape.X = posX
vsoNewShape.Y = posY
vsoNewShape.Rotation = rot
vsoNewShape.Cells("Width").ResultIU = width
vsoNewShape.Cells("Height").ResultIU = height
' 【重要】Shape Data(ユーザー定義データ)の移行
' ここがプロの仕事です。既存のデータを一つずつコピーします
CopyShapeData vsoShape, vsoNewShape
' 5. 破棄: 古いシェイプを削除
vsoShape.Delete
End If
Next i
' 設定を元に戻す
Application.DeferUpdates = False
Application.ScreenUpdating = True
MsgBox "更新が完了しました。", vbInformation
End Sub
''' <summary>
''' シェイプのカスタムデータ(Shape Data)をコピーする補助関数
''' </summary>
Private Sub CopyShapeData(ByVal sourceShape As Visio.Shape, ByVal targetShape As Visio.Shape)
Dim rowIdx As Integer
Dim rowCount As Integer
Dim rowName As String
Dim rowValue As String
' Shape Dataの行数を取得
rowCount = sourceShape.RowCount
' 各行をループして、ターゲットに同じ名前の行があれば値を書き込む
For rowIdx = 0 To rowCount - 1
rowName = sourceShape.RowName(rowIdx)
' 行名が "Prop.xxx" の形式であることを確認(カスタムデータの標準形式)
If Left(rowName, 5) = "Prop." Then
' ターゲット側に同じデータ項目が存在するか確認し、値をコピー
On Error Resume Next ' ターゲットに項目がない場合の回避
targetShape.Cells(rowName).Formula = sourceShape.Cells(rowName).Formula
On Error GoTo 0
End If
Next rowIdx
End Sub
4. アーキテクトの視点:ここが「極限」のポイント
このコードには、現場で生き残るための3つの重要な知見が詰まっています。
① 「後ろからループ」の鉄則
`For i = vsoPage.Shapes.Count To 1 Step -1`
これはVisioに限らず、コレクション(集合)から要素を削除する際の黄金律です。前から削除していくと、要素が詰まってインデックス番号が変わり、次にチェックすべきシェイプを「飛ばしてしまう」ことになります。
② `Application.DeferUpdates` の魔術
大規模な図面でこれを忘れると、VBAは「1つのシェイプを置換するたびに、画面全体を再描画」しようとします。これが数千回繰り返されると、フリーズしたように見えます。`DeferUpdates = True` は、「計算が終わるまで画面を更新するな」という命令です。
③ Shape Data の「魂」の継承
`CopyShapeData` 関数を見てください。単に値をコピーするのではなく、`Formula`(数式)をコピーしています。
なぜか?
Shape Dataの中には、単なる数値だけでなく、`=GetProp(“IPAddress”)` のような数式が入っていることがあるからです。`Result`(結果)ではなく `Formula`(数式)をコピーすることで、動的なデータ構造まで完全に再現できるのです。
5. まとめ:次なるステップへ
今回の手法をマスターすれば、あなたは単に「作業を自動化する人」から、「図面資産のライフサイクルを管理するエンジニア」へと昇格しました。
次に挑戦すべきこと:
- エラーハンドリングの強化: マスターが見つからない場合に、エラーで止まらずにログを残す仕組みを作る。
- ログ出力: どのシェイプが更新され、どのシェイプがスキップされたかをテキストファイルに出力する。
Visio VBAは奥が深い世界です。しかし、オブジェクトの構造を正しく捉え、一つひとつの操作の「重み」を理解していれば、どんな複雑な要件も必ず突破できます。
さあ、恐れずにコードを書き進めてください。応援していますよ!
