【実務・中級編】画面上の図形選択状態をコードで制御:Shape.CreateSelectionとSelectionオブジェクトの高度なプログラミング – Visio VBA解析バイブル

スポンサーリンク

Visio VBAを掌握する極限の知見:Selectionオブジェクトと `CreateSelection` の全貌

こんにちは。開発プロジェクトを率いるチーフアーキテクトの私だ。
これまで数々の巨大なプラント図面、ネットワークトポロジ、そして複雑な業務フローチャートを自動化するシステムを構築してきた。その中で、多くのVBA開発者がつまずく「見えない壁」がある。それが「図形の選択状態(Selection)の制御」だ。

「特定の条件に合致する図形を一括して色を変えたい」「検索結果をユーザーにハイライトして見せたい」
そう思ったとき、あなたはどうコードを書くだろうか?

まさか、`Page.Shapes` をループさせて `.Select` メソッドを泥臭く呼び出していないだろうか?
もしそうなら、今すぐその手を止めてほしい。そのアプローチは、大規模図面において「最悪のパフォーマンス」「予期せぬ実行時エラー」を引き起こす諸悪の根源だ。

今回は、Visioのオブジェクトモデルの深層を抉り、`Shape.CreateSelection` と `Selection` オブジェクトを駆使して、実務で耐えうる堅牢かつ超高速な選択制御の極意を伝授しよう。

1. なぜ `.Select` の連続呼び出しは御法度なのか?

Visio VBA初学者が陥る最大の罠がこれだ。

‘ 【アンチパターン】絶対にやってはいけない実装
Dim shp As Shape
For Each shp In ActivePage.Shapes
If shp.CellExists(“Prop.Cost”, False) Then
If shp.Cells(“Prop.Cost”).ResultIU > 100000 then
shp.Select visSelectAdd ‘ ← 画面描画とイベントが発生し、地獄のように遅い
End If
End If
Next shp

理由:VisioのUIスレッドとCOMオーバヘッドの呪縛

Visioの `.Select` メソッドは、単に内部のフラグを立てるだけではない。画面の再描画(UIの更新)、ウィンドウの選択変更イベントの発火、そしてCOM境界を越える通信が毎ループごとに発生する。
図形が数千個ある図面でこれをやると、完了までに数分を要するか、最悪の場合はメモリリークやVisio自体のフリーズを招く。

プロのエンジニアであれば、「メモリ上で論理的な集合(Selection)を作り、最後に1回だけ画面に反映する」というアプローチをとる。これが `CreateSelection` の存在意義だ。

2. `Shape.CreateSelection` の真価と正しいライフサイクル

Visioの `Selection` オブジェクトをプログラムから構築するには、主に2つのアプローチがある。

1. 空のSelectionを作って `Select` メソッドで詰めていく方法
2. 特定の起点となる図形から `CreateSelection` を呼び出す方法

特に後者の `Shape.CreateSelection` は、特定の条件(例:特定のレイヤー、接続されている図形群、カスタムプロパティの一致など)を満たす図形を効率的にグループ化するための強力な武器だ。

Selection構築のアルゴリズム

実務で求められる堅牢なコードは、以下の3ステップで構成される。

1. 画面描画とイベントの完全停止(`ScreenUpdating` / `EventEnabled`)
2. メモリ上での高速な条件判定とコレクション構築
3. 一括選択(`Windows.Item(1).Selection = targetSel`)

この設計を守るだけで、実行速度は文字通り数十倍から数百倍に跳ね上がる。

3. 【プロダクションコード】実務で使える高速・堅牢な選択制御モジュール

それでは、実際の現場でそのまま使えるモジュールを提示しよう。
このコードは、指定したページ内から「特定のカスタムプロパティを持ち、かつ値が閾値を超える図形」を瞬時に検索し、一括してユーザーの選択状態にするプロシージャだ。

Option Explicit

Public Sub SelectHighCostShapes()
‘ —————————————————————-
‘ 目的: 指定条件(コスト超過)に合致する図形をメモリ上で抽出し、一括選択する
‘ 備考: 画面描画を停止することで、数千個の図形でも一瞬で処理を完了させる
‘ —————————————————————-

Dim targetPage As Page
Set targetPage = ActivePage

‘ パフォーマンスと安定性のためのガード
If targetPage.Shapes.Count = 0 Then
MsgBox “対象ページに図形が存在しません。”, vbInformation, “処理終了”
Exit Sub
End If

‘ 【重要】爆速化の要:画面描画とイベントの抑制
Application.ScreenUpdating = False
Application.EventsEnabled = False

‘ エラーハンドリング時にも確実に画面描画を復元させるための構造
On Error GoTo ErrorHandler

Dim initialSel As Selection
Set initialSel = Nothing

Dim shp As Shape
Dim matchCount As Long
matchCount = 0

‘ 1. 条件に合致する図形を走査し、Selectionオブジェクトを動的に構築
For Each shp In targetPage.Shapes
‘ 例として、”Prop.Cost” セルが存在するかチェック
If shp.CellExists(“Prop.Cost”, False) Then
If shp.Cells(“Prop.Cost”).ResultIU > 100000 Then

If initialSel Is Nothing Then
‘ 最初の1個目は CreateSelection でベースを作成
Set initialSel = shp.CreateSelection(visSelTypeSingular)
Else
‘ 2個目以降は既存のSelectionに追加 (visSelectAdd)
‘ 注意: CreateSelectionの引数に既存Selectionを渡すことはできないため、
‘ 一度生成したSelectionに図形を追加していくアプローチをとる
initialSel.Select shp, visSelectAdd
End If

matchCount = matchCount + 1
End If
End If
Next shp

‘ 2. 該当図形が存在する場合のみ、ウィンドウの選択状態に一括反映
If Not initialSel Is Nothing Then
ActiveWindow.Selection = initialSel
MsgBox matchCount & “個のコスト超過図形を選択状態にしました。”, vbInformation, “完了”
Else
‘ 選択をクリア
ActiveWindow.DeselectAll
MsgBox “条件に合致する図形はありませんでした。”, vbInformation, “完了”
End If

CleanUp:
‘ 【重要】必ず環境を元の状態に戻す
Application.ScreenUpdating = True
Application.EventsEnabled = True
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
Resume CleanUp
End Sub

4. チーフアーキテクトからの実務アドバイス

このコードをそのまま社内ツールやデータベース連携システムに組み込む際の、更なる「極限の知見」を共有しよう。

データベースや外部ファイル(Excel/CSV)との連携

「どの図形を選択するか」の条件(IDやマスター名など)を、ハードコーディングしてはならない。
外部のMasterデータ(例:Excelの設備台帳)とVisioの `Shape.NameID` または `Prop.EquipmentID` を突き合わせる場合、あらかじめDictionaryオブジェクトに外部IDをロードし、`Shape` のループ内でO(1)の高速ルックアップを行うべきだ。
その上で、一致した図形群を前述の `Selection` オブジェクトに蓄積し、最後に `ActiveWindow.Selection` へ流し込む。このパイプライン設計が、数万レコードを扱う巨大プラント図面の自動化をも耐えうるものにする。

オブジェクトの解放(メモリマネジメント)

VBAは自動ガベージコレクションを持つが、VisioのCOMオブジェクト(特に `Selection` や `Shape`)を大量に生成・破棄するループを書くと、メモリの解放が追いつかなくなることがある。
処理の最後には、生成したローカルオブジェクト変数(`Set initialSel = Nothing` など)を明示的に解放する習慣をつけてほしい。これが「何日稼働させても落ちない堅牢なツール」と「すぐにメモリリークを起こす粗悪なマクロ」の分かれ道だ。

最後に

Visio VBAは、オブジェクトモデルの挙動、特にUIスレッドとの関係性を正しく理解すれば、極めて強力な自動化エンジンへと化ける。
今回解説した `Selection` のメモリ上での構築と一括反映のテクニックは、単なる「選択」に留まらず、一括プロパティ書き換え、レイヤー一括移動、図形の整列・レイアウト最適化など、あらゆる高度なバッチ処理の基盤となる。

プロフェッショナルとして、常に「パフォーマンス」と「保守性」を意識したコードを書き続けてほしい。君たちの健闘を祈る。

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