こんにちは、若きエンジニアの皆さん。Visio VBAの世界へようこそ。
私は長年、複雑なインフラ設計や大規模なシステム構成図を自動生成する仕組みを構築してきたチーフアーキテクトです。
皆さんは、マクロの記録から一歩踏み出し、「もっと速く、もっと大量のデータを処理したい」と思い始めたところでしょうか。素晴らしい向上心です。
今日は、初心者の方が必ずぶつかる「図面が大きくなるとマクロが極端に重くなる」という壁を、鮮やかに突破する秘技を伝授しましょう。テーマは、数万個の図形を高速にさばくための「Page.GetIDs」と「配列プロセッシング」です。
ここをマスターすれば、Visio VBAの基礎、そして応用への道は一気に開けますよ。
—
1. Visioの「オブジェクトモデル」を正しく理解する
コードを書く前に、私たちが操作する「Visioの世界のルール」を整理しましょう。Visioの構造は、マトリョーシカのような階層構造になっています。
1. Application: Visioというソフトそのもの。
2. Document: 開いているファイル(.vsdxなど)。
3. Page: 図面の中の「ページ」。
4. Shape: ページの上に置かれた「図形」。
初心者のうちは、ページ内の図形を処理するときに、以下のようなコードを書きがちです。
‘ よくある「遅い」書き方の例
Dim shp As Visio.Shape
For Each shp In ActivePage.Shapes
‘ ここで何か処理をする
Next shp
実は、図形が100個程度ならこれでも問題ありません。しかし、図形が数千、数万と増えたとき、この「For Each」は牙を剥きます。
なぜなら、VBAからVisioの「Shapeオブジェクト」にアクセスするたびに、メモリ上では非常に重い通信(プロセス間通信)が発生しているからです。1万回もこの「重いドア」を開け閉めしていたら、処理が終わらないのも無理はありません。
—
2. 救世主「Page.GetIDs」:住所リストを一括取得する
そこで登場するのが、今回の主役 `Page.GetIDs` メソッドです。
このメソッドの凄さは、「図形そのもの(重い物体)」を持ってくるのではなく、「図形のID(軽い数字)」のリストだけを、一瞬で配列(メモリ上のリスト)として取得する点にあります。
例えるなら、1万軒の家を1軒ずつ訪問して名前を聞いて回るのではなく、役所で「全住民の住所録(ID)」を1枚の紙でもらってくるようなものです。どちらが速いかは、明白ですよね。
Page.GetIDs の基本構文
‘ IDを格納するための動的配列を宣言
Dim shapeIDs() As Long
‘ ページ内の全図形IDを配列に一括取得
‘ visGetIDsNone は「フィルタリングなし(全部)」という意味の定数です
ActivePage.GetIDs visGetIDsNone, shapeIDs
—
3. 実践:数万個の図形を光速で処理するアルゴリズム
では、実際にこのID配列を使って、図形を効率的にループ処理するコードを書いてみましょう。
Public Sub FastShapeProcessing()
Dim targetPage As Visio.Page
Set targetPage = ActivePage ‘ 現在表示されているページを対象にする
‘ 1. IDを格納するための長整数型(Long)の配列を準備
Dim idArray() As Long
‘ 2. GetIDsメソッドで、ページ内の全図形IDを配列に流し込む
‘ これだけで、メモリ上に「IDのリスト」が構築されます
On Error Resume Next ‘ 図形がゼロの場合のエラー回避
targetPage.GetIDs visGetIDsNone, idArray
On Error GoTo 0
‘ 配列が空(図形がない)の場合は終了
If (Not idArray) = -1 Then
MsgBox “図形が見つかりません。”
Exit Sub
End If
‘ 3. 配列をループ処理する(ここが高速化のキモ!)
Dim i As Long
Dim currentID As Long
Dim targetShape As Visio.Shape
‘ 配列の最小要素(LBound)から最大要素(UBound)まで回す
For i = LBound(idArray) To UBound(idArray)
‘ 配列からIDを取り出す
currentID = idArray(i)
‘ IDを使って、必要な時だけShapeオブジェクトにアクセスする
‘ ItemFromIDは、インデックス番号で指定するより高速です
Set targetShape = targetPage.Shapes.ItemFromID(currentID)
‘ — ここに図形への処理を書く —
‘ 例:図形の色を少し変える、テキストを読み取るなど
‘ Debug.Print targetShape.NameID & ” を処理中…”
‘ —————————-
Next i
MsgBox UBound(idArray) + 1 & ” 個の図形を高速にスキャンしました!”
End Sub
—
4. なぜこの方法が「プロフェッショナル」なのか
このコードには、初心者が脱却すべき3つのポイントが詰まっています。
① インデックスではなくIDを使う
`Shapes(1)`, `Shapes(2)` という指定(インデックス指定)は、Visio内部で「1番目はどれだっけ?」と毎回探し直すため、図形が増えると指数関数的に遅くなります。一方、`ItemFromID` は、IDという背番号で直接指名するため、常に最速でアクセスできます。
② メモリ効率の最大化
`Shape` オブジェクトを大量に変数に保持するとメモリを圧迫しますが、`Long` 型(数字)の配列であれば、数万個あっても消費メモリはごくわずかです。
③ 柔軟なフィルタリング
今回は `visGetIDsNone` を使いましたが、ここを書き換えるだけで「選択されている図形だけ」「特定のレイヤーの図形だけ」といった抽出も一瞬で行えます。
—
5. 陥りやすいエラーと対策
この手法を使いこなすために、1点だけ注意が必要です。
- 「配列が空」の判定:
ページに図形が一つもない場合、`idArray` は初期化されず、`UBound` を取ろうとするとエラーになります。上記のサンプルコードのように、図形が存在するかどうかをチェックするロジック(`On Error` や配列の状態チェック)を入れるのが、現場で動く「堅牢なコード」のコツです。
—
結び:ここをクリアすれば、Visio VBAの基本はバッチリです
「図形を一つずつ触る」のではなく、「IDのリストを配列として一気に扱う」。
この考え方は、Visio VBAに限らず、Excel VBAやあらゆるプログラミングに通ずる「計算コストの意識」そのものです。
最初は少し難しく感じるかもしれませんが、この `GetIDs` を使いこなせるようになったとき、あなたは「マクロを書ける人」から「システムを構築できるエンジニア」へと進化しています。
大容量の図面を、まるで魔法のように一瞬で処理する快感をぜひ味わってください。
もし途中で迷ったら、いつでもこの「基本」に立ち返ってくださいね。応援していますよ!
