【入門編】Shape.IsSelectedを使わないスマートな非破壊ハイライト:特定条件のシェイプ背景を一時点滅・強調表示して元に戻す仕組み – Visio VBA解析バイブル

スポンサーリンク

やあ、プログラミングの深淵に挑む皆さん、こんにちは!世界最高峰の業務自動化エンジニア、そしてVisio VBAの探求者である私が、皆さんのVisio自動化への道を力強くサポートします。

Visio VBAの世界は、一見すると複雑に見えるかもしれません。しかし、その根幹をなすオブジェクトモデルを正しく理解し、ちょっとした「知恵」を加えれば、皆さんの手は魔法のようにVisioを操ることができるようになります。

今回は、Visio VBAで誰もが一度は「もっとスマートにできないものか?」と頭を悩ませるであろうテーマに挑みます。それは「ユーザーの選択状態を汚染せず、特定条件のシェイプを一時的にハイライト表示し、そして何事もなかったかのように元に戻す」という、まさに”非破壊的”なテクニックです。

「マクロの記録から一歩先へ」踏み出したい方、そして「なんとなく動く」コードから「意図通りに、堅牢に動く」コードへと進化させたい方。この記事を読み終える頃には、Visio VBAの基本はもちろん、その奥深さに触れることができるはずです。さあ、一緒にVisio VBAの極限の知見を掌握していきましょう!

なぜ「非破壊ハイライト」が必要なのか?:ユーザー体験を守るコードの作法

Visioの自動化を始めたばかりの頃、多くの人が直面する課題の一つに「図形を選択する」という操作があります。特定の図形を見つけ出し、ユーザーにその存在を知らせるために、最も手っ取り早いのがその図形を選択状態にすることでしょう。

しかし、ここに大きな落とし穴があります。

‘ よくあるが、避けるべきコードの例
Sub HighlightBySelecting()
Dim shp As Visio.Shape
For Each shp In ActivePage.Shapes
If InStr(shp.Text, “重要”) > 0 Then
shp.Select visSelect # 図形を選択!
End If
Next shp
End Sub

このコードを実行すると、確かに「重要」というテキストを含む図形は選択されます。しかし、その瞬間、ユーザーがそれまで選択していた他の図形は、問答無用で選択解除されてしまいます。

想像してみてください。ユーザーが苦労して複数の図形を選び、これから操作しようとしていたのに、VBAマクロを動かした途端、それらが勝手に解除されてしまったら…?
「え、何?私の選択が消えた!?」と、ユーザーは混乱し、場合によっては作業をやり直す羽目になります。これは、ユーザー体験にとって非常に「破壊的」な操作であり、自動化の目的である「効率化」とは真逆の効果を生んでしまいます。

私たちが目指すのは、ユーザーの作業を邪魔せず、そっと目的の図形を教えてあげるような、まるで気配りのできる秘書のようなVBAコードです。そのためには、`Shape.Select` や `Shape.IsSelected = True` といった「選択」に関わるプロパティやメソッドを使わず、別の方法で図形を強調表示するテクニックが必要になるわけです。

この記事では、この「非破壊ハイライト」を実現するための、Visio VBAの基礎から応用までを丁寧に解説していきます。

Visio VBAの基礎の骨格:Application, Document, Page, Shapeオブジェクト

Visio VBAでコードを書く上で、まず皆さんの脳内に強固なイメージとして定着させるべきは、Visioのオブジェクトモデルです。これは、Visioというアプリケーションの「骨格」であり、「地図」のようなもの。この地図を理解すれば、迷うことなく目的地(特定の図形や属性)にたどり着くことができます。

Visio VBAにおける主要なオブジェクトは、主に以下の4つが階層構造をなしています。

  • `Application`オブジェクト:
  • Visioアプリケーションそのものを表します。Visioの起動や終了、各種設定、イベントの管理など、Visio全体を司る最上位のオブジェクトです。
  • VBAコードの中では、ほとんどの場合 `Visio.Application` または `Application` で参照します。(Excel VBAの`Excel.Application`や`Application`と同じ感覚ですね。)
  • `Document`オブジェクト:
  • `Application`オブジェクトの下に位置し、Visioで開いているファイル(ドキュメント)を表します。一つのVisioアプリケーションで複数のドキュメントを開くことができるため、`Documents`コレクションを通じてアクセスします。
  • 現在アクティブなドキュメントは `Application.ActiveDocument` で取得できます。
  • `Page`オブジェクト:
  • `Document`オブジェクトの下に位置し、ドキュメント内の個々のページを表します。Visioのドキュメントは複数のページを持つことができるため、`Pages`コレクションを通じてアクセスします。
  • 現在アクティブなページは `Document.ActivePage` で取得します。
  • `Shape`オブジェクト:
  • `Page`オブジェクトの下に位置し、Visioのページ上にある個々の図形(シェイプ)を表します。これは、四角形、円、コネクタ、テキストボックスなど、Visioの描画要素のすべてを指します。`Shapes`コレクションを通じてアクセスします。
  • Visio VBAで最も頻繁に操作することになるオブジェクトと言っても過言ではありません。

これらのオブジェクトは、まるでロシアのマトリョーシカ人形のように、大きなものの中に小さなものが含まれる階層構造を持っています。

Application (Visioアプリ全体)
└─ Documents (開いている全てのファイル)
└─ Document (特定のファイル)
└─ Pages (そのファイル内の全てのページ)
└─ Page (特定のページ)
└─ Shapes (そのページ上の全ての図形)
└─ Shape (特定の図形)

この階層構造を意識することで、「どのドキュメントの、どのページの、どの図形を操作したいのか」という道筋が明確になり、VBAコードも迷いなく記述できるようになります。ここをクリアすれば、Visio VBAの基本はバッチリですよ!

`Shape.IsSelected` の落とし穴:なぜ避けるべきか、その本質

前述の通り、`Shape.Select`や`Shape.IsSelected = True`は、ユーザーの既存の選択状態を破壊し、予期せぬ結果を招く可能性があります。これはプログラミングの世界で言うところの「副作用」が非常に大きい操作です。

例えば、ユーザーが以下のような状況で作業していたとします。

1. 複数の図形をCtrlキーを押しながら選択した。
2. そのうちのいくつかの図形に、さらに別の図形をグループ化しようとしていた。
3. VBAマクロを実行。

このとき、もしマクロ内で`Shape.Select`が使われていたら、ユーザーの選択はすべてクリアされ、マクロが選択した図形のみが残ります。ユーザーは「なぜ?」と感じ、作業が中断されることになります。これは、自動化ツールの提供者として避けたい最悪のシナリオです。

では、どうすれば良いのでしょうか?
答えはシンプルです。「図形そのものの見た目を一時的に変更し、強調表示が終わったら元の見た目に戻す」というアプローチを取るのです。

非破壊ハイライトの核心:一時的な属性変更と完全復元

非破壊ハイライトの肝は、強調表示したい図形の「元の状態」を記憶しておき、変更後にはその「元の状態」に完璧に戻すというプロセスにあります。まるで、美術館で特別な照明を当てて展示物を際立たせ、閉館後には元の照明に戻すようなものです。

Visioの図形の見た目を構成する要素は多岐にわたりますが、今回は最も視覚的に分かりやすい背景色を例に解説します。もちろん、線色、線の太さ、テキスト色など、様々な属性に応用可能です。

Visioの図形が持つこれらの属性は、すべてShapeSheetと呼ばれる内部のワークシートにセルとして格納されています。VBAでは、`Shape.Cells`コレクションを通じてこれらのセルにアクセスし、値を読み書きすることができます。

`Shape.Cells` と `Shape.CellsU` の利用

`Shape.Cells` は、図形の各種セルにアクセスするためのプロパティです。セルの名前(例: “FillForegnd” は背景色)を指定して、その値を取得したり設定したりします。

ここで一つ、プロの視点から重要なアドバイスがあります。それは、`Shape.Cells` よりも、`Shape.CellsU` を積極的に使うべきだということです。

  • `Shape.Cells(“FillForegnd”)`: これはローカル名(表示名)でセルを参照します。Visioの言語設定によっては名前が変わる可能性があります。
  • `Shape.CellsU(“FillForegnd”)`: これはユニバーサル名(普遍名)でセルを参照します。言語設定に関わらず、常に同じ名前でセルにアクセスできます。より堅牢で国際化に対応したコードを書くためには必須の知識です。

背景色を変更する場合、`FillForegnd`(ユニバーサル名)というセルを操作します。このセルは、Visioのカラーパレットのインデックス(例: 0=白、1=黒、など)や、RGB値を文字列で保持しています。

ハイライトの具体的な手順

1. 対象シェイプの特定: ページ内の全シェイプをループし、ハイライトしたい条件に合致するシェイプを見つけます。
2. 元の背景色の保存: 各対象シェイプの `FillForegnd` セルの現在の値を、一時的に記憶する場所(例えば `Scripting.Dictionary` オブジェクト)に保存します。
3. 背景色の一時的な変更: 保存後、そのシェイプの `FillForegnd` セルに強調したい色(例: 赤)を設定します。
4. ハイライトの視認性確保: ユーザーが変更を確認できるよう、一時的に処理を停止する時間を設けます。(`MsgBox`や`Application.Wait`など)
5. 元の背景色への復元: 保存しておいた元の色情報を使って、すべての対象シェイプの `FillForegnd` セルを元の値に戻します。

この一連のプロセスをVBAコードで実現してみましょう。

実践!スマートな非破壊ハイライトVBAコード

それでは、実際にVisio VBAを使って非破壊ハイライトを実装してみましょう。今回は「シェイプのテキストに『重要』という文字列が含まれている場合」を条件に、背景色を赤くハイライトし、その後元に戻す例です。

Sub SmartNonDestructiveHighlight()
‘ 事前準備: Visioオブジェクトモデルの宣言と取得
Dim appVisio As Visio.Application
Dim docVisio As Visio.Document
Dim pagVisio As Visio.Page
Dim shp As Visio.Shape

‘ 元の背景色を保存するためのDictionaryオブジェクト
‘ Dictionaryはキーと値のペアでデータを管理するのに非常に便利です。
‘ スクリプティングランタイムライブラリへの参照設定が必要です。
‘ (「ツール」->「参照設定」から「Microsoft Scripting Runtime」にチェック)
‘ あるいは、CreateObject(“Scripting.Dictionary”) で動的に作成することも可能です。
Dim dicOriginalColors As Object ‘ Scripting.Dictionary
Set dicOriginalColors = CreateObject(“Scripting.Dictionary”)

‘ ShapeSheetのセルにアクセスするためのオブジェクト
Dim vsoCell As Visio.Cell
‘ 元の背景色を文字列で保持するための変数
Dim strOriginalFillColorFormula As String

‘ エラーハンドリングの開始
On Error GoTo ErrorHandler

‘ Visioアプリケーションオブジェクトを取得
Set appVisio = Visio.Application
‘ 現在アクティブなドキュメントを取得
Set docVisio = appVisio.ActiveDocument
‘ 現在アクティブなページを取得
Set pagVisio = docVisio.ActivePage

‘ パフォーマンス最適化と画面更新の抑制
‘ 大量のシェイプを処理する際に画面のちらつきを防ぎ、処理速度を向上させます。
appVisio.ScreenUpdating = False
‘ 再計算を一時停止することで、複雑な図面の更新負荷を軽減します。
appVisio.DeferRecalc = True

‘ 元に戻す操作を一つにまとめるためのUndoスコープを開始します。
‘ これにより、ユーザーはCtrl+Zでハイライトと復元の操作全体を一度で元に戻せます。
appVisio.BeginUndoScope “一時ハイライト”

‘ — ステップ1 & 2: 対象シェイプの特定と元の背景色の保存 —
‘ ページ内の全てのシェイプをループ処理
For Each shp In pagVisio.Shapes
‘ 条件判定: シェイプのテキストに「重要」という文字列が含まれるか?
‘ InStr関数は文字列が見つかれば1以上の値を返します。
If InStr(shp.Text, “重要”) > 0 Then
‘ 元の背景色(FillForegndセル)のFormulaU(ユニバーサルな計算式)を取得し保存
‘ FormulaUはVisioの言語設定に依存しない堅牢なセル参照方法です。
Set vsoCell = shp.CellsU(“FillForegnd”)
strOriginalFillColorFormula = vsoCell.FormulaU

‘ シェイプのIDをキーとして、元の背景色のFormulaをDictionaryに保存
‘ これにより、後で元の色に戻す際にどのシェイプにどの色を適用するかを追跡できます。
If Not dicOriginalColors.Exists(shp.ID) Then ‘ 同じシェイプを二重に登録しないように
dicOriginalColors.Add shp.ID, strOriginalFillColorFormula
End If

‘ — ステップ3: 背景色の一時的な変更 —
‘ 背景色を赤(RGB(255,0,0))に設定
‘ FormulaUにRGB関数を使って色を指定するのが一般的で柔軟な方法です。
shp.CellsU(“FillForegnd”).FormulaU = “RGB(255,0,0)”
End If
Next shp

‘ — ステップ4: ハイライトを確認する時間を与える —
‘ MsgBoxを表示して、ユーザーにハイライトされた状態を確認してもらいます。
‘ OKボタンが押されるまで処理は一時停止します。
MsgBox “条件に合致するシェイプを一時的にハイライトしました。” & vbCrLf & _
“OKをクリックすると元の色に戻ります。”, vbInformation, “Visio VBA ハイライト”

‘ — ステップ5: 元の背景色への復元 —
Dim shpID As Variant ‘ Dictionaryのキー(シェイプID)を格納する変数

‘ Dictionaryに保存した各シェイプIDと元の色情報を使って、色を復元
For Each shpID In dicOriginalColors.Keys
‘ IDからシェイプオブジェクトを取得
‘ シェイプが処理中に削除された可能性も考慮し、エラーハンドリングを強化
On Error Resume Next ‘ 次のエラーまで無視
Set shp = pagVisio.Shapes.ItemFromID(shpID)
On Error GoTo ErrorHandler ‘ エラーハンドリングを再開

If Not shp Is Nothing Then
‘ 保存しておいた元の背景色に戻す
shp.CellsU(“FillForegnd”).FormulaU = dicOriginalColors.Item(shpID)
Else
‘ シェイプが削除されていた場合のログなど
Debug.Print “Warning: Shape with ID ” & shpID & ” was not found for restoration.”
End If
Next shpID

‘ 処理の完了を知らせる
MsgBox “全てのシェイプが元の色に戻りました。”, vbInformation, “Visio VBA ハイライト完了”

Exit_Sub:
‘ Undoスコープを終了します。
If appVisio.InUndoScope Then
appVisio.EndUndoScope
End If

‘ パフォーマンス最適化設定を元に戻します。
appVisio.DeferRecalc = False
appVisio.ScreenUpdating = True

‘ オブジェクトの解放 (メモリリークを防ぐための重要なステップ)
Set vsoCell = Nothing
Set dicOriginalColors = Nothing
Set shp = Nothing
Set pagVisio = Nothing
Set docVisio = Nothing
Set appVisio = Nothing
Exit Sub

ErrorHandler:
‘ エラー発生時の処理
MsgBox “エラーが発生しました: ” & Err.Description & vbCrLf & _
“エラー番号: ” & Err.Number, vbCritical, “エラー”
Resume Exit_Sub ‘ 終了処理へジャンプ

End Sub

コードの解説とポイント

  • `Scripting.Dictionary`の活用:
  • `CreateObject(“Scripting.Dictionary”)` で動的に `Dictionary` オブジェクトを作成しています。これにより、VBAプロジェクトの参照設定を手動で行う手間が省けます。
  • `Dictionary` は「シェイプID」と「元の背景色のFormula」をセットで記憶するのに最適です。シェイプIDは図形を一意に識別する数値で、図形が削除されない限り変わりません。
  • `Shape.CellsU`の重要性:
  • `shp.CellsU(“FillForegnd”).FormulaU` を使って、背景色のユニバーサル名での計算式を取得・設定しています。これにより、Visioの言語環境に依存しない、堅牢なコードが実現できます。この使い方は、まさに「極限の知見」の一つです。
  • パフォーマンス最適化:
  • `appVisio.ScreenUpdating = False` と `appVisio.DeferRecalc = True` は、大量のシェイプを処理する際に必須のテクニックです。画面の再描画やVisio内部の再計算を一時的に停止することで、処理速度を大幅に向上させ、ユーザーインターフェースのちらつきを抑えます。処理の最後には必ず `True` に戻すことを忘れないでください。
  • `Application.BeginUndoScope` / `EndUndoScope`:
  • このメソッドを使うことで、VBAでの一連の操作(ハイライトと復元)をVisioの「元に戻す」履歴の中で一つの操作としてまとめることができます。これにより、ユーザーはCtrl+Zを押すだけで、マクロによる変更全体を元に戻せるようになり、非常にユーザーフレンドリーな挙動となります。これもまた、痒い所に手が届く「チーフアーキテクト」の配慮です。
  • オブジェクトの解放:
  • `Set obj = Nothing` は、VBAでオブジェクトを操作する際の基本中の基本です。処理が終わったオブジェクトは明示的に解放することで、メモリリークを防ぎ、安定した動作を確保します。特に大規模なVisioファイルや長時間動作するマクロでは、この習慣が非常に重要になります。
  • エラーハンドリング:
  • `On Error GoTo ErrorHandler` でエラー発生時の処理を定義しています。特に、元の色に戻す際に`Shapes.ItemFromID`でシェイプが見つからない可能性(ユーザーがその間にシェイプを削除したなど)を考慮し、`On Error Resume Next` で一時的にエラーを無視する区間を設けています。これにより、途中でエラーが発生しても、可能な限り多くのシェイプを元の状態に戻そうとします。

さらに進んだ応用と考慮事項

今回のコードはあくまで基本的なハイライトの例ですが、ここからさらに応用していくためのヒントをいくつかご紹介します。

1. 複数の属性変更への応用

背景色だけでなく、線色、線の太さ、テキスト色、透過度など、様々なShapeSheetセルを同じ要領で一時変更・復元できます。例えば、`LineForegnd` (線色)、`LineWeight` (線太さ)、`Char.Color` (テキスト色) などがあります。複数の属性を同時に変更・復元したい場合は、`Dictionary`の値として、元の属性値を複数格納できるカスタムクラスや配列などを使うと良いでしょう。

2. 点滅(ブリンク)効果の実装

一時的にハイライトするだけでなく、「点滅」させてさらに目立たせたい場合もあるでしょう。これは、VBAの`Application.OnTime`メソッドや、フォームのタイマーコントロール(非推奨ですが…)を使って、一定時間ごとに色を変更する処理を繰り返すことで実現できます。ただし、`Application.OnTime`はVisioのUIスレッドをブロックする可能性があるので、使いすぎには注意が必要です。

3. パフォーマンスのさらなる最適化

非常に大量のシェイプ(数千、数万)を扱う場合、上記の`ScreenUpdating`や`DeferRecalc`に加え、以下も検討してください。

  • トランザクション処理: `Application.BeginUndoScope`は一つのUndo操作としてまとめるだけでなく、パフォーマンス面でもバッチ処理として機能することがあります。
  • `Cell.Result` と `Cell.Formula` の違い: `FormulaU` は計算式を扱いますが、単純に現在の値だけを取得したい場合は `vsoCell.Result(visNoCast)` を使う方が高速な場合があります。しかし、色の指定など、将来的な変更にも対応できる`FormulaU`の方が堅牢性は高いです。

4. ユーザーインターフェース(UI)との連携

今回の例では `MsgBox` で一時停止していますが、より洗練されたUIを提供したい場合は、UserFormを使って「ハイライトを解除」ボタンを設けたり、進行状況バーを表示したりすることも可能です。これにより、ユーザーはより快適にマクロを利用できます。

まとめ:Visio VBAの真髄に触れる一歩

今回の「非破壊ハイライト」のテクニックは、Visio VBAのオブジェクトモデルの理解、`Shape.CellsU`を使った堅牢な属性操作、そして`Dictionary`による状態管理、さらにはパフォーマンス最適化やエラーハンドリングといった、Visio VBAをマスターするための重要な要素が凝縮されています。

単に図形を操作するだけでなく、「ユーザー体験を損なわない」という視点を持ってコードを書くことは、プロのエンジニアにとって非常に大切な心構えです。この考え方を身につければ、皆さんのVBAコードは単なる自動化ツールを超え、Visioを使う人々に真の価値を提供する強力な「相棒」となるでしょう。

今回学んだ知識と「非破壊的」なアプローチは、Visio VBAの様々な場面で応用できます。ぜひ、この知見を胸に、皆さんのVisio自動化の旅をさらに深めていってください。きっと、Visio VBAの真髄に触れる、エキサイティングな発見が待っているはずです!

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