こんにちは! Visio VBAの世界へようこそ。
マクロの記録ボタンを押すだけの自動化から一歩抜け出して、「自分の手でVisioのオブジェクトを完全にコントロールしたい」と願うあなたなら、きっとこのテーマにワクワクするはずです。
今回は、Visioの隠れた(そして極めて強力な)機能である「リストコンテナ(List Container)」、そしてその中の要素の並び順をVBAで自在に操る`SetListMemberState`メソッドについて、徹底的に解説していきます。
「組織図のメンバーの順番を自動で並び替える」「フローチャートのステップの間に新しい処理を割り込ませる」――そんな現場の要望をスマートに解決する極意を、一緒にマスターしていきましょう!
—
1. Visioの「リストコンテナ」とは何か?(基礎の確認)
普通の「四角形や丸を並べた図形」と違い、Visioのコンテナ(特にリストとして定義されたもの)は、中に含まれるシェイプ(メンバー)の順序(インデックス)を厳密に管理しています。
例えば、タスク管理ボードやレーン、あるいは縦に並んだプロセスリストを思い浮かべてみてください。一番上が「1番目」、その下が「2番目」……というように、順番に意味がありますよね。
GUI操作であれば、リスト内のシェイプをマウスでドラッグ&ドロップして順番を変えられますが、これをVBAでやろうとすると、単に座標(X/Yプロパティ)をいじるだけではダメなんです。座標を強引に変えても、リストの構造ルールによって元の位置にスナップバックされてしまいます。
そこで登場するのが、リストの順序を司る専用の仕組みです。
—
2. 順序制御の切り札:`SetListMemberState` とは?
リストコンテナ内のシェイプの順序をプログラムから変更したいとき、私たちエンジニアが頼るべき唯一にして最強のメソッドが `SetListMemberState` です。
まずは、このメソッドの正体をざっくり掴みましょう。
- 誰が持っているメソッドか?:`Page` オブジェクト(または `Master` オブジェクト)
- 何をするものか?:指定したコンテナ(List)に対して、指定したメンバー(Shape)をどの位置(インデックス)に、どのような状態で組み込むかを指示する。
呪文の文法
object.SetListMemberState (ContainerShape, ObjectToProcess, ListPosition)
- `ContainerShape`:リストコンテナ自体のシェイプ
- `ObjectToProcess`:操作対象となるメンバーシェイプ
- `ListPosition`:移動先・挿入先のインデックス(1始まり)、または特殊な定数
ここがVisio VBAの面白いところであり、少し気をつけなければならないポイントです。配列のインデックスが `0` から始まる言語が多い中、Visioのシェイプインデックスやリストポジションは基本的に `1` から始まる ので注意してくださいね。
—
3. 実践!リスト内のシェイプを自在に並び替えるVBAコード
百聞は一見にしかず。実際に動くコードを見てみましょう。
このコードは、アクティブページ上にある特定のリストコンテナを指定し、その中にある特定のメンバーを「一番上(インデックス1)」に移動させるサンプルです。
Sub MoveShapeInListSample()
Dim vsoPage As Visio.Page
Set vsoPage = ActivePage
Dim vsoContainerShape As Visio.Shape
Dim vsoMemberShape As Visio.Shape
‘ 【前提】あらかじめページ上に配置されているシェイプのIDを指定して取得します
‘ ※実際の開発では、シェイプ名やテキストで検索して取得すると実用的です
On Error Resume Next
Set vsoContainerShape = vsoPage.Shapes.ItemU(1) ‘ 例:1番目をコンテナとする
Set vsoMemberShape = vsoPage.Shapes.ItemU(3) ‘ 例:3番目を移動対象とする
On Error GoTo 0
If vsoContainerShape Is Nothing Or vsoMemberShape Is Nothing Then
MsgBox “対象のシェイプが見つかりません。”, vbCritical
Exit Sub
End If
‘ —————————————————-
‘ SetListMemberState を使った順序変更の核心部分
‘ —————————————————-
‘ 第3引数に移動先のインデックス(ここでは 1 = 最上位)を指定します
vsoPage.SetListMemberState vsoContainerShape, vsoMemberShape, 1
MsgBox “シェイプをリストの先頭に移動しました!”, vbInformation
End Sub
コードのここがポイント!
- `SetListMemberState` を呼び出すだけで、Visioのエンジンが自動的にコンテナのレイアウト(縦方向なら上から下への整列など)を再計算し、アニメーション的(または瞬時に)にレイアウトを再構築してくれます。
- 開発者が面倒な座標計算(Y座標をいくつにして……といった処理)を一切しなくてよいのが、このメソッドの最大のメリットです。
—
4. 現場でありがち!陥りやすい罠とエラー回避の知見
ここからは、チーフアーキテクトとして現場でよく見る「ハマりどころ」をいくつか伝授します。ここを知っているだけで、デバッグにかける時間が何時間も節約できますよ。
罠その1:そもそもそのシェイプは「リスト」なのか?
Visioには「普通のグループ」「通常のコンテナ」「リストコンテナ」の3種類が存在します。見た目が枠で囲まれていても、それが「リスト」属性を持っていない場合、`SetListMemberState` は容赦なく実行時エラー(エラー番号:-20321 など)を吐きます。
【対策】
操作する前に、シェイプがリストコンテナかどうかを判定する防御的コードを書きましょう。
‘ シェイプがリストコンテナかどうかを判定する簡易チェック
Public Function IsListContainer(vsoShape As Visio.Shape) As Boolean
‘ VisioのCellからコンテナ・リストの種別を判定(ContainerPropertiesを使用)
If vsoShape.ContainerProperties Is Nothing Then
IsListContainer = False
Else
‘ ListDirectionセルが存在するか等でリスト判定も可能
IsListContainer = True ‘ 簡易的な例
End If
End Function
罠その2:インデックスの範囲外指定
リスト内に現在「3つ」しかメンバーがいないのに、インデックス「10」を指定して移動させようとすると、これもエラーになるか、あるいはリストの末尾に丸め込まれて意図しない挙動になります。
【対策】
現在のメンバー数を `ContainerProperties` から取得し、その範囲内に収まるようにインデックスを制御するのがプロの技です。
Dim memberCount As Long
‘ リストに含まれるメンバー数を取得
memberCount = vsoContainerShape.ContainerProperties.GetMemberShapes(visContainerMemberType捞Derived).Count
—
5. まとめ:ここをクリアすれば、Visio VBAはもう怖くない!
今回は、リストコンテナ内シェイプの順序操作という、一歩進んだVisio VBAのテクニックをご紹介しました。
- リスト構造の順序変更は座標操作ではなく `Page.SetListMemberState` を使う
- インデックスは `1` から始まる
- 対象が本当にリストコンテナかを確認する防御的コードを入れる
この3つさえ押さえておけば、どれほど複雑なダイアグラムの自動生成・整理であっても、あなたの思いのままにコントロールできるようになります。
「マクロの記録」の殻を破ったあなたなら、このコードをベースに業務効率化ツールをガンガン作れるはずです。
Visio VBAの世界を、ぜひ心ゆくまで楽しんでくださいね!
