コネクタの自動ルーティングを完全制御:VBAで2つの図形間を最短経路でスマートに結ぶ自動配線術
開発プロジェクトのリーダーとして、皆さんの業務効率化という熱意に、私は心から敬意を表します。しかし、現実を直視しましょう。Visioでの図形間のコネクタ作成、特に複雑なフローチャートや組織図においては、マウスでの手作業がどれほどの時間を浪費しているか。そして、その手作業がどれほどのバグの温床となっているか。
本稿では、この非効率な現状に終止符を打ち、Visio VBAを駆使してコネクタの自動ルーティングを「完全制御」する術を、実務でそのまま使えるプロダクションコード例と共に伝授します。単なるリファレンスの羅列ではなく、オブジェクトのライフサイクル、パフォーマンスの重み、そして堅牢な設計思想に基づいた、まさに「極限の知見」をお届けします。
なぜ手動コネクタ作成は非効率なのか? 組織図・フローチャート作成の隠れたコスト
まずは、なぜ私たちがこの課題に取り組むべきなのか、その本質を理解しましょう。
- 時間的コスト: 多数の図形を結ぶ作業は、単純に時間がかかります。特に図形の配置変更や追加・削除が発生した場合、コネクタの再配線はさらに負担となります。
- 品質的コスト(バグ):
- 接着不良: コネクタが図形に正しく接着されていないと、図形を移動させた際に線が追従せず、図が崩壊します。
- 交差・重なり: 複雑な図では、コネクタ同士が交差したり、図形に重なったりして、視認性が著しく低下します。
- 不自然な経路: 最短経路や最も視覚的に分かりやすい経路を通らないコネクタは、図全体の理解を妨げます。
- 保守性の低下: 手動で作成されたコネクタは、その作成意図や依存関係がコードとして明示されないため、後から図を変更する際にどこから手をつけるべきか判断が難しくなります。
これらの問題は、単なる「手間」で片付けられるものではありません。プロジェクト全体の生産性、そして最終的な成果物の品質に直結する「隠れたコスト」なのです。
Visio VBAオブジェクトモデルの「深淵」:Application, Document, Page, Shape の真実
コネクタ自動ルーティングの核心に迫る前に、Visio VBAの主要オブジェクトモデルを、そのライフサイクルとパフォーマンスの観点から理解することが不可欠です。
1. `Application` オブジェクト: Visio プロセスの「心臓部」
- 役割: Visioアプリケーション全体を代表します。開いているドキュメント、アドイン、グローバル設定などを管理します。
- ライフサイクル: プログラムの開始時に生成され、Visioアプリケーションが終了するまで存在します。
- パフォーマンスの重み: `Application` オブジェクトへの直接的な操作は、Visio全体の状態に影響を与えるため、注意が必要です。不要なプロパティの取得や設定は、パフォーマンス低下の原因となり得ます。特に、`Application.ScreenUpdating` の制御は、描画処理をスキップさせることで劇的なパフォーマンス向上に繋がります。
2. `Document` オブジェクト: 作業空間の「憲法」
- 役割: 開いているVisioファイル(`.vsd`, `.vsdx` など)を表します。ページ、図形、マスターシェイプ、プロパティなどを管理します。
- ライフサイクル: `Application.Documents.Add` や `Application.Documents.Open` で生成され、`Document.Close` で破棄されます。
- パフォーマンスの重み: 多数のドキュメントを開いた状態での操作は、メモリ使用量と処理速度に影響します。操作対象のドキュメントを明確に指定し、不要なドキュメントは速やかに閉じるのが賢明です。
3. `Page` オブジェクト: 図面の「キャンバス」
- 役割: ドキュメント内の個々のページを表します。ページ上の図形、コネクタ、ガイドなどを管理します。
- ライフサイクル: `Document.Pages.Add` や `Document.Page` プロパティで取得され、`Page.Delete` で削除されます。
- パフォーマンスの重み: ページ内の図形数が増加すると、`Page` オブジェクトへのアクセスや、ページ内の要素を列挙する処理のパフォーマンスは低下します。特定の図形を直接参照する `Page.Shapes.Item(ShapeNameOrIndex)` や、`Page.Shapes.GetFromSelection` のような効率的なアクセス方法を使い分けましょう。
4. `Shape` オブジェクト: 図形の「魂」
- 役割: ページ上の個々の図形(図形、コネクタ、テキストブロックなど)を表します。位置、サイズ、形状、接続点、プロパティなどを持ちます。
- ライフサイクル: `Page.Shapes.Add` 系メソッドや、既存の図形をコピーして作成されます。`Shape.Delete` で削除されます。
- パフォーマンスの重み: 図形数が多いページでの `Shape` オブジェクトの操作は、最もパフォーマンスに影響を与えやすい部分です。
- `Shape.Cells` プロパティ: 図形の各セル(プロパティ)へのアクセスは、頻繁に行うとパフォーマンスが低下する可能性があります。必要なセルだけを取得し、まとめて処理するのが定石です。
- `Shape.CellsU` vs `Shape.Cells`: `CellsU` はユーザー単位、`Cells` は内部単位でのアクセスです。通常は `CellsU` の方が直感的ですが、パフォーマンスを極限まで追求する場合、内部単位での操作が若干有利になることもあります。しかし、可読性を優先するなら `CellsU` を推奨します。
- `Shape.ConnectionPoints`: コネクタの接続点に関する操作は、図形の構造に影響を与えるため、慎重に行う必要があります。
これらのオブジェクトの特性を理解し、各操作がVisioの描画エンジンにどのような負荷をかけるかを常に意識することが、堅牢で高性能なVBAコードを作成するための第一歩です。
コネクタ自動ルーティングの「アルゴリズム」:最短経路とスマートな接着の秘密
マウス操作を排除し、VBAでコネクタを自動生成、そして「最短経路」で「スマートに接着」させるためのアルゴリズムを設計します。
1. 接続対象図形の特定と接続点の決定
まず、どの図形とどの図形を、どの「接続点」で結ぶかを明確にする必要があります。
- 図形の特定:
- 図形名 (`Shape.Name` または `Shape.NameU`)
- 図形に付与されたカスタムプロパティ (`Shape.CellsU(“Prop.MyID”).FormulaU`)
- 図形のグループ (`Shape.Parent`)
- `Shape.CellsU(“User.DiagramType”)` のようなユーザー定義セルで図形種別を識別
- 接続点の特定:
- `Shape.ConnectionPoints(Index)` プロパティで、図形に定義されている接続点の座標を取得できます。
- 一般的に、コネクタを接続する際には、接続元図形の「右側」や「下側」、接続先図形の「左側」や「上側」の接続点を選択することが多いです。
- 接続点のインデックスは、図形によって異なります。必要であれば、図形にカスタムプロパティとして接続点の番号を定義しておくと便利です。
2. コネクタの生成と初期設定
`Page.DrawCurved` や `Page.DrawStraight` メソッドでコネクタ(実際にはLineシェイプ)を生成します。
- `Page.DrawCurved(x1, y1, x2, y2)`: 2点を通る曲線を描画します。
- `Page.DrawStraight(x1, y1, x2, y2)`: 2点を通る直線を描画します。
生成後、このLineシェイプを「コネクタ」として扱えるように、以下の設定を行います。
- 「コネクタ」への変換: 生成したLineシェイプを `Shape.Type = visTypeIsConnector` に設定します。これにより、Visioのコネクタとしての機能(接続点への接着、ルーティングなど)が有効になります。
- BeginX, BeginY, EndX, EndY の設定: コネクタの始点と終点を、接続対象図形の接続点の座標に設定します。
- 接続: `Connector.BeginConnect` と `Connector.EndConnect` メソッドを使用して、コネクタを各図形の接続点に「接着」させます。これが、図形移動時の自動追従を実現する鍵です。
‘ BeginConnect/EndConnect の例 (ShapeA, ShapeB は接続対象の図形、cpA, cpB は接続点インデックス)
Dim conn As Visio.Connector
Set conn = generatedLineShape ‘ 生成したLineシェイプをConnector型にキャスト
conn.BeginConnect cpA, ShapeA
conn.EndConnect cpB, ShapeB
3. 最短経路の「インテリジェント」な決定
ここが、手動作業との決定的な違いであり、VBAによる自動化の真骨頂です。コネクタの経路を自動決定するには、いくつかの戦略が考えられます。
- 直線: 最もシンプルですが、図形が近接していたり、間に障害物がある場合は不向きです。
- 直角ルーティング (L字型、U字型): フローチャートなどでよく使われる、視覚的に分かりやすい経路です。
- 始点と終点のX座標、Y座標を比較し、どちらを先に移動させるか(水平移動か垂直移動か)を決定します。
- 例えば、始点(X1, Y1)、終点(X2, Y2)とした場合、
- X1 < X2 かつ Y1 < Y2 の場合 → (X1, Y1) → (X1, Y2) → (X2, Y2) または (X1, Y1) → (X2, Y1) → (X2, Y2)
- といった具合に、中間点を計算します。
- 障害物回避ルーティング: より高度なアルゴリズムでは、ページ上の他の図形やコネクタを「障害物」とみなし、それらを避ける経路を計算します。これは、Aアルゴリズムなどの経路探索アルゴリズムを応用することで実現可能ですが、Visio VBAの標準機能だけで実装するのは非常に複雑になります。
- Visioの自動ルーティング機能の活用: 実は、Visioには強力な自動ルーティング機能が備わっています。`Connector.Route` メソッドや、`Page.Layout` メソッドなどがこれにあたります。これらの機能をVBAから呼び出し、パラメータを調整することで、ある程度の「スマートな」ルーティングを実現できます。
- `Connector.Route`: コネクタ自身にルーティングを試みさせます。
- `Page.Layout`: ページ全体のレイアウトを調整し、コネクタのルーティングも最適化します。
実務で「コピペで動く」プロダクションコード例:直角ルーティングと接着
ここでは、最も汎用的で、かつ複雑な図形配置でも比較的うまく機能する「直角ルーティング(L字型)」を基本とし、図形へのスマートな接着を行うVBAコード例を示します。
Option Explicit
‘==============================================================================
‘ Function: ConnectShapesByAutoRoute
‘ Purpose: 2つの図形間を自動ルーティングされたコネクタで接続します。
‘ 図形間の相対位置に基づき、最短経路(直角ルーティング)を優先します。
‘ Arguments:
‘ Page: 対象のVisioページオブジェクト
‘ ShapeA: 接続元図形オブジェクト
‘ ShapeB: 接続先図形オブジェクト
‘ cpA_Idx: 接続元図形’s Connection Point Index (例: 1, 2, 3, 4 for right, top, left, bottom)
‘ cpB_Idx: 接続先図形’s Connection Point Index
‘ connectorLineStyle: 描画するコネクタのLineStyle (例: “Connector”)
‘ Returns: Visio.Connector オブジェクト、または Nothing (エラー時)
‘==============================================================================
Public Function ConnectShapesByAutoRoute( _
ByVal TargetPage As Visio.Page, _
ByVal ShapeA As Visio.Shape, _
ByVal ShapeB As Visio.Shape, _
Optional ByVal cpA_Idx As Integer = 1, _
Optional ByVal cpB_Idx As Integer = 1, _
Optional ByVal connectorLineStyle As String = “Connector” _
) As Visio.Connector
Dim connShape As Visio.Shape
Dim conn As Visio.Connector
Dim startX As Double, startY As Double
Dim endX As Double, endY As Double
Dim midX As Double, midY As Double
On Error GoTo ErrorHandler
‘ — 1. 接続点の座標を取得 —
‘ BeginConnect/EndConnect メソッドは、接続点インデックスだけでなく、
‘ 接続先のShapeオブジェクトも引数に取るため、ここでは直接座標を取得しておきます。
‘ (Visio 2016以降では BeginConnect(Index, Shape) が推奨されています)
startX = ShapeA.CellsU(“ConnectionPoint.” & cpA_Idx & “.X”).ResultIU
startY = ShapeA.CellsU(“ConnectionPoint.” & cpA_Idx & “.Y”).ResultIU
endX = ShapeB.CellsU(“ConnectionPoint.” & cpB_Idx & “.X”).ResultIU
endY = ShapeB.CellsU(“ConnectionPoint.” & cpB_Idx & “.Y”).ResultIU
‘ — 2. 中間点の計算 (直角ルーティング) —
‘ 始点と終点のX, Y座標を比較し、L字型または逆L字型の経路を決定します。
‘ ここでは、X座標の差とY座標の差が大きい方を先に移動させるロジックを単純化して記述します。
‘ より洗練されたルーティングが必要な場合は、障害物などを考慮したアルゴリズムが必要です。
‘ 簡易的な直角ルーティングの例:
‘ ShapeAがShapeBより左にあり、ShapeAがShapeBより上にある場合 (左下 → 右上 の関係)
If startX < endX And startY > endY Then
‘ 中間点は (ShapeAのX座標, ShapeBのY座標) または (ShapeBのX座標, ShapeAのY座標)
‘ どちらがより「短く」かつ「自然」か?
‘ ここでは、X方向を先に移動させるケースを試みます。
midX = startX
midY = endY
‘ ShapeAがShapeBより左にあり、ShapeAがShapeBより下にある場合 (左上 → 右下 の関係)
ElseIf startX < endX And startY < endY Then
midX = startX
midY = endY
' ShapeAがShapeBより右にあり、ShapeAがShapeBより上にある場合 (右下 → 左上 の関係)
ElseIf startX > endX And startY > endY Then
midX = startX
midY = endY
‘ ShapeAがShapeBより右にあり、ShapeAがShapeBより下にある場合 (右上 → 左下 の関係)
ElseIf startX > endX And startY < endY Then
midX = startX
midY = endY
' 同じX座標、または同じY座標の場合 (単純な直線でOK)
Else
midX = startX
midY = endY
End If
' --- 3. コネクタ(Lineシェイプ)を生成 ---
' 始点から中間点、中間点から終点へ描画することで、直角ルーティングを実現します。
' 実際には、2つのLineシェイプを連結するか、1つのLineシェイプの制御点を操作しますが、
' ここではBeginConnect/EndConnectで自動ルーティングに任せるため、
' 始点と終点のみを指定し、後でコネクタに変換します。
' 描画する線は、便宜上、始点と終点を結んだ直線として一度描画します。
Set connShape = TargetPage.DrawLine(startX, startY, endX, endY)
' --- 4. Lineシェイプをコネクタに変換 ---
' Shape.Type を変更することで、Visioのコネクタとして扱えるようになります。
connShape.Type = visTypeIsConnector
Set conn = connShape ' Connector型として扱う
' --- 5. コネクタを各図形の接続点に接着 ---
' BeginConnect / EndConnect メソッドは、接続対象のShapeオブジェクトと接続点インデックスを指定します。
' これにより、図形が移動してもコネクタが自動的に追従します。
' Visio 2010以降では、この形式が推奨されています。
conn.BeginConnect cpA_Idx, ShapeA
conn.EndConnect cpB_Idx, ShapeB
' --- 6. コネクタのルーティングを試みる (Visioの自動ルーティング機能を利用) ---
' コネクタ自身にルーティングを試みさせます。
' 障害物回避や最短経路探索などのロジックは、Visioの内部エンジンに任せます。
' 必要に応じて、ルーティングのアルゴリズムやオプションを指定できます。
' (例: conn.Route 1, 1 は、ルーティングアルゴリズムのタイプとオプションを指定)
' ここでは、デフォルトのルーティングを試みます。
conn.Route
' --- 7. コネクタのスタイルを適用 ---
' マスターシェイプからスタイルを適用する場合
If connectorLineStyle <> “” Then
On Error Resume Next ‘ スタイルが見つからない場合のエラーを回避
connShape.CellsU(“LineStyle”).FormulaU = “””” & connectorLineStyle & “”””
On Error GoTo ErrorHandler ‘ エラーハンドリングを元に戻す
End If
‘ — 成功 —
Set ConnectShapesByAutoRoute = conn
Exit Function
ErrorHandler:
MsgBox “コネクタの自動ルーティング中にエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“説明: ” & Err.Description, vbCritical, “コネクタ自動接続エラー”
‘ エラーが発生した場合、生成された可能性のあるShapeを削除するなどの後処理を検討
If Not connShape Is Nothing Then
If connShape.VisioObject.Type = visTypeIsConnector Or connShape.VisioObject.Type = visTypeIsLine Then
On Error Resume Next
connShape.Delete
On Error GoTo 0
End If
End If
Set ConnectShapesByAutoRoute = Nothing
End Function
‘==============================================================================
‘ サンプル実行プロシージャ
‘==============================================================================
Sub Example_ConnectTwoShapes()
Dim vsoPage As Visio.Page
Dim vsoShape1 As Visio.Shape
Dim vsoShape2 As Visio.Shape
Dim vsoConnector As Visio.Connector
‘ — 画面更新を一時停止してパフォーマンスを向上 —
Application.ScreenUpdating = False
‘ — 対象ページを取得 —
‘ 現在アクティブなドキュメントの最初のページを取得
If Application.Documents.Count = 0 Then
MsgBox “Visioドキュメントが開かれていません。新しいドキュメントを作成または既存のドキュメントを開いてください。”, vbExclamation
Exit Sub
End If
Set vsoPage = Application.ActiveDocument.Pages(1)
‘ — テスト用の図形を配置 (例として、RectangleとProcessを使用) —
‘ 既存の図形を使用する場合は、ShapeNameやユーザー定義プロパティで特定してください。
On Error Resume Next
Set vsoShape1 = vsoPage.Shapes(“Rectangle 1”)
If vsoShape1 Is Nothing Then
Set vsoShape1 = vsoPage.DrawRectangle(1, 5, 3, 7) ‘ 左下の四角形
vsoShape1.Name = “ShapeA”
End If
Set vsoShape2 = vsoPage.Shapes(“Process 1”)
If vsoShape2 Is Nothing Then
Set vsoShape2 = vsoPage.DrawRectangle(7, 2, 9, 4) ‘ 右上の四角形
vsoShape2.Name = “ShapeB”
End If
On Error GoTo 0
If vsoShape1 Is Nothing Or vsoShape2 Is Nothing Then
MsgBox “テスト図形が配置できませんでした。”, vbCritical
Application.ScreenUpdating = True
Exit Sub
End If
‘ — コネクタで接続 —
‘ ShapeAの右側(cp 1)からShapeBの上側(cp 2)へ接続
Set vsoConnector = ConnectShapesByAutoRoute(vsoPage, vsoShape1, vsoShape2, 1, 2, “Dynamic Connector”)
If Not vsoConnector Is Nothing Then
MsgBox “ShapeA と ShapeB がコネクタで接続されました。”, vbInformation
‘ 接続されたコネクタのIDなどを表示する
Debug.Print “接続されたコネクタ名: ” & vsoConnector.Name
Debug.Print “接続元Shape: ” & vsoConnector.BeginConnectedShape.Name
Debug.Print “接続先Shape: ” & vsoConnector.EndConnectedShape.Name
Else
MsgBox “コネクタの接続に失敗しました。”, vbExclamation
End If
‘ — 画面更新を再開 —
Application.ScreenUpdating = True
‘ — オブジェクトの解放 —
Set vsoConnector = Nothing
Set vsoShape1 = Nothing
Set vsoShape2 = Nothing
Set vsoPage = Nothing
End Sub
コード解説と注意点
- `ConnectShapesByAutoRoute` 関数:
- 接続点の指定: `cpA_Idx`, `cpB_Idx` は、接続したい図形の接続点のインデックスです。Visioの標準図形では、概ね「右:1, 上:2, 左:3, 下:4」ですが、カスタム図形や配置によっては異なる場合があります。`Shape.ConnectionPoints` プロパティで確認できます。
- 中間点の計算: 例では非常に単純なX, Y座標の比較に基づいたL字型ルーティングを実装しています。実際の複雑なレイアウトでは、`ShapeA` と `ShapeB` の位置関係、およびページ上の他の図形との干渉を考慮した、より高度なアルゴリズムが必要になる場合があります。
- `DrawLine` → `Type = visTypeIsConnector`: まず直線を描画し、その後 `Type` プロパティを変更してコネクタに変換しています。これにより、`BeginConnect` / `EndConnect` メソッドが利用可能になります。
- `BeginConnect` / `EndConnect`: これらのメソッドが、コネクタを対象図形の指定した接続点に「接着」させる核心部分です。これにより、図形移動時の追従性が保証されます。
- `conn.Route`: コネクタ自身にルーティングを試みさせます。Visioの強力なルーティングエンジンが、ある程度の自動調整を行ってくれます。`Route` メソッドには引数があり、ルーティングのアルゴリズムや挙動を微調整できますが、ここではデフォルト設定を使用しています。
- `LineStyle`: コネクタの見た目(線の種類、太さ、色など)は、マスターシェイプからスタイルを適用するのが一般的です。`”Connector”` や `”Dynamic Connector”` など、Visioのステンシルに含まれるスタイル名を指定します。
- `Example_ConnectTwoShapes` サブプロシージャ:
- `Application.ScreenUpdating = False`: VBAコード実行中の画面描画を一時停止し、パフォーマンスを大幅に向上させます。処理完了後に `True` に戻すのを忘れないでください。
- 図形の特定: 例では、`Shapes(“Name”)` で既存の図形を取得しようとしていますが、実際には図形名だけでなく、カスタムプロパティ(例: `Shape.CellsU(“Prop.MyID”).FormulaU = “Shape123″`) を使用して、より確実に図形を特定することが推奨されます。
- エラーハンドリング: `On Error GoTo ErrorHandler` により、予期せぬエラー発生時に処理を安全に中断し、メッセージを表示します。
ファイル・データベース連携と「保守性の高い」設計
自動ルーティングを実務で活用する上で、単にVBAコードを書くだけでは不十分です。データソースとの連携、そして将来の変更に柔軟に対応できる「保守性の高い」設計が不可欠です。
1. データソースからの図形・コネクタ情報取得
多くの場合、接続すべき図形とその関係性は、Excelファイル、CSVファイル、データベース、あるいは他のシステムから取得されます。
- Excel/CSV: VBAから `FileSystemObject` や `ADODB.Connection` を使って読み込み、各行の情報を解析して図形を特定し、コネクタを生成します。
- 注意点:
- 図形名の命名規則: データソースとVisio図形名(またはカスタムプロパティ)の一貫性を保つことが重要です。
- ID管理: 図形にユニークなIDを付与し、データソースのIDとマッピングすることで、図形の追加・削除・変更に強くします。
- パフォーマンス: 大量のデータを処理する場合、一度に全てを読み込むのではなく、バッチ処理を検討するか、`ADODB` を使用してデータベースから直接クエリを実行する方が効率的です。
- データベース (SQL Server, Accessなど): `ADODB.Connection` と `ADODB.Recordset` を使用して、SQLクエリで必要な情報を取得します。
- 注意点:
- 接続文字列の管理: 接続文字列をコードに直接記述せず、設定ファイルやWindowsの認証機能を利用して安全に管理しましょう。
- トランザクション: 複数の図形やコネクタを更新する場合、一連の処理が完了しなかった場合にロールバックできるようなトランザクション管理を検討します。
- Web API: RESTful APIなどからJSONやXML形式でデータを受け取る場合、VBAから `MSXML2.XMLHTTP` や `WinHttp.WinHttpRequest.5.1` を使用してHTTPリクエストを送信し、レスポンスを解析します。
- 注意点:
- エラー処理: APIのレスポンスコード(404 Not Found, 500 Internal Server Errorなど)を適切にハンドリングし、リトライ処理なども考慮します。
- 非同期処理: 大量のデータを取得する場合、API呼び出しを非同期で行うことで、UIのフリーズを防ぐことができます(ただしVBAでの実装はやや複雑になります)。
2. 保守性の高い設計思想:SOLID原則の適用
「コピペで動く」コードは一時的に便利ですが、将来的な変更や機能追加に対応できなければ、すぐに「負債」となります。保守性を高めるために、ソフトウェア設計の基本原則であるSOLID原則を意識しましょう。
- 単一責任の原則 (SRP):
- 一つのモジュール(関数やクラス)は、一つの責任だけを持つべきです。
- 例: 図形を特定する関数、コネクタを生成する関数、データソースから情報を取得する関数、これらを明確に分離します。
- オープン/クローズドの原則 (OCP):
- ソフトウェアのエンティティ(クラス、モジュール、関数など)は、拡張に対してはオープンであるが、修正に対してはクローズドであるべきです。
- 例: 新しい種類のコネクタスタイルやルーティングアルゴリズムを追加したい場合、既存の `ConnectShapesByAutoRoute` 関数を直接修正するのではなく、新しい関数を作成したり、インターフェースを定義して拡張できるように設計します。
- リスコフの置換原則 (LSP):
- サブタイプは、その親タイプと置換可能でなければならない。
- (VBAではクラス継承が限定的なため、直接的な適用は難しいですが、インターフェースベースの設計を意識することで、この原則の精神を活かせます。)
- インターフェース分離の原則 (ISP):
- クライアントに、そのクライアントが使用しないインターフェースへの依存を強制すべきではない。
- 例: コネクタ生成のインターフェースは、コネクタのルーティング情報だけを要求し、図形のプロパティ取得のような不要なメソッドは含めないようにします。
- 依存性逆転の原則 (DIP):
- 上位レベルのモジュールは、下位レベルのモジュールに依存すべきではない。両方とも抽象に依存すべきである。
- 例: コネクタ生成ロジックが、具体的な `Visio.Page` オブジェクトに直接依存するのではなく、抽象化された「描画インターフェース」に依存するように設計します。これにより、Visio以外の描画エンジンへの切り替えや、単体テストが容易になります。
「プロダクションコード」としてのVBAモジュールの構成例
‘==============================================================================
‘ Module: clsShapeConnectorManager
‘ Purpose: 図形間のコネクタ生成と管理を行うクラスモジュール
‘ Notes: 保守性と拡張性を考慮した設計
‘==============================================================================
Option Explicit
‘ — 定数 —
Private Const DEFAULT_CONNECTOR_STYLE As String = “Dynamic Connector” ‘ デフォルトのコネクタスタイル
‘ — プロパティ —
Private m_vsoPage As Visio.Page
‘ — コンストラクタ —
Public Sub Init(ByVal targetPage As Visio.Page)
Set m_vsoPage = targetPage
End Sub
‘ — メインメソッド: 指定された2図形を接続 —
Public Function ConnectShapes( _
ByVal shapeStart As Visio.Shape, _
ByVal shapeEnd As Visio.Shape, _
Optional ByVal cpStartIdx As Integer = 1, _
Optional ByVal cpEndIdx As Integer = 1, _
Optional ByVal connectorStyle As String = “” _
) As Visio.Connector
Dim connectorStyleToUse As String
If connectorStyle = “” Then
connectorStyleToUse = DEFAULT_CONNECTOR_STYLE
Else
connectorStyleToUse = connectorStyle
End If
‘ 接続処理のコアロジックを別のプライベートメソッドに委譲
Set ConnectShapes = Internal_CreateAndRouteConnector(shapeStart, shapeEnd, cpStartIdx, cpEndIdx, connectorStyleToUse)
End Function
‘ — 内部処理: コネクタ生成とルーティング —
Private Function Internal_CreateAndRouteConnector( _
ByVal shapeStart As Visio.Shape, _
ByVal shapeEnd As Visio.Shape, _
ByVal cpStartIdx As Integer, _
ByVal cpEndIdx As Integer, _
ByVal connectorStyle As String _
) As Visio.Connector
Dim startX As Double, startY As Double
Dim endX As Double, endY As Double
Dim connShape As Visio.Shape
Dim conn As Visio.Connector
On Error GoTo ErrorHandler
‘ — 1. 座標取得 —
startX = shapeStart.CellsU(“ConnectionPoint.” & cpStartIdx & “.X”).ResultIU
startY = shapeStart.CellsU(“ConnectionPoint.” & cpStartIdx & “.Y”).ResultIU
endX = shapeEnd.CellsU(“ConnectionPoint.” & cpEndIdx & “.X”).ResultIU
endY = shapeEnd.CellsU(“ConnectionPoint.” & cpEndIdx & “.Y”).ResultIU
‘ — 2. コネクタ生成 (仮) —
Set connShape = m_vsoPage.DrawLine(startX, startY, endX, endY)
‘ — 3. コネクタへの変換と接着 —
connShape.Type = visTypeIsConnector
Set conn = connShape
conn.BeginConnect cpStartIdx, shapeStart
conn.EndConnect cpEndIdx, shapeEnd
‘ — 4. ルーティング実行 —
conn.Route
‘ — 5. スタイル適用 —
On Error Resume Next
connShape.CellsU(“LineStyle”).FormulaU = “””” & connectorStyle & “”””
On Error GoTo ErrorHandler
Set Internal_CreateAndRouteConnector = conn
Exit Function
ErrorHandler:
MsgBox “コネクタ生成・ルーティングエラー: ” & Err.Description, vbCritical
If Not connShape Is Nothing Then
On Error Resume Next
connShape.Delete
On Error GoTo 0
End If
Set Internal_CreateAndRouteConnector = Nothing
End Function
‘ — 外部データソースからコネクタ情報を読み込むメソッド —
‘ (例: Excelから読み込む場合。実際にはADODB等でDB接続も可能)
Public Sub LoadConnectorsFromExcel(ByVal excelFilePath As String, ByVal sheetName As String)
‘ — Excelファイルを開く処理 (省略) —
‘ Dim xlApp As Object ‘ late binding
‘ Dim xlWB As Object
‘ Dim xlSheet As Object
‘ Set xlApp = CreateObject(“Excel.Application”)
‘ Set xlWB = xlApp.Workbooks.Open(excelFilePath)
‘ Set xlSheet = xlWB.Sheets(sheetName)
‘ — データ読み込みループ —
‘ Dim lastRow As Long
‘ lastRow = xlSheet.Cells(Rows.Count, “A”).End(xlUp).Row ‘ A列の最終行を取得
‘ Dim i As Long
‘ Dim startShapeName As String, endShapeName As String
‘ Dim cpStart As Integer, cpEnd As Integer
‘ Dim connectorStyle As String
‘ For i = 2 To lastRow ‘ 2行目から開始 (ヘッダーを除く)
‘ startShapeName = xlSheet.Cells(i, “A”).Value ‘ 接続元図形名
‘ endShapeName = xlSheet.Cells(i, “B”).Value ‘ 接続先図形名
‘ cpStart = xlSheet.Cells(i, “C”).Value ‘ 接続元接続点
‘ cpEnd = xlSheet.Cells(i, “D”).Value ‘ 接続先接続点
‘ connectorStyle = xlSheet.Cells(i, “E”).Value ‘ コネクタスタイル
‘ Dim vsoShape1 As Visio.Shape
‘ Dim vsoShape2 As Visio.Shape
‘ ‘ — 図形オブジェクトの特定 —
‘ ‘ 図形名で検索。より堅牢にするにはカスタムプロパティを使用。
‘ On Error Resume Next
‘ Set vsoShape1 = m_vsoPage.Shapes(startShapeName)
‘ Set vsoShape2 = m_vsoPage.Shapes(endShapeName)
‘ On Error GoTo 0
‘ If Not vsoShape1 Is Nothing And Not vsoShape2 Is Nothing Then
‘ ‘ — コネクタを生成 —
‘ Dim vsoConn As Visio.Connector
‘ Set vsoConn = Me.ConnectShapes(vsoShape1, vsoShape2, cpStart, cpEnd, connectorStyle)
‘ If Not vsoConn Is Nothing Then
‘ Debug.Print startShapeName & ” -> ” & endShapeName & ” を接続しました。”
‘ Else
‘ Debug.Print “警告: ” & startShapeName & ” -> ” & endShapeName & ” の接続に失敗しました。”
‘ End If
‘ Else
‘ Debug.Print “警告: 図形が見つかりませんでした (” & startShapeName & ” または ” & endShapeName & “).”
‘ End If
‘ Set vsoShape1 = Nothing
‘ Set vsoShape2 = Nothing
‘ Next i
‘ ‘ — Excelオブジェクトの解放 (省略) —
‘ ‘ xlWB.Close SaveChanges:=False
‘ ‘ xlApp.Quit
‘ ‘ Set xlSheet = Nothing
‘ ‘ Set xlWB = Nothing
‘ ‘ Set xlApp = Nothing
MsgBox “Excelからのコネクタ読み込み処理は、この例ではコメントアウトされています。”, vbInformation
End Sub
‘ — クリーンアップ —
Public Sub Cleanup()
Set m_vsoPage = Nothing
End Sub
‘==============================================================================
‘ 標準モジュール (例: basMain)
‘==============================================================================
‘ ‘==============================================================================
‘ ‘ Sub: RunConnectorAutomation
‘ ‘ Purpose: コネクタ自動化処理の実行
‘ ‘==============================================================================
‘ Public Sub RunConnectorAutomation()
‘ Dim vsoPage As Visio.Page
‘ Dim ConnectorManager As clsShapeConnectorManager
‘ ‘ — 初期化 —
‘ On Error GoTo InitializationError
‘ If Application.Documents.Count = 0 Then
‘ MsgBox “Visioドキュメントが開かれていません。”, vbExclamation
‘ Exit Sub
‘ End If
‘ Set vsoPage = Application.ActiveDocument.Pages(1)
‘ Set ConnectorManager = New clsShapeConnectorManager
‘ ConnectorManager.Init vsoPage
‘ ‘ — 画面更新を停止 —
‘ Application.ScreenUpdating = False
‘ ‘ — データソースからコネクタ情報を読み込む —
‘ ‘ 例: Excelファイルから読み込む場合 (パスは適宜変更)
‘ ‘ ConnectorManager.LoadConnectorsFromExcel “C:\Data\Connectors.xlsx”, “Sheet1”
‘ ‘ — または、直接コードで図形を指定して接続する場合 —
‘ ‘ (テスト用。実際はデータソースから取得)
‘ Dim vsoShape1 As Visio.Shape
‘ Dim vsoShape2 As Visio.Shape
‘ On Error Resume Next
‘ Set vsoShape1 = vsoPage.Shapes(“ShapeA”)
‘ Set vsoShape2 = vsoPage.Shapes(“ShapeB”)
‘ On Error GoTo 0
‘ If Not vsoShape1 Is Nothing And Not vsoShape2 Is Nothing Then
‘ ConnectorManager.ConnectShapes vsoShape1, vsoShape2, 1, 2, “Connector”
‘ Else
‘ MsgBox “テスト用図形が見つかりません。”, vbExclamation
‘ End If
‘ ‘ — 終了処理 —
‘ ConnectorManager.Cleanup
‘ Set ConnectorManager = Nothing
‘ Set vsoPage = Nothing
‘ ‘ — 画面更新を再開 —
‘ Application.ScreenUpdating = True
‘ MsgBox “コネクタの自動化処理が完了しました。”, vbInformation
‘ Exit Sub
‘ InitializationError:
‘ MsgBox “初期化エラー: ” & Err.Description, vbCritical
‘ If Not ConnectorManager Is Nothing Then
‘ ConnectorManager.Cleanup
‘ Set ConnectorManager = Nothing
‘ End If
‘ Set vsoPage = Nothing
‘ Application.ScreenUpdating = True
‘ End Sub
このクラスモジュール `clsShapeConnectorManager` は、コネクタ生成ロジックをカプセル化し、`Init` メソッドで対象ページを設定、`ConnectShapes` メソッドで具体的な接続処理を実行します。`LoadConnectorsFromExcel` のようなメソッドを追加することで、様々なデータソースからの読み込みに対応できます。
まとめ:Visio VBAで「未来の効率」を創造する
本稿では、Visio VBAを用いたコネクタの自動ルーティングについて、その必要性、オブジェクトモデルの深淵、具体的なアルゴリズム、そして実務で活用するための堅牢な設計思想までを網羅的に解説しました。
- 手作業の非効率性を理解し、自動化の必要性を認識する。
- Visio VBAの主要オブジェクトのライフサイクルとパフォーマンス特性を把握する。
- コネクタ生成、接着、ルーティングのアルゴリズムを理解する。
- 提供したサンプルコードを基に、自身の環境に合わせてカスタマイズする。
- ファイル・データベース連携を考慮し、SOLID原則に基づいた保守性の高い設計を心がける。
Visio VBAは、単なるGUI操作の自動化ツールではありません。適切に使いこなせば、複雑な図作成プロセスを劇的に効率化し、ヒューマンエラーを排除し、そして何よりも、皆さんの貴重な時間を、より創造的で付加価値の高い業務へと解放する強力な武器となります。
この「極限の知見」が、皆さんの業務効率化プロジェクトを成功に導く一助となれば幸いです。さあ、Visio VBAの力を解き放ち、未来の効率を創造しましょう。
