SelectionManager徹底解説:オブジェクト未選択時の例外ハンドリングとユーザー誘導UIの設計
CorelDRAW VBA開発において、最も頻発し、かつユーザーのフラストレーションを最も高めるバグの筆頭をご存知だろうか。
それは、「オブジェクトが選択されていない状態でマクロを実行したことによる実行時エラー」である。
「選択範囲がありません (No object selected)」――この冷酷なエラーメッセージをユーザーに突きつけるだけのスクリプトは、プロのエンジニアが書いたものとは言えない。業務効率化ツールとして現場に導入した瞬間、ユーザーから「このボタンを押すと時々フリーズする(実際にはVBAのエラーダイアログで止まっているだけ)」というクレームが相次ぐことだろう。
今回は、CorelDRAWの `SelectionManager`(`ActiveSelection` および `ActiveSelectionRange`)の挙動を深く理解し、「絶対にクラッシュしない堅牢な判定ロジック」と、「ユーザーを迷わせずスムーズに代替処理へ誘導するUI設計」の実務ノウハウを、チーフアーキテクトの私から授けよう。
—
1. なぜ「`ActiveSelection.Shapes.Count`」だけでは不十分なのか?
多くの初学者が陥る罠が、以下のような単純な判定コードだ。
‘ 【アンチパターン】これでは実務に耐えられない
Sub BadExample()
Dim sh As Shape
‘ 何も選択されていない状態でここを通るとエラーの温床になる
Set sh = ActiveSelection.Shapes(1)
sh.Fill.ApplyNoFill
End Sub
あるいは、少しVBAをかじった者であれば、カウント数を見るだろう。
‘ 【まだ甘いパターン】
Sub SlightlyBetter()
If ActiveSelection.Shapes.Count = 0 Then
MsgBox “オブジェクトを選択してください。”, vbExclamation
Exit Sub
End If
‘ 処理続行…
End Sub
これで動くこともある。だが、CorelDRAWのオブジェクトモデルの裏側――オブジェクトのライフサイクルとメモリ管理を知る我々エンジニアにとって、これだけでは不十分だ。
CorelDRAWでは、グループ化されたオブジェクト、レイヤーのロック状態、非表示レイヤー、さらにはOLEオブジェクトやガイドラインなど、`SelectionRange` に含まれる実体の状態は非常に複雑である。単に `Count > 0` を見るだけでは、予期せぬ型ミスマッチや、参照切れによる実行時エラー(エラー438: オブジェクトは、このプロパティまたはメソッドをサポートしていません等)を防ぎきれない。
真に堅牢なコードを書くためには、「SelectionManagerの正確な状態判定」と「ユーザーへの優美なフォールバック(誘導)」を一体化させる必要がある。
—
2. プロダクション品質:堅牢なセレクション判定アーキテクチャ
実務でそのまま使える、洗練された判定ロジックのテンプレートを提示する。
このコードは、単にエラーを防ぐだけでなく、選択状態に応じた「分岐処理」までをカプセル化している。
Option Explicit
”’
”’
Sub ProductionReadyProcess()
‘ 1. セレクションの安全性を厳密にチェック
If Not ValidateSelection() Then Exit Sub
‘ 2. ここから先は安全にオブジェクトを操作できる
ExecuteBusinessLogic
End Sub
”’
”’
Private Function ValidateSelection() As Boolean
Dim sr As ShapeRange
‘ ActiveSelectionRangeを取得(ActiveSelectionよりもRangeオブジェクトを使うのがプロの常道)
Set sr = ActiveSelectionRange
‘ 【検証1】セレクション自体が取得できているか、かつカウントが0ではないか
If sr Is Nothing Then
Call GuideUser(“オブジェクトが選択されていません。”, “処理を中断しました。対象を選択してから再度実行してください。”)
ValidateSelection = False
Exit Function
End If
If sr.Count = 0 Then
Call GuideUser(“オブジェクト未選択”, “この機能を使用するには、少なくとも1つのオブジェクトを選択してください。”)
ValidateSelection = False
Exit Function
End If
‘ 【検証2:高度な要件】ロックされたオブジェクトのみが選択されていないか等の追加チェックもここに挟む
‘ 例外的な環境要因によるすり抜けを防ぐ
ValidateSelection = True
End Function
”’
”’
Private Sub GuideUser(ByVal title As String, ByVal message As String)
‘ 現場のオペレーターが混乱しないよう、ビープ音と共に明確なモーダルダイアログを表示
Beep
MsgBox message, vbExclamation + vbOKOnly, “CorelDRAW 業務自動化アシスタント – ” & title
‘ 将来的には、ここで「自動的に全選択ツールに切り替える」「レイヤーパレットを開く」などの
‘ 代替UI(ユーザー誘導)をフックさせる拡張性を持たせておく。
End Sub
”’
”’
Private Sub ExecuteBusinessLogic()
Dim sh As Shape
‘ トランザクションの開始(CorelDRAWではOptimizationを使うのが鉄則)
Optimization = True
EventsEnabled = False
On Error GoTo ErrorHandler
For Each sh in ActiveSelectionRange
‘ 実処理の記述(例:選択図形のバウンディングボックス取得や属性変更)
‘ Debug.Print sh.Name
Next sh
CleanUp:
Optimization = False
EventsEnabled = True
ActiveDocument.Windows(1).Refresh
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical
Resume CleanUp
End Sub
—
3. チーフアーキテクトからの設計指針:なぜこのコードが優れているのか
1. `ActiveSelectionRange` の採用
単なる `ActiveSelection` プロパティではなく、明示的に `ShapeRange` オブジェクトを取得して評価している。これにより、単一選択・複数選択の差異を吸収し、統一されたメソッドチェーン(`Count` や `For Each` の安全性)の恩恵を受けられる。
2. 関心事の分離 (Separation of Concerns)
「選択されているかどうかの検証 (`ValidateSelection`)」「ユーザーへのフィードバック (`GuideUser`)」「実際の処理 (`ExecuteBusinessLogic`)」を完全にモジュール分割している。これにより、保守性が劇的に向上し、他のマクロへもこのバリデーション層をそのまま流用できる。
3. パフォーマンスと安定性の両立 (`Optimization` と `EventsEnabled`)
選択オブジェクトをループ処理する際、画面の再描画やイベント監視が有効なままだと、CorelDRAWは激しく重くなり、最悪の場合クラッシュする。安全にキャッチした後の実処理ブロックでは、必ずこれらを無効化する配慮を忘れてはならない。
—
4. おわりに:ツールは「人に優しく」あってこそ
業務自動化の目的は、現場の作業時間を短縮し、ヒューマンエラーを根絶することだ。
マクロがエラーで強制終了し、「デバッグなんて分からない!」と困惑するオペレーターの姿を想像してほしい。それは自動化ではなく、新たな「ストレスの製造」に他ならない。
今回解説した `SelectionManager` の堅牢なハンドリングとユーザー誘導UIの設計は、君が作るツールを「おもちゃ」から「プロフェッショナルな基幹システムの一部」へと昇華させるための必須教養である。
次の開発から、ぜひこの設計思想を取り入れてみてほしい。コードの質が劇的に変わることを約束しよう。
