こんにちは!Visio VBAの自動化の世界へようこそ。
マクロの記録から一歩踏み出し、「もっと実務で使える、高速でスマートなプログラムを書きたい!」と思っているあなたへ。
今回は、Visio VBAにおいて「数千個のシェイプを扱うなら絶対に避けて通れない最強の技術」、`GetResults` と `SetResults` メソッドを使った配列一括転送について解説します。
ここをクリアすれば、あなたの書くコードは見違えるほど高速になり、処理が終わるのをコーヒーを飲みながら何分も待つような苦行から解放されますよ。さあ、一緒に本質をマスターしていきましょう!
—
ぜんぜん終わらない?個別アクセスの「重すぎる罠」
まずは、よくある初心者の失敗パターンからお話しします。
例えば、図面内にある2,000個のシェイプから、それぞれの「幅 (Width)」と「高さ (Height)」のデータを取得して、何か処理をしたいとします。
素直なプログラミング初心者は、こんなコードを書きたくなります。
‘ 【やってはいけない遅延コードの例】
Dim shp As Visio.Shape
Dim w As Double, h As Double
For Each shp In ActivePage.Shapes
‘ 個別にセルへアクセスして取得
w = shp.Cells(“Width”).ResultIU
h = shp.Cells(“Height”).ResultIU
‘ ここになんらかの処理
‘ …
Next shp
一見、何の問題もなさそうに見えますよね?
しかし、このコードを数千個のシェイプを持つ巨大な図面で実行すると……ものすごく時間がかかります。 下手をすると数分間固まることもあるでしょう。
なぜ遅いのか?(VBAとVisioの「見えない壁」)
理由は、VBAの実行環境(COMクライアント)と、Visioの描画エンジン(COMサーバー)の間の「通信コスト」にあります。
`shp.Cells(“Width”)` と書くたびに、VBAはVisioのエンジンに対して「おい、幅のセルをくれ!」と個別にお使い(プロセス間通信)を頼んでいます。
シェイプが2,000個あれば、お使いを 4,000回(幅と高さで2回×2,000) も往復させていることになります。これではエンジンが疲弊して遅くなるのは当然ですね。
—
救世主現る!`GetResults` / `SetResults` による「一括輸送」
この問題を一発で解決するのが、今回紹介する `GetResults`(一括読み込み)と `SetResults`(一括書き込み)メソッドです。
考え方はシンプルです。
個別にお使いを走らせるのをやめ、「このコンテナ(配列)に、必要なデータを全部まとめて積んで、一度に持ってきてくれ!」 とVisioにお願いするのです。
これなら、通信はたったの「1往復」。数千個のシェイプデータであっても、一瞬(コンマ数秒の世界)でメモリ上にロードされます。
—
実践!数千シェイプのデータを一瞬で読み書きするコード
百聞は一見にしかず。実際に `GetResults` を使って、ページ上の全シェイプの幅と高さを一括取得し、少し大きくして `SetResults` で一括書き戻すプロフェッショナルなコードを見てみましょう。
Sub FastBatchProcessShapes()
Dim shps As Visio.Shapes
Set shps = ActivePage.Shapes
Dim count As Long
count = shps.Count
If count = 0 Then Exit Sub
‘ 1. シェイプの数に合わせて「名前の配列」と「セルの配列」を準備する
Dim i As Long
ReDim sourceStreams(0 To count – 1) As String
‘ 各シェイプのどのセルを取得するかを指定する文字列の配列を作る
For i = 1 To count
‘ シェイプのインデックスとセル名を指定
‘ 形式: 「シェイプID!セル名」または単純に「Width」「Height」など
‘ ここでは確実性のために相対パスやシート名を含めたセル指定を行います
Next i
‘ — ちょっと待って!もっと簡単な書き方があります —
‘ 実は、MasterやShapeオブジェクトの配列を作って渡す方法もありますが、
‘ Visioの GetResults は 「セル名(例: “Width”)」 の配列と、
‘ 「取得対象のセルの配列(Cellオブジェクトの配列)」を受け取ります。
‘ より実用的な Cellオブジェクトを使った一括取得の形を見てみましょう!
Dim cellArray() As Visio.Cell
ReDim cellArray(0 To (count 2) – 1) ‘ WidthとHeightの2つ分なのでシェイプ数×2
Dim index As Long
index = 0
For i = 1 To count
Set cellArray(index) = shps(i).Cells(“Width”)
Set cellArray(index + 1) = shps(i).Cells(“Height”)
index = index + 2
Next i
‘ 2. GetResultsの準備
‘ 引数1: 取得したいセルの配列(Variant型に詰める必要がある)
‘ 引数2: フラグ(通常は visGetFloats などを指定)
‘ 引数3: 結果を受け取るためのVariant型の配列
Dim sourceCells() As Variant
sourceCells = cellArray ‘ Variant配列にキャスト
Dim resultsArray As Variant
‘ 【ここが核心!】一発で全データを取得!
shps.Application.GetResults sourceCells, visGetFloats, resultsArray
‘ この時点で、resultsArray の中には、全シェイプの Width, Height がズラッと格納されています。
‘ 例: resultsArray(0) = 1番目の幅, resultsArray(1) = 1番目の高さ, resultsArray(2) = 2番目の幅…
‘ 3. 取得したデータに対して加工を行う(例: 全ての幅と高さを1.2倍にする)
For i = LBound(resultsArray) To UBound(resultsArray)
resultsArray(i) = resultsArray(i) 1.2
Next i
‘ 4. 【ここも核心!】SetResultsで一発書き戻し!
‘ 引数1: 書き込み先セルの配列
‘ 引数2: フラグ
‘ 引数3: 書き込む値の配列
shps.Application.SetResults sourceCells, resultsArray
MsgBox count & ” 個のシェイプのサイズ変更が一瞬で完了しました!”, vbInformation
End Sub
コードのポイント解説
1. `Visio.Cell` の配列を作る
一つひとつのセルオブジェクトをあらかじめ配列に格納しておきます。これが「お使いのリスト」になります。
2. `Application.GetResults` の呼び出し
`ActivePage` や `Shape` ではなく、大元の `Application` オブジェクトに対して実行するのがポイントです。第一引数にセル配列、第三引数(受取用変数)を渡すだけで、Variant型の二次元(または一次元)配列にすべての値がゴソッとコピーされます。
3. `Application.SetResults` による一括反映
メモリ上で加工した配列を、そのまま `SetResults` に放り込みます。Visioの描画更新が最小限に抑えられるため、画面がチラつくこともなく、驚異的なスピードで処理が完了します。
—
陥りやすいエラーと知っておくべき注意点
この強力な `GetResults` / `SetResults` ですが、上級者でもたまにハマる「落とし穴」があります。事前に知っておけば怖くありません。
1. 単位(Unit)の罠に気をつけろ
`GetResults` で取得できる数値は、原則として 内部単位(IU: Inches / セント・ポイント等のVisio内部の基準値) です。
ミリメートルやセンチメートルで取得・設定したい場合は、フラグや単位変換に注意が必要です(通常は `visGetFloats` を使い、値はインチ単位でやり取りされます)。画面上の表示単位(mmなど)と混同しないようにしましょう。
2. 数式(Formula)ではなく「値(Result)」が対象
このメソッドは、セルに入っている計算結果(数値)をやり取りするためのものです。
セルに数式(例: `Width = Height 2` など)が入っている場合でも、返ってくるのはその計算された「数値」です。また、書き戻すときも「数値」として書き込まれるため、元々入っていた数式が上書きされて消えてしまうことがあるので注意してください。
3. 動的配列の次元数とインデックスの不一致
VBAの `GetResults` は、引数に渡す「セル配列」と「結果配列」の構造が一致している必要があります。特に `ReDim` を使う際のインデックスのズレ(`0始まり` なのか `1始まり` なのか)には気を配りましょう。
—
まとめ:ここをクリアすれば、Visio VBAは敵なし!
今回は、`Shape.GetResults` と `SetResults` を使った、数千シェイプのデータ一括処理について解説しました。
- 個別アクセス(`shp.Cells().Result` のループ)は遅いので避ける!
- Cellオブジェクトの配列を作り、`Application.GetResults` で一気に読み込む!
- メモリ上でデータを加工し、`Application.SetResults` で一気に書き戻す!
このパターンをマスターすれば、どれほど巨大なネットワーク図やフロアマップ、組織図のデータ処理であっても、プロ仕様の高速なマクロを作ることができます。
「ここをクリアすれば、Visio VBAの基本はバッチリですよ!」
ぜひご自身の開発現場で試して、その圧倒的なスピードを体感してみてください。あなたの自動化ライフがさらに快適になることを応援しています!
