【入門編】Shape.IndexとZ-Orderの関係解析:重なり順の正確な数値制御とダイレクト並べ替え – Visio VBA解析バイブル

スポンサーリンク

こんにちは!Visio VBAの奥深い世界へようこそ。
今回は、多くの開発者が一度はハマる「図形の重なり順(Z-Order)」と、その裏で静かにうごめく`Shape.Index`の本当の関係について徹底的に解き明かしていきます。

「マクロの記録」を使うと、図形を一番上に持ってくる操作は `BringToFront` というメソッドで記録されますよね。
でも、複雑な図面をプログラムで自動生成したり整理したりする時、この「相対的な移動操作」だけでは、まるでコントロールが効かなくなってイライラした経験はありませんか?

「上へ」「下へ」というフワッとした指示ではなく、「上から〇番目」「インデックス番号〇番」と、正確な数値でレイヤーを支配するダイレクト並べ替えの極意を、先輩エンジニアの私と一緒にマスターしていきましょう!ここをクリアすれば、あなたのVisio VBAスキルは確実にプロの領域へ到達しますよ。

1. そもそも `Shape.Index` とは何か?(基本のキ)

Visioのページ(Pageオブジェクト)の中には、たくさんの図形(Shapeオブジェクト)が存在しています。
このページ内の図形たちを管理するために、Visioは自動的に1から始まる番号を割り振っています。これが `Shape.Index`(インデックス) です。

‘ ページの最初(インデックス1)にある図形の名前イミディエイトウィンドウに出力する
Debug.Print ActivePage.Shapes(1).Name

「おっ、じゃあ配列のインデックスみたいなものか!」と思ったそこのあなた。半分正解で、半分は大間違いです。
ここがVisio VBAの最も罠に嵌りやすいポイントなのですが、Visioの `Shape.Index` は、図形がキャンバスに描かれた「重なり順(Z-Order)」と完全に同期しているのです。

重なり順とインデックスの残酷な関係

VisioのZ-Orderには、独特のルールがあります。

  • インデックスの若い番号(1に近い方):画面の一番奥(最背面)に描画される。
  • インデックスの大きい番号(Shapes.Countに近い方):画面の一番手前(最前面)に描画される。

つまり、`Shape.Index` を操作するということは、そのまま図形の重なり順を数値で直接コントロールすると同義なのです。

2. なぜ `BringToFront` では現場で通用しないのか?

実務で大量の図形を扱う自動化ツールを作るとき、こんな要件に出くわします。

> 「データベースから読み込んだID順に、必ず『背景 < 枠線 < アイコン < テキスト』の順序で正確に重ね合わせたい」

これを `BringToFront` や `SendToBack` などの相対操作でやろうとすると、どうなるでしょうか?
「現在の状態から何回手前に持ってくるか」を毎回計算しなければならず、処理が重くなるうえに、少しでも手動で触られると順番が盛大に狂い始めます。バグの温床ですね。

だからこそ、「目的の図形を、狙ったインデックスの位置へダイレクトに配置する」 という数値アプローチが必要不可欠なのです。

3. 【実践】Z-Orderを数値で完全制御するVBAコード

お待たせしました。ここからは、現場でそのままコピペして使える実用的なコードをお見せします。

今回のミッションは、「選択している図形、あるいは特定の条件に合致する図形を、一発で最背面(Index = 1)に固定する」 ロジックです。

Sub ForceSetToBack()
Dim vsoPage As Visio.Page
Dim vsoShape As Visio.Shape

‘ アクティブページを取得
Set vsoPage = ActivePage

‘ エラーハンドリングの準備
On Error GoTo ErrorHandler

‘ 何か図形が選択されているかチェック
If ActiveWindow.Selection.Count = 0 Then
MsgBox “最背面へ移動させたい図形を選択してください。”, vbExclamation, “通知”
Exit Sub
End If

‘ 選択中の最初の図形を取得
Set vsoShape = ActiveWindow.Selection(1)

‘ 【核心】VisioのZ-Orderをダイレクトに変更するメソッド
‘ 引数に 1 を指定することで、強制的に最背面(Index: 1)へワープさせます
vsoShape.BringToLayer 0 ‘ ※レイヤー操作とは別概念です。

‘ 正確には、VisioのCOMオブジェクトモデルではインデックスの直接代入(vsoShape.Index = 1 など)はできません。
‘ その代わり、OrderPosition プロパティを使います!
vsoShape.OrderPosition = 1 ‘ 1 = visZOrderBottom (最背面)

MsgBox “図形「” & vsoShape.Name & “」を最背面(Index: 1)に固定しました。”, vbInformation, “成功”

Exit Sub

ErrorHandler:
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical, “エラー”
End Sub

コードの重要ポイント:`OrderPosition` プロパティ

お気づきになったでしょうか?
実はVisioのオブジェクトモデルには、`Shape.Index = 3` のようにインデックス番号を直接イコールで書き換えるプロパティは存在しません。(これをやろうとすると実行時エラーになります)

代わりに用意されているのが、`OrderPosition` プロパティです。
これを使うことで、その図形を「上から(あるいは下から)何番目のZ-Orderに置くか」をダイレクトに指定できるのです。

4. 陥りやすい罠とエンジニアの知見

ここで、現場のエンジニアが必ず直面する「罠」を2つ共有しておきます。これを覚えておくだけで、無駄な残業を何時間も回避できますよ。

罠その1:ループ処理中にインデックスがズレる恐怖

複数の図形を順番に並べ替えるとき、`For i = 1 To Shapes.Count` のようなループを回しながら `OrderPosition` を変更すると、図形が移動した瞬間に他の図形のインデックス番号が自動的に振り直されます。

‘ 【やってはいけないアンチパターン】
Dim i As Long
For i = 1 To ActivePage.Shapes.Count
‘ ループ途中でインデックスが変わるため、処理がめちゃくちゃになります
ActivePage.Shapes(i).OrderPosition = i
Next i

【回避策】
複数の図形を意図した順番に並べ替える場合は、一度対象の図形を配列やコレクションに格納してから、「逆順(手前から奥、または奥から手前)」に処理を流し込むか、専用のソートアルゴリズムを組む必要があります。

罠その2:グループ化された図形(Group)の落とし穴

Visioの図形がグループ化されている場合、

  • グループ自体のページ内でのインデックス
  • グループ「の中」にある子図形のインデックス

この2つは全く別のレイヤー空間として管理されています。子図形のZ-Orderを操作したい場合は、必ず `vsoShape.Shapes` コレクションを経由してアクセスしなければなりません。

まとめ

今回は、`Shape.Index` と Z-Order の切っても切れない関係、そして `OrderPosition` を使った正確な数値制御の方法について解説しました。

  • `Shape.Index` は図形の描画順序(Z-Order)と完全にリンクしている。
  • インデックスの若い方が「奥」、大きい方が「手前」。
  • インデックスの直接代入はできないが、`OrderPosition` プロパティを使えばダイレクトな数値制御が可能。
  • ループ処理中のインデックスの変動に注意する。

「マクロの記録」の呪縛を解き放ち、こうしたオブジェクトモデルの本質を理解できるようになると、Visioで作れない自動図面はないと言っても過言ではありません。

ここをクリアしたあなたなら、もうVisio VBAの基礎はバッチリです!
ぜひ明日の業務から、このテクニックを組み込んでスマートな自動化を実現してみてくださいね。それでは、また次回のハードコアな解説でお会いしましょう!

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