【実務・中級編】Shapeオブジェクトの識別子マスター:ID・Name・NameUの違いと使い分けの鉄則 – Visio VBA解析バイブル

スポンサーリンク

Visio VBAを掌握する極限の知見:Shapeオブジェクト識別子マスター(ID・Name・NameU)

業務自動化の現場において、Visio VBAの成否を分ける最大の分水嶺は「図形の正確な特定」にある。

ドキュメント内に散らばる何百、何千ものシェイプ。その中から目的のターゲットを迷いなく捕らえ、データを流し込み、あるいは動的にレイアウトを再構築する――。この基本動作が揺らぐようでは、いかに高度なロジックを組んでも、実務に耐えうる堅牢なツールとは言えない。

特に開発者を長年悩ませてきたのが、ID、Name、NameUという3つの識別子の存在だ。これらをなんとなく使い分けているうちは、プロのエンジニアとは言えない。図面のコピー、マルチリンガル環境(日本語版・英語版の混在)、あるいはユーザーによる手動での図形リネーム。これらすべての変化に耐え、「絶対に壊れない」図形取得ロジックをどう構築すべきか。

今回は、Visioオブジェクトモデルの深層を知り尽くしたアーキテクトの視点から、識別子選定の鉄則とプロダクションコードを授けよう。

1. 3つの識別子の正体と、その「重み」を知る

まずは、それぞれの仕様とライフサイクルにおける挙動を正確に把握する。

① ID(`Shape.ID`)

  • 定義: 図面ページ(Page)内におけるシェイプの生成順に割り振られる一意の整数。
  • ライフサイクル: 非常にはかない。図形を削除・新規作成すると再割り当てされる。また、図形のコピー&ペーストでも変化する。
  • 評価: プログラムの「実行中の一時的なコンテキスト」以外で永続的なキーとして使ってはならない。 ただし、同一トランザクション内(ループ処理など)での高速アクセスの主役としては最も優れている。

② ローカル名(`Shape.Name`)

  • 定義: VisioのUI上で表示されるシェイプの名前(例: `”プロセス.12″`, `”長方形”`)。
  • ライフサイクル: ユーザーがGUIから自由に変更可能。さらに、Visioのインストール言語に強く依存する(日本語環境では `”プロセス”`、英語環境では `”Process”` になる)。
  • 評価: 実務の自動化において、コード内にハードコーディングしてはならない諸悪の根源。 多言語展開や保守フェーズで確実にバグを生む。

③ ユニバーサル名(`Shape.NameU`)

  • 定義: 言語に依存しない、Visio内部の不変のマスター識別子(例: `”Process”`, `”Sheet.1″`)。
  • ライフサイクル: UIの表示言語が変わろうとも不変。開発者が明示的に変更しない限り、オブジェクトの生存期間中は一貫性を保つ。
  • 評価: 外部データベースや設定ファイルとの連携、マスターステンシル由来の図形を特定する際の「最強のカード」。 これを使いこなせるかどうかが、プロとアマの境界線だ。

2. 【実務の鉄則】なぜそのコードは動かなくなるのか?

多くの初学者がやりがちなアンチパターンを見てみよう。

‘ 【アンチパターン】絶対にやってはいけない例
Dim shp As Visio.Shape
Set shp = ActivePage.Shapes(“プロセス”) ‘ ローカル名依存:英語版Visioで即死する

‘ 【アンチパターン】IDを固定値で決め打ちする例
Dim shp As Visio.Shape
Set shp = ActivePage.Shapes.ItemU(15) ‘ IDは図形の削除やコピペで容易に変わる

これらは、開発環境では動いたとしても、運用フェーズや別環境(海外拠点など)に展開した瞬間に崩壊する。

堅牢な設計のための2大原則

1. コード内のロジック指定には必ず `NameU`(またはカスタムプロップ)を使用する。
2. 外部連携やユーザー定義の識別には、ShapeSheetの「User-defined Cells(ユーザー定義セル)」を併用する。

3. 【プロダクションコード】壊れない図形取得・操作エンジンの実装例

ここからは、実務でそのままコピー&ペーストして使える、堅牢で洗練されたVBAモジュールを提示する。

このコードでは、以下の要件を満たしている:

  • ローカル名に頼らず、`NameU` をベースにシェイプを安全に検索・取得する。
  • 取得したシェイプに対し、安全にデータ(カスタムプロパティ)を書き込む。
  • エラーハンドリングを徹底し、想定外の図形欠損時にも沈黙せずログを残す。

Option Explicit

‘ ==============================================================================
‘ モジュール名: modShapeController
‘ 概要: 堅牢な識別子(NameU)を用いたシェイプ操作の標準モジュール
‘ ==============================================================================

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

‘ 処理対象のユニバーサル名(多言語対応のため必ずNameUベースの識別子を指定)
Const TARGET_NAME_U As String = “Process”

Dim targetShp As Visio.Shape

On Error GoTo ErrorHandler

‘ 1. NameU を用いて安全にシェイプを特定・取得する
Set targetShp = GetShapeByNameU(vsoPage, TARGET_NAME_U)

If targetShp Is Nothing Then
MsgBox “指定されたシェイプ (” & TARGET_NAME_U & “) がアクティブページに見つかりません。”, vbCritical, “処理中断”
Exit Sub
End If

‘ 2. 特定したシェイプに対する堅牢なプロパティ操作
‘ ※ここではShapeSheetのカスタムプロパティ(Prop.Cost等)への書き込みを想定
Call SetShapeProperty(targetShp, “Prop.Cost”, “150000”)
Call SetShapeProperty(targetShp, “Prop.Status”, “Approved”)

MsgBox “シェイプの自動処理が正常に完了しました。”, vbInformation, “成功”
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “システムエラー”
End Sub

‘ ==============================================================================
‘ 汎用関数: NameU(ユニバーサル名)でPage内のシェイプを安全に検索する
‘ ==============================================================================
Private Function GetShapeByNameU(vsoPage As Visio.Page, targetNameU As String) As Visio.Shape
Dim shp As Visio.Shape
Dim foundShp As Visio.Shape
Set foundShp = Nothing

‘ ページの全シェイプを走査(グループ化された子シェイプも考慮する場合は再帰処理が必要だが、今回はトップレベルを対象)
For Each shp in vsoPage.Shapes
‘ NameUの前方一致、または完全一致で比較(Visio内部のNameUは “Process.34” のように連番がつくことがあるため注意)
‘ 完全一致か、ドットを除いたベース名で比較するロジックを推奨
If GetBaseNameU(shp.NameU) = targetNameU Then
Set foundShp = shp
Exit For
End If
Next shp

Set GetShapeByNameU = foundShp
End Function

‘ ==============================================================================
‘ 補助関数: Visioが自動付与する連番サフィックス(例: .12)を除外したベース名を取得
‘ ==============================================================================
Private Function GetBaseNameU(ByVal rawNameU As String) As String
Dim dotPos As Long
dotPos = InStr(rawNameU, “.”)
If dotPos > 0 Then
GetBaseNameU = Left(rawNameU, dotPos – 1)
Else
GetBaseNameU = rawNameU
End If
End Function

‘ ==============================================================================
‘ 汎用プロシージャ: シェイプのカスタムプロパティに安全に値を書き込む
‘ ==============================================================================
Private Sub SetShapeProperty(shp As Visio.Shape, propName As String, propValue As String)
On Error GoTo PropError

‘ セルが存在するか確認しつつ値をセット
If shp.CellExistsU(propName & “.Value”, VisExistsFlags.visExistsAnywhere) Then
shp.CellsU(propName & “.Value”).FormulaU = “””” & propValue & “”””
Else
‘ プロパティが存在しない場合のフォールバック(必要に応じて設計)
Debug.Print “Warning: プロパティ ” & propName & ” は存在しません。”
End If
Exit Sub

PropError:
Debug.Print “Error setting property ” & propName & “: ” & Err.Description
End Sub

4. データベース・外部ファイル連携におけるアーキテクチャの極意

業務システム(ERPやDB)とVisioを連携させる際、図形と外部データを紐付けるキーとして `NameU` や `ID` だけでは不十分なケースがある。ユーザーが図形を新しく追加・複製した際、Visio側が自動採番する `NameU`(例: `Process.56`)は予測できないからだ。

ここで真価を発揮するのが、「ShapeSheetのUser定義セルへの固有ID(GUID等)の埋め込み」である。

1. 初期配置・マスタ作成時:
プログラム、あるいはステンシル側であらかじめ `User.BusinessID` などのカスタムセルを用意しておく。
2. 外部連携時:
Visio上のシェイプをスキャンする際、`NameU` ではなく `User.BusinessID` の値をキーとしてデータベースレコードを引く。

‘ 外部DB連携時のキー取得イメージ
Dim businessId As String
If shp.CellExistsU(“User.BusinessID”, visExistsAnywhere) Then
businessId = shp.CellsU(“User.BusinessID.ResultStr(visNoUnits)”)
‘ この businessId をもとにDBから最新情報を取得する
End If

この設計を取り入れることで、図形がどこに移動しようが、名前が書き換えられようが、外部データとのリンクが切れることは一生なくなる。

総括

Visio VBAにおけるオブジェクトの特定は、単なる「検索」ではなく、システムの寿命を左右するアーキテクチャの根幹である。

  • IDは一瞬のスピードのために使い捨てろ。
  • ローカル名(Name)は多言語の罠があるためコードに書くな。
  • ユニバーサル名(NameU)User定義セルを組み合わせ、環境変化に怯えない堅牢なコードを組み上げろ。

この知見を実装に落とし込んだ瞬間から、あなたの作るVisio自動化ツールは、現場の信頼を勝ち取る「真のプロダクト」へと昇華するはずだ。

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