Visio VBAを掌握する極限の知見:コードだけで描く「動的アイコン生成エンジン」の設計と実装
こんにちは。業務自動化エンジニアのチーフアーキテクトだ。
これまでに何百ものVisio自動化プロジェクトを見てきたが、未だに多くの開発者が古い悪癖を引きずっている。「図形を描画するために、わざわざステンシルファイルを読み込み、マスター図形をドロップして……」というアプローチだ。
ハッキリ言おう。大規模なインフラ図や業務フローの自動生成において、外部ステンシルへの依存はパフォーマンス上のボトルネックであり、配布時のファイル破損リスク(依存関係地獄)を生む最大のガンだ。
今回は、ステンシルに一切頼らず、Visio VBAのプリミティブな描画メソッド(`DrawRectangle`, `DrawOval`, `DrawLine`等)とグループ化を駆使し、外部依存ゼロ・高速・完全動的な「ステータスインジケーター(アイコン)生成エンジン」の構築法を伝授する。
実務でそのまま使えるプロダクションコードを用意した。心してついてきてほしい。
—
1. なぜ「動的描画エンジン」が必要なのか?
実務の現場では、サーバーの状態(稼働中・停止中・警告)、あるいは業務プロセスの進捗(完了・進行中・未着手)を視覚化するカスタムアイコンが大量に必要になる。
これをマスター図形のコピペでやろうとすると、以下の問題に直面する。
1. 配布の手間: ユーザーのPCに専用ステンシル(`.vssx`)を同じパスで配置しなければならない。
2. スタイルの硬直性: 実行時のデータ(例: 負荷率のパーセンテージ)に応じて色やサイズを動的に変えるのが難しい。
3. 描画速度の低下: 画面描画の最適化(`ScreenUpdating`)を怠ると、図形がパタパタと書き換わる醜いレンダリングが起きる。
これを解決するのが、「コードによる幾何学的な座標計算とグループ化」である。
—
2. 堅牢な設計:バグを生まない3つの鉄則
コードで図形を組み立てる際、適当に座標を叩いてはいけない。以下の設計思想をコードに落とし込む。
① `Visio.Application` のイベントと画面描画の完全制御
処理の最初に `ScreenUpdating = False` と `EventsEnabled = False` をかけ、描画負荷を極限まで削る。これがない自動化スクリプトはプロ失格だ。
② 相対座標によるグループ化(Container/Groupパターン)
個別のパーツ(外枠、内側のアイコン、テキスト)をバラバラに描画したあと、それらを `Page.DropMany` または `Shape.Group` でまとめる。その際、親シェイプ(グループ)を基準とした相対位置を計算させることが、レイアウト崩壊を防ぐ唯一の道だ。
③ エラーハンドリングとオブジェクトの解放
VBAのCOMオブジェクトはメモリリークを引き起こしやすい。`.CellsU` やシャープオブジェクトの参照は、処理が終わったら速やかに `Nothing` を代入して解放する。
—
3. プロダクションコード:動的ステータスアイコン生成エンジン
以下のコードは、指定した座標に「外枠(矩形)」「状態を示すシグナル(円)」「ラベル(テキスト)」を一体化したステータスインジケーターを動的に生成するプロシージャだ。
標準モジュールに貼り付け、`CreateDynamicStatusIcon` を実行してその速度と美しさを体感してほしい。
Option Explicit
‘ ==============================================================================
‘ 担当者: チーフアーキテクト
‘ 概要: 外部ステンシルに依存せず、VBAコードのみで複合ステータスアイコンを動的生成する
‘ ==============================================================================
Public Sub RunIconGenerationDemo()
‘ 実行前準備:パフォーマンスと安定性の最大化
On Error GoTo ErrorHandler
Dim appVisio As Visio.Application
Set appVisio = ActiveVisio
appVisio.ScreenUpdating = False
appVisio.EventsEnabled = False
appVisio.UndoScopeBegin “Generate Dynamic Icons”
Dim targetPage As Visio.Page
Set targetPage = appVisio.ActivePage
‘ デモとして、異なるステータスのアイコンを3つ並べて生成する
‘ 引数: ページ, X座標(inch), Y座標(inch), ステータス(“OK”, “Warning”, “Error”), ラベル名
Call CreateStatusIcon(targetPage, 2.0, 5.0, “OK”, “Web Server 01”)
Call CreateStatusIcon(targetPage, 4.0, 5.0, “Warning”, “DB Cluster”)
Call CreateStatusIcon(targetPage, 6.0, 5.0, “Error”, “API Gateway”)
CleanUp:
‘ 変更の確定とパフォーマンス設定の復元
appVisio.UndoScopeEnd “Generate Dynamic Icons”
appVisio.ScreenUpdating = True
appVisio.EventsEnabled = True
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “Visio VBA Engine”
Resume CleanUp
End Sub
‘ ——————————————————————————
‘ コアエンジン: 複合アイコン生成関数
‘ ——————————————————————————
Public Sub CreateStatusIcon(ByVal pg As Visio.Page, ByVal x As Double, ByVal y As Double, ByVal status As String, ByVal labelText As String)
Const ICON_WIDTH As Double = 1.5 ‘ インチ
Const ICON_HEIGHT As Double = 0.8 ‘ インチ
Dim shpRect As Visio.Shape
Dim shpOval As Visio.Shape
Dim shpText As Visio.Shape
Dim shpGroup As Visio.Shape
Dim subShapes(1 To 3) As Visio.Shape
‘ 1. ベースとなる矩形を描画(外枠)
‘ 引数: x1, y1, x2, y2 (ページの左下原点からの絶対座標)
Set shpRect = pg.DrawRectangle(x, y + ICON_HEIGHT, x + ICON_WIDTH, y)
shpRect.Text = “” ‘ 一旦クリア
‘ 外枠のスタイル設定 (シャドウなし、角丸、背景色白)
shpRect.CellsU(“LineColor”).FormulaU = “RGB(100,100,100)”
shpRect.CellsU(“LineWeight”).FormulaU = “0.02”
shpRect.CellsU(“FillForegnd”).FormulaU = “RGB(245,245,245)”
‘ 2. ステータスを示すシグナル(円)を描画
‘ 矩形の左端に近い位置に配置
Dim ovalX As Double: ovalX = x + 0.15
Dim ovalY As Double: ovalY = y + (ICON_HEIGHT / 2)
Dim ovalSize As Double: ovalSize = 0.3
Set shpOval = pg.DrawOval(ovalX, ovalY + (ovalSize / 2), ovalX + ovalSize, ovalY – (ovalSize / 2))
‘ ステータスに応じた色分岐(ここにデータベースや外部APIの判定を組み込める)
Select Case UCase(status)
Case “OK”
shpOval.CellsU(“FillForegnd”).FormulaU = “RGB(40,167,69)” ‘ 緑
Case “WARNING”
shpOval.CellsU(“FillForegnd”).FormulaU = “RGB(255,193,7)” ‘ 黄
Case “ERROR”
shpOval.CellsU(“FillForegnd”).FormulaU = “RGB(220,53,69)” ‘ 赤
Case Else
shpOval.CellsU(“FillForegnd”).FormulaU = “RGB(108,117,125)” ‘ グレー
End Select
shpOval.CellsU(“LineColor”).FormulaU = “RGB(50,50,50)”
‘ 3. ラベルテキスト用の矩形(見えないテキストボックス)を配置
Dim textX As Double: textX = x + 0.55
Dim textY As Double: textY = y + ICON_HEIGHT
Set shpText = pg.DrawRectangle(textX, textY, x + ICON_WIDTH – 0.1, y)
‘ テキストボックスの枠線を透明にし、文字を左寄せで設定
shpText.CellsU(“LinePattern”).FormulaU = “0” ‘ 枠線なし
shpText.CellsU(“FillPattern”).FormulaU = “0” ‘ 塗りつぶしなし
shpText.Text = labelText & vbCrLf & “[” & UCase(status) & “]”
‘ フォント・段落の調整
With shpText.Characters
.Font = pg.Document.Fonts(“Meiryo UI”).Index
.CharProps(visCharacterSize) = 9
End With
shpText.CellsU(“ParaLeftMargin”).FormulaU = “0.02 in”
shpText.CellsU(“VerticalAlign”).FormulaU = “1” ‘ 中央揃え
‘ 4. 複数シェイプを配列にまとめ、グループ化する
Set subShapes(1) = shpRect
Set subShapes(2) = shpOval
Set subShapes(3) = shpText
Set shpGroup = pg.Group(subShapes)
‘ グループ自体のロックやメタデータ付与(必要に応じて)
shpGroup.Name = “StatusIcon_” & labelText
‘ 5. オブジェクト参照の解放(メモリ最適化)
Set shpRect = Nothing
Set shpOval = Nothing
Set shpText = Nothing
Set shpGroup = Nothing
End Sub
—
4. データベースや外部ファイル(JSON/CSV)連携における勘所
実務の現場では、この動的アイコン生成エンジンは「単体」で動くことはない。大抵は外部のDB(SQL Server等)や監視ツールのJSON出力結果と連動させることになる。
ここで、アーキテクトとして実務上の重要な注意点を一つ。
> 「ループ内でデータベース接続を開閉するな。データを一度メモリ(配列またはDictionary)にキャッシュし、一括で描画ループを回せ。」
悪い例(アンチパターン)
‘ データベースから1件ずつレコードを取り出しながらDrawRectangleを呼び出す
‘ -> DBのラウンドトリップとVisioの描画処理がインターリーブし、処理が極端に重くなる
正しいアーキテクチャ
1. データ層 (Data Layer): 外部DBやREST APIからJSON/Recordsetを一括取得し、VBAの `Dictionary` や二次元配列に格納する。
2. ビジネスロジック層 (Logic Layer): 座標計算アルゴリズム(例えば、1行に5個並んだら次の行へ折り返す自動レイアウト計算)を適用する。
3. プレゼンテーション層 (Visio Engine): キャッシュされたデータを元に、今回紹介した `CreateStatusIcon` を爆速で連続実行する。
—
5. まとめ:Visio VBAの限界を超えるために
今回紹介したコードは、単なる「お絵描きマクロ」ではない。
Visioの形状モデル(ShapeSheet)の挙動を理解し、不要なオーバーヘッドを削ぎ落とした「プロダクション品質のコード」だ。
テンプレートやステンシルに頼る開発は、今日で終わりにしよう。
コードで幾何学を支配し、意のままに動的な図面を生み出すエンジンを手に入れたあなたなら、どんなに複雑な業務システム連携であっても、エレガントに自動化できるはずだ。
現場での健闘を祈る。質問があれば、いつでもコードレビューを受け付ける。
