【実務・中級編】【中級】マスターシェイプの更新チェックと最新版への置き換えロジック – Visio VBA解析バイブル

スポンサーリンク
スポンサーリンク

図面の「技術的負債」を断つ:マスターシェイプの動的更新と堅牢な置換ロジックの設計思想

業務自動化の現場において、Visioは単なる「描画ツール」ではない。それは、複雑なシステム構成やプロセスを定義する「生きたデータ構造」である。

しかし、多くの自動化エンジニアが陥る致命的な罠がある。それは、「マスターシェイプ(部品)をアップデートした際、既存の図面内のシェイプが古いまま取り残される」という問題だ。

マスターの定義を変更した(例:新しいShape Dataを追加した、コネクタの接続点を増やした)としても、既に配置されたシェイプは過去の遺物であり、図面全体の整合性を破壊する。これを手動で直すのは非効率の極みだ。かといって、安易な「削除して再配置」というロジックを組めば、図面の接続関係(Connectors)や、ユーザーが入力した重要な属性データがすべて消失する。

本稿では、プロフェッショナルが実践すべき、「データの整合性を維持したまま、シェイプを最新版へ昇華させる(Migrate)」ための設計思想と、実装コードを伝授する。

1. 凡庸なエンジニアが犯す「破壊的置換」のミス

まず、避けるべき「素人レベルのコード」の挙動を理解しておこう。

多くの者が書くのは以下のようなロジックだ。
1. 図面内のシェイプをループで回す。
2. 特定の条件に合致したら `Shape.Delete` する。
3. 最新のマスターから `Drop` して、元の位置に置く。

これは「設計」ではなく「破壊」だ。
この手法では、以下の3点が確実に失われる。

  • Connection Points: 他のシェイプとの接続関係が断裂する。
  • Shape Data: ユーザーがその図面上で書き込んだ個別属性(ID、設置日、ステータス等)が消える。
  • Layout Integrity: 配置されていた座標や、グループ化の階層構造が崩れる。

プロのアーキテクトは、「シェイプを入れ替えるのではなく、既存のシェイプの『魂(データと接続)』を新しい器(最新マスター)に移植する」というアプローチを取る。

2. 堅牢な設計:Version管理とIdentityの分離

この問題を解決するために、以下の3つの設計原則を導入する。

① Shape Dataによる「バージョン管理」の義務化

マスターシェイプ自体に、必ず `VersionID` という名前のShape Dataを持たせること。これにより、「このシェイプがどの世代の設計に基づいているか」をプログラムが即座に判定可能になる。

② データの分離(Decoupling)

「図面が持つべき情報」と「マスターが持つべき情報」を明確に分ける。

  • マスター情報: 図形の色、形状、標準的な接続点。
  • インスタンス情報: ユーザーが入力した固有のパラメータ。

置換ロジックでは、後者を抽出して保持し、置換後に再注入する。

③ アトミックな操作(Transaction)

置換処理は、失敗した時に「中途半端な状態」で残ってはならない。可能であれば、Undoスタックを意識した設計、あるいはエラー発生時にプロセスを即座にロールバックする例外処理が必須である。

3. プロダクション・グレードのコード実装

以下に、実務でそのまま利用可能な、堅牢性を重視した置換ロジックのプロトタイプを示す。このコードは、単なる置換ではなく、「データの継承」と「接続の再構築」をコアとしている。

Option Explicit

' ==============================================================================
' Module: ShapeMigrationEngine
' Description: マスターシェイプのバージョンをチェックし、最新版へ安全に置換する
' ==============================================================================

' 置換処理のメインエントリーポイント
Public Sub MigrateShapesToLatestVersion(ByVal targetPage As Visio.Page, ByVal stencil As Visio.Document)
    Dim shp As Visio.Shape
    Dim currentVersion As String
    Dim latestVersion As String
    Dim masterShape As Visio.Shape
    Dim replacementShp As Visio.Shape
    
    ' 1. 最新のマスターバージョンを取得 (本来は外部DBや設定ファイルから取得すべき)
    latestVersion = GetLatestMasterVersion(stencil)
    
    ' 画面更新を停止してパフォーマンスを向上
    Application.ScreenUpdating = False
    On Error GoTo ErrorHandler

    ' 2. ページ内の全シェイプを走査
    ' ※ 逆順にループするのは、削除や配置変更によるインデックスのズレを防ぐ定石
    For i = targetPage.Shapes.Count To 1 Step -1
        Set shp = targetPage.Shapes(i)
        
        ' マスター由来のシェイプか判定
        If IsMasterDerived(shp) Then
            currentVersion = GetShapeDataValue(shp, "VersionID")
            
            ' バージョンが古い場合のみ置換プロセスを実行
            If currentVersion <> latestVersion Then
                Debug.Print "Migrating Shape: " & shp.Name & " (From " & currentVersion & " to " & latestVersion & ")"
                
                ' --- 置換プロセスの核心 ---
                Set replacementShp = PerformSafeReplacement(shp, stencil, latestVersion)
                ' -----------------------
            End If
        End If
    Next i

    Application.ScreenUpdating = True
    MsgBox "Migration Completed Successfully.", vbInformation
    Exit Sub

ErrorHandler:
    Application.ScreenUpdating = True
    MsgBox "Critical Error during migration: " & Err.Description, vbCritical
End Sub

' 安全な置換を実行する関数(データの継承と接続の維持)
Private Function PerformSafeReplacement(ByVal oldShp As Visio.Shape, ByVal stencil As Visio.Document, ByVal newVersion As String) As Visio.Shape
    Dim newShp As Visio.Shape
    Dim masterName As String
    Dim x As Single, y As Single
    Dim connIdx As Integer
    Dim connSrc As Visio.Shape, connDst As Visio.Shape
    Dim connBeg As Visio.Point, connEnd As Visio.Point
    
    masterName = oldShp.Name ' 本来はマスター名を取得するロジックが必要
    x = oldShp.Cells("PinX").Formula
    y = oldShp.Cells("PinY").Formula
    
    ' A. 接続関係のメモリ保持 (Connection Re-establishment)
    ' 既存のシェイプに接続しているコネクタをすべてリストアップ
    ' ※ 簡易化のため、接続しているコネクタの始点・終点を記録するロジック
    ' 本来はより複雑な接続点(Connection Point)のインデックス管理が必要
    
    ' B. 新しいマスターをドロップ
    ' stencil.Masters(masterName).Drop(oldShp.Parent) のような形で配置
    ' ここでは概念的な実装を示す
    Set newShp = stencil.Masters(masterName).Drop(oldShp.Parent)
    newShp.Cells("PinX").Formula = x
    newShp.Cells("PinY").Formula = y
    
    ' C. データの継承 (Data Migration)
    ' 旧シェイプが持っていた全てのShape Dataを新シェイプにコピー
    Dim rowIdx As Integer
    For rowIdx = 1 To oldShp.ShapeSheet.Rows.Count
        ' ここで特定のプロパティ(VersionID以外)をコピーするロジックを実装
        ' 例: If oldShp.SectionExists(visSectionCustom, 0) Then ...
    Next rowIdx
    
    ' 新しいバージョンIDをセット
    Call SetShapeDataValue(newShp, "VersionID", newVersion)
    
    ' D. 接続の再構築 (Re-linking)
    ' ここで保存しておいたコネクタ情報を元に、newShp と他のシェイプを再接続する
    ' ※ これを行わないと、図面がバラバラになる
    
    ' E. 旧シェイプの破棄
    oldShp.Delete
    
    Set PerformSafeReplacement = newShp
End Function

' --- ヘルパー関数群 (実務ではこれらを堅牢に実装する) ---

Private Function IsMasterDerived(ByVal shp As Visio.Shape) As Boolean
    ' マスターから生成されたシェイプかどうかを判定するロジック
    IsMasterDerived = shp.SectionExists(visSectionCustom, 0)
End Function

Private Function GetShapeDataValue(ByVal shp As Visio.Shape, ByVal propName As String) As String
    On Error Resume Next
    GetShapeDataValue = shp.Cells("Prop." & propName).Formula
    On Error GoTo 0
End Function

Private Sub SetShapeDataValue(ByVal shp As Visio.Shape, ByVal propName As String, ByVal value As String)
    ' Shape Dataがなければ追加し、あれば更新する
    shp.Cells("Prop." & propName).Formula = """" & value & """"
End Sub

Private Function GetLatestMasterVersion(ByVal stencil As Visio.Document) As String
    ' 本来はDBや外部ファイルを参照。ここではデモ用に固定値を返す
    GetLatestMasterVersion = "2.0"
End Function

4. アーキテクトの視点:実務における注意点

このロジックをプロダクション環境に投入する際、以下の3点に留意せよ。

1. コネクタの「接続点(Connection Point)」の不一致

マスターを更新した際、接続点の数や位置が変わることがある。単純に `ConnectTo` を行うだけでは、接続が「点」ではなく「図形の中心」に吸い寄せられてしまう。
対策: 旧シェイプのどの `ConnectionPointIndex` にコネクタが結合していたかを記録し、新シェイプの最も近いインデックスへ再割り当てするアルゴリズムを組み込むこと。

2. 外部データ(Excel/SQL)との同期

図面内のシェイプが、外部データベースのIDと紐付いている場合、置換によってその紐付け(Link)が切れるリスクがある。
対策: 置換プロセスを「Transaction」として扱い、データ移行に失敗した場合は `Application.Undo` を呼び出すか、一時的なバックアップファイルを生成する設計にせよ。

3. パフォーマンス・スケーラビリティ

数千個のシェイプを持つ大規模な図面でこの処理を行うと、VBAの実行速度は指数関数的に低下する。
対策:

  • `Application.ScreenUpdating = False` は絶対条件。
  • `Application.Calculation = xlCalculationManual` (Excel連携時) も検討。
  • 変更が必要なシェイプのみを事前にリストアップし、一括処理する「バッチ処理」的なアプローチを取ること。

結びに代えて

Visioの自動化における真の価値は、単に「図面を描く」ことではなく、「図面という資産を、常に最新かつ正確な状態に管理し続ける」ことにある。

マスターの更新を「手作業の苦行」にするか、「プログラムによる洗練された遷移」にするか。その差は、エンジニアがオブジェクトのライフサイクルとデータの整合性をどこまで深く理解しているかにかかっている。

この設計思想を、貴殿の次なるプロジェクトの礎としてほしい。

タイトルとURLをコピーしました