【テクニカル・上級編】シェイプシート(ShapeSheet)の数式ハック:VBAからCellsU経由でガードや数式を操る – Visio VBA解析バイブル

スポンサーリンク

Visioの心臓を外科手術する:ShapeSheetハックによる「ガード」と「動的数式」の極意

Visioというソフトウェアは、GUIで見える図形(Shape)の裏側に、Excel以上の複雑さを持つ「ShapeSheet」というデータベースを隠し持っている。多くのエンジニアはGUIで図形を配置し、満足して終わる。だが、真の自動化を追求するアーキテクトにとって、Visioは「ShapeSheetを操作するためのフロントエンド」に過ぎない。

今日は、VBAから`CellsU`を叩き、図形の深層心理である数式を書き換える手法について、現場の死線を潜り抜けてきた知見を共有する。

1. なぜ「FormulaU」なのか:直感に頼らない真の制御

Visio VBAにおいて、`Shape.Text`や`Shape.Cells(“Width”).ResultIU`などを触る段階では、まだ初級者だ。真の自動化エンジニアは、`FormulaU`を直接操作する。

なぜか? `ResultIU`(内部単位での値)を書き込むと、その後の数式連鎖(Dependency)が破壊される可能性があるからだ。`FormulaU`を用いれば、単なる値の代入ではなく「数式の定義」が可能になる。これこそが、Visioのレイアウトエンジンを意のままに操る鍵だ。

実践:ShapeSheetへの直接代入とガードの鉄則

ユーザーの誤操作で位置やサイズが変わることを防ぎたいとき、GUIの「保護」チェックボックスを触る必要はない。`GUARD`関数をVBAから注入すればいい。

‘ @brief 指定したシェイプの幅と高さを動的に固定し、GUARD関数で保護する
Public Sub ApplyStrictProtection(targetShape As Visio.Shape)
Dim cellWidth As Visio.Cell
Dim cellHeight As Visio.Cell

‘ エラーハンドリングの徹底:オブジェクトの存在確認は基本中の基本
If targetShape Is Nothing Then Exit Sub

‘ CellsUはUIの言語設定(ロケール)に依存しないU版を使用すること
Set cellWidth = targetShape.CellsU(“Width”)
Set cellHeight = targetShape.CellsU(“Height”)

‘ GUARD関数で数式を囲う。これによりGUIでのリサイズが物理的に不可能になる
‘ “GUARD(50mm)” のように記述することで、値が固定される
cellWidth.FormulaU = “GUARD(” & cellWidth.ResultIU & “)”
cellHeight.FormulaU = “GUARD(” & cellHeight.ResultIU & “)”

‘ メモリリークを防ぐためのオブジェクト解放
Set cellWidth = Nothing
Set cellHeight = Nothing
End Sub

2. パフォーマンスの深淵:画面更新とメモリ最適化

大規模な図面を操作する際、最も忌むべきは`Application.ScreenUpdating = True`のまま処理を走らせることだ。Visioは描画更新のたびにShapeSheetの再計算(Re-calculate)を走らせる。数千個のシェイプを操作する場合、これだけで処理時間が10倍以上跳ね上がる。

アーキテクトの定石:更新停止と再計算の制御

Public Sub HighPerformanceUpdate(doc As Visio.Document)
Dim app As Visio.Application
Set app = doc.Application

‘ 画面更新を停止。これを忘れるとパフォーマンスは崩壊する
app.ScreenUpdating = False

‘ ShapeSheetの自動再計算を一時停止する禁断のテクニック
‘ 大量にFormulaUを書き換える際は、このフラグが不可欠
app.DeferRecalc = True

‘ — ここに重い処理を記述 —

‘ 処理終了後に再計算を強制し、描画を更新する
app.DeferRecalc = False
app.ScreenUpdating = True
End Sub

3. レガシー連携:Win32 APIでVisioの「外」と対話する

Visioのオートメーションだけで解決できない問題(例えば、外部DBとの同期中に一時的にWindowsのメッセージループを回したい、あるいは特定のプロセス終了を待機したい等)が発生する場合、`Kernel32`を呼び出す必要がある。

VBAのシングルスレッド環境で長時間処理を行うと、OSからは「応答なし」に見える。`DoEvents`を挟むのは定石だが、より厳密に制御したい場合は`Sleep`関数でスレッドを譲渡する。

If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Else
Private Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
End If

‘ 外部プロセスとの同期が必要な際の待機処理
Public Sub WaitAndProcess(ms As Long)
Sleep ms
DoEvents ‘ VisioのUIスレッドに制御を戻す
End Sub

4. 伝説のエンジニアからの忠告:オブジェクトモデルの寿命

Visioのオブジェクトモデルを使い倒す上で、最も多くのバグを生むのは「参照の放置」だ。

特に`ActivePage`や`ActiveWindow`を何度も呼び出すコードは、プロセスの不安定化を招く。VBAのガベージコレクションは優秀ではない。必要なオブジェクトはローカル変数に格納し、処理が終われば必ず`Nothing`を代入する。 これが、数日間連続稼働する自動化ツールを構築するための唯一の防衛策だ。

まとめ:次に進むべきステップ

1. FormulaUを制せよ:固定値ではなく、`”Width0.5″`のような数式を動的に注入するロジックを組む。
2. 依存関係を意識せよ:ShapeSheetのセル同士が数式で結ばれている場合、一箇所を書き換えるだけで全体が波及的に変化する。この「連鎖」を逆手に取れば、複雑なロジックをVBAなしで実装可能だ。
3. エラーハンドリングは「沈黙」させるな:`On Error Resume Next`を多用するのは三流だ。`Err.Number`を個別に拾い、どのセルの書き換えが失敗したのかログに残せ。

VisioのShapeSheetは、単なるプロパティの集合ではない。それは、君が書くコードが生き続けるための「永続的メモリ」なのだ。その深淵を理解したとき、君はVisioを「描画ツール」ではなく「エンジニアリング・エンジン」として使いこなしているはずだ。

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