【Visio VBA極限解説】Page.FindShapesを完全制覇する:APIレベルの高速図形フィルタリングと一括操作の極意
開発プロジェクトの現場で、こんな壁にぶつかったことはないだろうか。
「図面全体から、特定の透過率や破線スタイルを持つ図形だけを瞬時に抽出し、一括で色を変えたり集計したりしたい」
素人が最初に書くコードは決まってこうだ。
`ActivePage.Shapes` を `For Each` で全走査し、すべてのシェイプのセルをひとつずつ覗き見していく。図形が数百個ならまだいい。数千個のオブジェクトが絡み合うプラント図やネットワーク図でこれをやったらどうなるか? 画面はフリーズし、ファンがうなりを上げ、実用には耐えないゴミコードが完成する。
Visio VBAにおけるパフォーマンスのボトルネックは、常に「VBAランタイムとVisioのCOM境界を跨ぐ回数」にある。プロパティにアクセスするたびに、この重いオーバーヘッドが発生するのだ。
この悪夢を断ち切る唯一のカードが、`Page.FindShapes` メソッドである。
今回は、Visioのオブジェクトモデルの深淵を知る者だけが使いこなせる、`FindShapes` を用いた超高速フィルタリングと、実務でそのまま使えるプロダクションコードを伝授しよう。
—
1. なぜ愚直なループは地獄を見るのか?
`ActivePage.Shapes` を直接舐めるアプローチは、いわゆる「全件スキャン」だ。Visioのデータ構造は、グループ化やレイヤー構造が複雑に絡み合ったツリー構造を形成している。
これをVBA側で一件ずつ評価していくと、COMオブジェクトへのアクセス回数が爆発的に増大する。
「動けばいい」で作られたマクロが、実運用フェーズで「使いものにならない」と烙印を押される最大の原因はここにある。
APIレベルの検索:`Page.FindShapes` の正体
`Page.FindShapes` は、単なるVBAのループのラッパーではない。Visioのネイティブエンジン内部で最適化された検索クエリを実行し、条件に合致するシェイプの集合(`Selection` オブジェクト)を一撃で返す。
このメソッドを使いこなすことで、VBA側での無駄な条件分岐を根絶し、処理速度を文字通り「桁違い」に引き上げることが可能になるのだ。
—
2. 堅牢な設計:FindShapesの落とし穴と回避策
`FindShapes` は強力だが、MSDNの表面的なリファレンスを読んだだけでは、実務の現場で確実にハマる。以下のアーキテクチャ上の注意点を抑えておいてほしい。
1. 検索クエリの構文厳守
`FindShapes` の引数に渡すクエリ文字列(XPath風の構文)は、構文ミスに対して非常にシビアである。スペルミスやクォーテーションのミスは、実行時エラーではなく「何もヒットしない」というサイレント失敗を引き起こしやすい。
2. グループ化された図形(子シェイプ)の扱い
デフォルトの検索範囲やクエリの指定によっては、グループ内部のサブシェイプを取りこぼす場合がある。図面全体の構造を把握した上で、適切なスコープを設定する必要がある。
3. 存在しないセルの参照エラー
すべてのシェイプが対象のプロパティ(例えば透過率を表す `LocPinX` やカスタムプロパティなど)を持っているとは限らない。存在しないセルにアクセスしようとした瞬間にVBAはクラッシュする。
これらをクリアする、実務投入レベルのコードを次節で公開する。
—
3. 【プロダクションコード】特定属性の図形抽出&一括書式変更
以下のコードは、指定したページ内から「特定の線種(例:破線)」かつ「特定の透過率(例:半透明)」を持つ図形を `FindShapes` で高速抽出し、一括で赤色に変更する実用プロシージャである。
コピペして標準モジュールに貼り付ければ、即座にその性能を体感できるはずだ。
Option Explicit
‘ =========================================================================
‘ プロジェクト名: VisioShapeOptimizer
‘ 概要 : Page.FindShapesを用いた高速フィルタリングと一括書式変更
‘ 備考 : メンテナンス性を考慮し、エラーハンドリングとオブジェクト解放を徹底
‘ =========================================================================
Public Sub ExecuteAdvancedShapeFiltering()
Dim targetPage As Visio.Page
Dim foundShapes As Visio.Selection
Dim shp As Visio.Shape
Dim queryStr As String
Dim processedCount As Long
‘ エラーハンドリングの基本:堅牢なセーフティネットの構築
On Error GoTo ErrorHandler
‘ アクティブページの取得
Set targetPage = ActivePage
If targetPage Is Nothing Then
MsgBox “有効なアクティブページが存在しません。”, vbCritical, “致命的エラー”
Exit Sub
End If
‘ —————————————————————–
S 検索クエリの構築
‘ —————————————————————–
‘ 例:線種(LinePattern)が破線(2など、Visioの仕様に依存)であり、
‘ かつ透過率(LineTransparency または FillTransparency)が適用されているものをターゲットにする。
‘ ※実際の図面仕様にあわせてプロパティ名は適宜調整してください。
‘ ここでは、透かし効果が入っている(透過率 > 0)図形を狙うクエリの概念を示します。
‘ VisioのShapeSheet関数を内包したクエリ構文
‘ 例として「FillTransparency > 0.5」(塗りつぶし透過率が50%超)の図形を検索
queryStr = “Prop.CustProp1 = 0 OR FillTransparency > 0.5”
‘ ※より厳密なグラフィック属性(線種や色)で絞り込む場合のクエリ例:
‘ queryStr = “LinePattern = 2” ‘ 破線
‘ —————————————————————–
‘ APIレベルの高速検索実行
‘ —————————————————————–
‘ FindShapes(Query, Flags, Result)
‘ visFindShapesAncestors: 親グループも含めるか等のフラグ指定
Set foundShapes = targetPage.FindShapes(visFindShapesVisible, queryStr)
If foundShapes.Count = 0 Then
MsgBox “条件に一致する図形は見つかりませんでした。”, vbInformation, “処理完了”
GoTo CleanUp
End If
‘ —————————————————————–
‘ 抽出された図形群に対する一括操作(バッチ処理)
‘ —————————————————————–
processedCount = 0
‘ 画面描画を停止し、さらにパフォーマンスを極限まで引き上げる
Application.ScreenUpdating = False
For Each shp in foundShapes
‘ 例:条件に合致した図形の枠線を赤色(RGB: 255, 0, 0)に変更
‘ セルへの直接アクセスは最小限に抑える
If shp.CellExistsU(“LineColor”, visExistsAnywhere) Then
shp.CellsU(“LineColor.Red”).FormulaU = “255”
shp.CellsU(“LineColor.Green”).FormulaU = “0”
shp.CellsU(“LineColor.Blue”).FormulaU = “0”
processedCount = processedCount + 1
End If
Next shp
Application.ScreenUpdating = True
MsgBox “処理が正常に完了しました。” & vbCrLf & _
“対象図形数: ” & foundShapes.Count & ” 件” & vbCrLf & _
“更新図形数: ” & processedCount & ” 件”, vbInformation, “成功”
CleanUp:
‘ オブジェクトの明示的な解放(メモリリーク防止の鉄則)
Set foundShapes = Nothing
Set targetPage = Nothing
Exit Sub
ErrorHandler:
Application.ScreenUpdating = True
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error 0x” & Hex(Err.Number) & “: ” & Err.Description, vbCritical, “異常終了”
Resume CleanUp
End Sub
—
4. プロフェッショナルとしての実装上の注意点
このコードを実際の業務システムや大型の自動化ツールに組み込む際、以下の2点を必ず担保してほしい。
1. `Application.ScreenUpdating = False` の諸刃の剣
大量のシェイプを一括操作する際、描画の抑制は必須だ。しかし、エラー発生時に `False` のままトラップを抜けてしまうと、Visioの画面がフリーズしたような状態になり、ユーザーをパニックに陥れる。
必ず上記コードのように、`On Error` を経由したクリーンアップブロック(`CleanUp:`)で画面描画を復元する構造を維持すること。
2. 外部データベース・Excel連携への拡張
この `FindShapes` で抽出した `Selection` オブジェクトを起点として、図形のテキストやIDを抽出し、外部のSQLデータベースやExcelマスターへ非同期でログ出力・集計するパイプラインを構築することが可能だ。
「図面からデータを抜き出す」作業において、ループによる総当たりを捨て、APIクエリによるフィルタリングをファーストチョイスに据えること。これが、プロとアマを分ける決定的な境界線である。
—
結びにかえて
Visio VBAは、単なる「お絵かきツールのマクロ」ではない。正しくオブジェクトモデルを理解し、APIの特性(重量級のCOM境界をいかに跨がないか)を意識した設計を行えば、企業内の図面管理・検証業務を根底から変える強力なエンジニアリング武器となる。
今回の `Page.FindShapes` のアプローチをあなたの武器庫に加え、泥臭い全件ループとは今日で決別してほしい。
