Visioを「お絵描きツール」から「業務アプリケーション」へ昇華させる:マスターシェイプへの動的動作割り当て戦略
多くのエンジニアがVisio VBAに手を出すが、そのほとんどが「図形を自動で並べる」という低次元な自動化で止まっている。しかし、真に価値のある業務ツールとは、「図形そのものが機能(ロジック)を保持し、ユーザーの操作に反応して業務プロセスを駆動させる」ものである。
例えば、ステンシルから「承認ノード」を配置し、それをダブルクリックすれば、即座に外部データベースから承認経路を読み込み、ダイアログを表示する。あるいは、特定のシェイプを右クリックして「データ連携」を選択すれば、そのシェイプが持つ属性値に基づいてAPIリクエストを飛ばす。
これを実現するために、多くの凡庸な開発者が陥る罠がある。それは、`ActiveWindow.Selection` を監視し、巨大な `If…ElseIf` や `Select Case` で「今どの図形が選ばれているか」を判定する泥臭い実装だ。
断言する。その設計は破綻している。
図形が増えるたびにコードを修正し、シェイプの名前を変更した瞬間にバグが多発する。保守性の地獄へようこそ。
本記事では、世界最高峰の設計思想に基づいた「行動ディスパッチャ・パターン」を用いた、堅牢で拡張性の高い動作割り当て手法を伝授する。
1. アーキテクチャ設計:静的な図形に「DNA」を組み込む
プロの設計では、VBA側に「どの図形が何をするか」を書かない。代わりに、マスターシェイプの「User-defined Cells(ユーザー定義セル)」に、実行すべき関数名やアクションIDをメタデータとして保持させる。
設計コンセプト
1. マスターシェイプ(DNA): `User.ActionName` というセルに、実行したいVBA関数名を文字列で保持させる。
2. ディスパッチャ(神経系): ユーザーイベント(ダブルクリック等)を捕捉し、シェイプのメタデータを読み取り、該当する関数へ動的にルーティングする。
3. アクションモジュール(筋肉): 個別の機能ロジックを独立した関数として定義する。
これにより、新しい機能を追加したい場合は「マスターシェイプに新しい関数名を書き込み、VBAに関数を一つ追加する」だけで完結する。既存のイベントハンドラを一切触る必要はない。
2. 実装:プロダクショングレードのコード例
以下に、ダブルクリックイベントをトリガーとして、マスターシェイプに定義されたアクションを動的に実行する実装例を示す。
ステップ1:マスターシェイプの準備
ステンシルのマスターシェイプを右クリック $\rightarrow$ [シェイプデータ] または [ShapeSheet] を開き、`User` セクションに `ActionName` という名前のセルを作成し、値に `ExecuteApprovalProcess` などの関数名を記入しておくこと。
ステップ2:VBA実装
' ==============================================================================
' Module: Mod_BehaviorDispatcher
' Description: シェイプのメタデータに基づき、動作を動的に割り当てるディスパッチャ
' ==============================================================================
Option Explicit
' 動作を定義するインターフェース的な役割を果たす関数
' Visioのイベントから呼ばれ、適切なアクションへルーティングする
Public Sub DispatchShapeAction(ByVal targetShape As Visio.Shape)
On Error GoTo ErrorHandler
If targetShape Is Nothing Then Exit Sub
' 1. マスターシェイプまたはインスタンスからアクション名を取得
' User-defined cell "ActionName" を参照する
Dim actionName As String
actionName = GetActionName(targetShape)
If actionName = "" Then
Debug.Print "No action defined for shape: " & targetShape.Name
Exit Sub
End If
' 2. CallByNameを用いて、標準モジュール等に定義された関数を動的に実行
' ここでは簡略化のため、Select Caseによるルーティングを行うが、
' より高度な実装ではクラスモジュールを用いたコマンドパターンを推奨する。
ExecuteAction actionName, targetShape
Exit Sub
ErrorHandler:
MsgBox "Action Execution Error: " & Err.Description, vbCritical, "System Error"
End Sub
' ユーザー定義セルからアクション名を取得するヘルパー関数
Private Function GetActionName(ByVal shp As Visio.Shape) As String
On Error Resume Next
' ShapeSheetのUserセクションからActionNameセルを取得
GetActionName = shp.CellsU("User.ActionName").Formula
' 式形式(= "ActionName")で格納されているため、ダブルクォーテーションを除去
GetActionName = Replace(GetActionName, """", "")
On Error GoTo 0
End Function
' アクション名に基づいて実際のロジックを呼び出す
Private Sub ExecuteAction(ByVal actionName As String, ByVal shp As Visio.Shape)
Select Case actionName
Case "ExecuteApprovalProcess"
Call Action_ApprovalProcess(shp)
Case "SyncWithDatabase"
Call Action_SyncDatabase(shp)
Case "OpenDetailProperty"
Call Action_OpenDetails(shp)
Case Else
MsgBox "未定義のアクションです: " & actionName, vbExclamation
End Select
End Sub
' ==============================================================================
' Module: Mod_Actions
' Description: 個別のビジネスロジックを記述するモジュール
' ==============================================================================
Public Sub Action_ApprovalProcess(ByVal shp As Visio.Shape)
' ここに承認プロセスのロジックを記述
MsgBox "シェイプ [" & shp.Name & "] の承認プロセスを開始します。", vbInformation
' 例: DB接続 -> 承認者リスト取得 -> ダイアログ表示
End Sub
Public Sub Action_SyncDatabase(ByVal shp As Visio.Shape)
' ここにDB連携ロジックを記述
MsgBox "データベースと同期中...", vbInformation
End Sub
Public Sub Action_OpenDetails(ByVal shp As Visio.Shape)
MsgBox "詳細プロパティを表示します。", vbInformation
End Sub
ステップ3:イベントの紐付け(重要)
Visio VBAには標準的な「Shape_DoubleClick」イベントが存在しない(Documentレベルで捕捉する必要がある)。
最も堅牢な方法は、ShapeSheetの `EventDblClick` セルに、VBAの関数を呼び出す式を書き込むことだ。
ShapeSheet設定:
`EventDblClick` $\rightarrow$ `=CALL(“DispatchShapeAction”, TheShape)`
※ `TheShape` は自分自身を指す予約語である。
3. チーフアーキテクトが教える「落とし穴」と回避策
この設計を実務に投入する際、素人が見落とし、プロが徹底的に管理するポイントが3つある。
① ステンシルの読み取り専用問題
マスターシェイプをVBAで動的に書き換えようとすると、ステンシルが読み取り専用(Read-only)である場合に実行時エラーが発生する。
対策: マスターを直接書き換えるのではなく、配置した後の「インスタンスシェイプ」のセルを操作するか、ステンシルを一度 `Document.Open` で書き込み権限付きで開き、変更後に保存せずに閉じる戦略をとれ。
② パフォーマンスの重み(ShapeSheetの評価コスト)
大量のシェイプが配置された状態で、すべてのシェイプに複雑な数式を埋め込むと、再計算コストが増大し、描画がカクつく。
対策: `EventDblClick` のようなイベント駆動型であれば問題ないが、`User-defined Cell` の値を頻繁に参照するループ処理を書く場合は、`Shape.CellsU` へのアクセス回数を最小限に抑え、変数にキャッシュせよ。
③ 外部データベース連携のトランザクション管理
シェイプからDBを叩く際、ループ内で接続・切断を繰り返すのは最悪のアンチパターンだ。
対策:
- Connection Poolの擬似的な実装: グローバル変数またはシングルトンクラスでDB接続を保持し、セッションを使い回せ。
- 非同期的な処理の検討: 重い処理はVBAで待たせず、外部のexeやスクリプトに引数を渡してキックし、完了通知を待つ設計にせよ。
4. まとめ:真の自動化とは何か
単に「コードが動く」ことと、「システムとして運用できる」ことの間には深い溝がある。
今回提示した「メタデータによる動作割り当て」は、コードとデータを分離する設計思想である。これにより、VBAに詳しくない管理者がステンシル上の値を書き換えるだけで、ツールの挙動を制御することが可能になる。
「コードを書き換えずに挙動を変えられる設計」。これこそが、プロフェッショナルが到達すべき極限の自動化である。
さあ、今すぐその泥臭い `If…ElseIf` を捨て、アーキテクチャに基づいた設計に移行せよ。
