【実務・中級編】一時的な計算用セル(Scratch Section)の活用:ShapeSheetのワークエリアをVBAで動的作成・破棄する手法 – Visio VBA解析バイブル

スポンサーリンク

はじめに:なぜ一流のエンジニアは「User-Defined Cells」ではなく「Scratch Section」を動的に操るのか

Visio VBAの世界へようこそ。多くの開発者は、図形に独自のデータや計算式を持たせる際、安易に「User-Defined Cells(Userセクション)」に頼りがちだ。しかし、それは「静的な設定」を保持するための場所であり、一時的な計算や複雑な座標変換のために行を増やし続けるのは、プロフェッショナルの仕事とは言えない。

ファイルサイズは肥大化し、図形をコピーするたびに不要なゴミが蓄積され、最終的にはソリューション全体のパフォーマンスを蝕む。

今回伝授するのは、ShapeSheet内の「Scratch Section(スクラッチ・セクション)」をVBAから動的に生成し、計算が終われば即座に破棄するという、高度なメモリ管理にも似た設計思想だ。Visioを単なる描画ソフトではなく、強力な「計算エンジン」として使い倒すための極限の技術を、ここに記す。

1. Scratchセクションという「作業用メモリ」

Scratchセクションは、その名の通り「下書き」のための領域だ。1行につき「X, Y, A, B, C, D」という6つの汎用変数スロットを持っている。

なぜここを使うべきなのか?
1. 計算の局所化: VBA側でループを回して座標計算を行うより、ShapeSheetのエンジンに数式(Formula)として投げ、内部で再計算させる方が圧倒的に速い。
2. 副作用の防止: 計算用セルを恒久的に残さないことで、図形の挙動をシンプルに保ち、将来のメンテナンスコストを下げる。
3. データ連携の最適化: 外部DBやExcelから大量の座標データを取り込む際、一時的な変換処理をこの領域で行うことで、ロジックをVBAコード内に隠蔽できる。

2. 堅牢な設計:動的Scratch操作のライフサイクル

一時的な計算用セルを扱う際、最も恐れるべきは「計算行の放置」と「インデックスの不整合」だ。以下のステップを徹底せよ。

1. セクションの存在確認: Scratchセクションが存在しなければ作成する。
2. 行の追加とインデックス保持: `AddRow`メソッドで末尾に行を追加し、その行番号を確実に取得する。
3. 数式の注入と値の回収: `CellsSRC`を使用して高速にアクセスする。
4. 行の削除(クリーンアップ): 計算が終われば、`Finally`ブロックに相当するエラーハンドリングの中で必ず行を削除する。

3. 実戦的プロダクションコード

以下のコードは、ある図形の複雑な位置情報を元に、特定の計算(例:回転を考慮したオフセット計算など)を一時的に行い、結果だけを回収するクラスメソッドを想定した実装だ。


‘ @Description: ShapeSheetのScratchセクションを動的に利用して計算を行う
‘ @Param targetShape: 対象となるShapeオブジェクト
‘ @Return: 計算されたDouble値

Public Function CalculateComplexGeometry(ByRef targetShape As Visio.Shape) As Double
On Error GoTo ErrorHandler

Dim rowIdx As Integer
Dim resultValue As Double

‘ 1. Scratchセクションの準備
‘ セクションが存在しない場合は自動的に作成される
If Not targetShape.SectionExists(visSectionScratch, visExistsAnywhere) Then
targetShape.AddSection visSectionScratch
End If

‘ 2. 作業用の行を追加
‘ visRowScratch は 0始まり。AddRowは追加された行のインデックスを返す。
rowIdx = targetShape.AddRow(visSectionScratch, visRowLast, visTagDefault)

‘ 3. 数式の注入 (例: 図形の幅の半分と高さを掛け合わせる、といった一時的なロジック)
‘ CellsSRC(Section, Row, Column) を使用するのが最速。
‘ visScratchA, visScratchB などが列定数。
With targetShape
‘ A列に計算式を代入 (例: 幅 0.5)
.CellsSRC(visSectionScratch, rowIdx, visScratchA).FormulaForceU = “Width 0.5”
‘ B列に計算式を代入 (例: A列の結果 + 高さ)
.CellsSRC(visSectionScratch, rowIdx, visScratchB).FormulaForceU = “Scratch.A” & (rowIdx + 1) & ” + Height”

‘ 4. 計算結果の回収
‘ Resultメソッドを使用して、型安全に値を取得する
resultValue = .CellsSRC(visSectionScratch, rowIdx, visScratchB).Result(visNumber)
End With

‘ 計算成功
CalculateComplexGeometry = resultValue

Cleanup:
‘ 5. 後始末:作成した行を確実に削除する
‘ これを怠ると、ツールを使うたびにShapeSheetが肥大化する
If Not targetShape Is Nothing Then
If targetShape.SectionExists(visSectionScratch, visExistsAnywhere) Then
targetShape.DeleteRow visSectionScratch, rowIdx
End If
End If
Exit Function

ErrorHandler:
‘ エラーが発生してもCleanupへ飛ばし、ゴミを残さない
Debug.Print “Error in CalculateComplexGeometry: ” & Err.Description
Resume Cleanup
End Function

4. チーフアーキテクトによる深掘り解説

なぜ `CellsSRC` なのか?

通常、`targetShape.Cells(“Scratch.X1”)` のように名前でアクセスすることも可能だ。しかし、これは内部的に「文字列解析」という重い処理を挟む。ループ内で数千回の計算を行う場合、`CellsSRC`(Section, Row, Columnのインデックス指定)を使うことで、Visioのメモリ空間へダイレクトにアクセスでき、パフォーマンスは劇的に向上する。

データベース連携時の注意

外部のSQL ServerやExcelから図形情報を更新する際、VBA側で全ての計算を完了させようとすると、ネットワークレイテンシやVBAの実行速度がボトルネックになる。
「DBから値をScratchへ流し込む」→「ShapeSheet側で一斉計算」→「結果を確定」というパイプラインを構築することで、描画更新(画面のチラつき)を最小限に抑えることが可能だ。

ライフサイクルの管理

この手法の唯一の弱点は、`DeleteRow`に失敗した際に「謎の計算行」が図形に残ることだ。これを防ぐために、私の設計では必ず `On Error GoTo ErrorHandler` を使い、エラー時でも必ず `Cleanup` ルーチンを通るように強制している。これは、ファイルやDB接続を扱う際のリソース解放(Dispose)と同じ考え方だ。

おわりに:美しさは目に見えない場所に宿る

優れたVisioツールとは、見た目が美しいだけでなく、その背後にあるShapeSheetが極限まで洗練されているものだ。

Scratchセクションを動的に操る技術を習得すれば、あなたの作成するツールは、どれだけ複雑な計算を行っても「軽く、速く、壊れない」ものへと進化する。安易なUserセルへの逃げを捨て、この「一時計算領域」の制御をマスターせよ。それが伝説的なエンジニアへの第一歩である。

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