【入門編】Shape.NameIDの落とし穴:動的に生成・削除される図形の名前衝突を防ぐユニーク名管理術 – Visio VBA解析バイブル

スポンサーリンク

こんにちは!Visio VBAの自動化の世界へようこそ。
マクロの記録ボタンを押してコードを覗いてみたものの、「なんだか生成された図形の名前が毎回バラバラになる」「図形を消したり作ったりしているうちに、プログラムが意図しない図形を掴んでエラーを起こす……」そんな壁にぶ和れていませんか?

「図形を自動で綺麗に並べたい」「データを流し込んで大量のフローチャートを作りたい」と思ったとき、必ずと言っていいほど直面するのが『図形の名前(Name / NameID)の衝突と追跡のトラブル』です。

今回は、数々のVisio自動化システムを構築してきたシニアエンジニアの視点から、動的に生成・削除される図形を完全にコントロールし、名前の衝突を華麗にかわす「ユニーク名管理術」の極意を優しく、そして深く伝授します。

ここをクリアすれば、あなたのVisio VBAのスキルは「初心者」から確実に脱却できますよ!バッチリついてきてくださいね。

1. なぜVisioの図形名(NameID)でハマるのか?

まずは、Visioが裏側でどうやって図形を管理しているかを知る必要があります。ここがすべての落とし穴のスタート地点です。

「名前」の3つの顔を理解する

Visioの `Shape` オブジェクトには、主に3つの名前に関連するプロパティがあります。

1. `Name`: ユーザーインターフェース(画面左側の図形ウインドウなど)で見える名前(例: `プロセス`, `プロセス.3` など)。
2. `NameID`: プログラムから図形を一意に識別するための、Visio内部の文字列(例: `Sheet.1!Basic_Shapes:id:1` や、単に `Sheet.1`)。
3. `Text`: 図形の中に書き込まれている文字列。

私たちがVBAで「図形を生成・指定」するときによく使うのは `Name` や `NameID` ですが、ここに大きな罠があります。

罠の正体:デフォルト名前付与ルールの恐怖

Visioは、図形をドロップ(生成)するたびに、同じマスターシェイプであれば自動的にインクリメント(連番)する名前を付けます。

  • 1個目:`プロセス`
  • 2個目:`プロセス.1`
  • 3個目:`プロセス.2`

ここで恐ろしいのが、「途中で2個目の『プロセス.1』を削除したあとに、新しく図形を作るとどうなるか?」という点です。
Visioは空いた番号を再利用したり、ユニーク性を保とうと独自の採番を行うため、プログラム側で「さっき作ったあの図形をもう一度操作したい!」と思ったときに、名前が変わってしまったり、別の図形を指してしまったりするのです。これが名前の衝突と追跡ロストの正体です。

2. 解決策:Visioの「Master名」と「User セル」を活用したユニーク名管理

この混沌を制するためには、Visio任せの名前管理を卒業し、「自分で一意のID(キー)を図形に埋め込み、それを元にプログラムから探し出す」というアプローチを取ります。

プロの現場で使われている最も堅牢な手法は、図形のShapeSheet(内部プロパティ)の「User-defined Cells(ユーザー定義セル)」に独自のIDを書き込む方法です。

これなら、画面上の名前(`Name`)がどう変わろうとも、プログラム側からは「私のつけた固有IDを持つ図形はどれだ?」と確実に見つけ出すことができます。

3. 実践!動的生成と確実な追跡を行うVBAコード

百聞は一見にしかず。実際に「動的に図形を生成し、ユニークIDを付与し、後からそれを確実に呼び出して色を変える」という一連の処理を書いてみましょう。

以下のコードを、VisioのVBAエディタ(Alt + F11)の標準モジュールに貼り付けて実行してみてください。

Sub Demo_UniqueShapeManagement()
Dim vsoPage As Visio.Page
Set vsoPage = ActivePage

‘ 1. テスト用に新しい図形を動的に3つ生成し、ユニークIDを埋め込む
Dim i As Long
Dim targetID As String
targetID = “MyNode_B” ‘ 後から追跡したい特定のID

For i = 1 To 3
‘ 基本的な四角形(Rectangle)のマスターを取得してドロップ
Dim vsoMaster As Visio.Master
Set vsoMaster = Visio.Documents.Item(“basic_u.vssx”).Masters.ItemBypyName(“Rectangle”) ‘ ※環境に合わせてマスタ名は調整してください
‘ ※簡略化のため、今回はActivePageに単純な矩形を描画する例にします

Dim vsoShape As Visio.Shape
‘ 簡易的に長方形をドロップ(位置はX, Yで指定)
Set vsoShape = vsoPage.DrawRectangle(i 1, 5, i 1 + 1.5, 4)

‘ 【重要】独自のユニークIDを組み立てる
Dim uniqueKey As String
uniqueKey = “Node_” & i

‘ 図形にカスタムプロパティ(Userセルの行)を追加してユニークIDを書き込む
‘ これによりVisioの標準「Name」に依存しない絶対的な追跡が可能になります
Call SetCustomID(vsoShape, uniqueKey)

‘ ついでに見分けがつくようにテキストを入れる
vsoShape.Text = “ID: ” & uniqueKey

Debug.Print “生成完了: ” & uniqueKey & ” (Visio内部名: ” & vsoShape.Name & “)”
Next i

‘ 2. (ここで図形が削除されたり、順番が入れ替わったりしたと仮定)
‘ Visioの名前はあてにならないが、私たちが埋め込んだIDから「特定の図形」をピンポイントで探す!

MsgBox “図形を生成しました。これからユニークID ‘Node_B’ を持つ図形を探して赤色に変えます。”, vbInformation, “実演”

Dim foundShape As Visio.Shape
Set foundShape = FindShapeByCustomID(vsoPage, “Node_2”) ‘ Node_2 を探す

If Not foundShape Is Nothing Then
‘ 見つかった!色を赤に変更する(CellsSRCで塗りつぶし色を変更)
foundShape.CellsSRC(visSectionObject, visRowFill, visFillForegnd).ResultIU = RGB(255, 0, 0)
MsgBox “お見事! ‘Node_2’(Visio名が変わっていてもOK)を特定し、赤色に変更しました。”, vbInformation, “成功”
Else
MsgBox “指定されたIDの図形が見つかりませんでした。”, vbCritical, “エラー”
End If

End Sub

‘ ==========================================
‘ 補助関数1: 図形にカスタムのユニークID(Userセル)を刻み込む
‘ ==========================================
Private Sub SetCustomID(vsoShape As Visio.Shape, idString As String)
Const propName As String = “MyUniqueKey”

‘ すでにUserセクションに同名の行がないか確認し、なければ追加
If Not vsoShape.CellExistsU(“User.” & propName, visExistsLocally) Then
vsoShape.AddNamedRow visSectionUser, propName, visTagDefault
End If

‘ 値としてユニーク文字列を格納(シングルクォーテーションで囲む)
vsoShape.CellsU(“User.” & propName).FormulaU = “””” & idString & “”””
End Sub

‘ ==========================================
‘ 補助関数2: ページ内を走査し、カスタムIDが一致する図形を返す(追跡の決定版)
‘ ==========================================
Private Function FindShapeByCustomID(vsoPage As Visio.Page, targetID As String) As Visio.Shape
Const propName As String = “MyUniqueKey”
Dim shp As Visio.Shape

For Each shp in vsoPage.Shapes
‘ ユーザー定義セルが存在し、かつ値が一致するかチェック
If shp.CellExistsU(“User.” & propName, visExistsLocally) Then
‘ CellsUの値を取得(前後につくダブルクォーテーションを取り除く)
Dim currentID As String
currentID = shp.CellsU(“User.” & propName).ResultStr(“”)

If currentID = targetID Then
Set FindShapeByCustomID = shp
Exit Function
End If
End If
Next shp

‘ 見つからなければNothingを返す
Set FindShapeByCustomID = Nothing
End Function

4. コードの解説と「エンジニアの知見」

このコードの肝は、Visioが自動的につける `Name` や `NameID` を一切信用しないという点にあります。

なぜ `CellsSRC` や `User` セルを使うのか?

Visioの図形は、Excelでいうセルのような数式構造(ShapeSheet)を持っています。その中に `User` という、ユーザーが自由に使えるカスタム領域(変数置き場のようなもの)が存在します。

ここに `MyUniqueKey` という名前の行を作り、プログラム側で決めたID(例: `Node_2`)を書き込んでおきます。
こうすることで、ユーザーが画面上で図形を移動させようが、別の図形を間に挟んで自動採番名が変わろうが、図形が持つ「戸籍(Userセルの値)」は一生変わりません。

検索する際も、ページ内の全図形(`vsoPage.Shapes`)をループさせて、「私の戸籍IDはこれですか?」と1つずつ確認していくため、名前の衝突による誤作動を100%防ぐことができます。

まとめ:名前衝突の恐怖から解放されるために

今回は、Visio VBAにおける図形名の衝突を防ぎ、確実に図形を追跡するユニーク名管理術を解説しました。

  • Visioのデフォルト名(`Name` / `NameID`)は自動で変わるため、プログラムのキーとして使ってはならない。
  • 図形のShapeSheet(Userセル)に独自のIDを書き込み、それを「戸籍」として利用する。
  • 検索時はページ内の図形をループし、Userセルの値でマッチングを行う。

このテクニックを身につければ、数千個の図形を動的に生成・削除・更新するような大規模な自動化ツールを作っても、エラーに怯えることはもうなくなります。

「ここをクリアすれば、Visio VBAの基本はバッチリですよ!」
ぜひ次の開発案件や業務効率化のマクロに、この設計を取り入れてみてください。あなたのVBAライフが、より堅牢でスマートなものになることを応援しています!

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