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

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

マクロの記録を卒業する。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は奥が深い世界です。しかし、オブジェクトの構造を正しく捉え、一つひとつの操作の「重み」を理解していれば、どんな複雑な要件も必ず突破できます。

さあ、恐れずにコードを書き進めてください。応援していますよ!

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