選択中の図形(Selection)をFor Each文を使ってループし、
各図形をShapeオブジェクトとして取得・処理する方法を解説します。
ソースコード
Sub 選択中の図形に処理を実行する() ' 選択しているのが図形でなければExit On Error Resume Next Dim 選択ShapeRange As ShapeRange Set 選択ShapeRange = Selection.ShapeRange If 選択ShapeRange Is Nothing Then Exit Sub On Error GoTo 0 ' Selection内の全Shapeをループ Dim シェイプ As Shape For Each シェイプ In 選択ShapeRange ' 処理のサンプル①:テキストを持つ図形だけを削除 If シェイプ.TextFrame2.HasText Then シェイプ.Delete End If ' 処理のサンプル②:グラフ以外の図形を削除 If シェイプ.Type <> "Chart" Then シェイプ.Delete End If Next End Sub
' 選択中の図形が確実に1つとわかる場合はこれでもOK Dim シェイプ As Shape Set シェイプ = Selection.ShapeRange(1) ' 処理のサンプル:図形テキストをクリア シェイプ.TextFrame2.TextRange.Text = ""
解説
シート上の図形に対して処理を実行するには、
通常はShapeオブジェクトのプロパティ/メソッドを実行します。
シート上の全図形は「Worksheetオブジェクト.Shapes」で取得できますので、
以下の処理であれば比較的ストレートなコードで記述することが可能です。
▼ シート上にあるすべての図形をShape型として取得
Dim シェイプ As Shape For Each シェイプ In Worksheets("○○").Shapes
▼ 特定の名前の図形をShape型として処理
Dim シェイプ As Shape Set シェイプ = Worksheets("○○").Shapes("TextBox 1")
しかし問題はSelection内の図形をShape型として取得したい場合で、
図形を選択していてもSelectionはShapesコレクションになりません。
' ↓こう書くことはできない(詳細は後述) Dim シェイプ As Shape For Each シェイプ In Selection
この対策として使用するのがShapeRangeプロパティで、
このプロパティは様々な図形オブジェクトをShapeのリストにしてくれます。
' ↓これでOK Dim シェイプ As Shape For Each シェイプ In Selection.ShapeRange
厳密にはちょっと違うのですが、
「ShapeRangeは図形ObjectをShapesコレクションに変換するプロパティ」
と捉えるとわかりやすいかもしれません。
単独の図形も要素1つのShapesコレクション(のよう)にしてくれるので、
For Each文がそのまま動いてくれるのもありがたい仕様ですね。
ついでにこのプロパティは選択しているのが図形かを判定するのにも使え、
前述のこのコードでSelectionがRangeなどの場合を除外できます。
' 選択しているのが図形でなければExit On Error Resume Next Dim 選択ShapeRange As ShapeRange Set 選択ShapeRange = Selection.ShapeRange If 選択ShapeRange Is Nothing Then Exit Sub On Error GoTo 0
SelectionのShape化、Selectionの対象判定共に必須の処理ですので、
選択図形を扱う際はShapeRangeプロパティの仕様をしっかり習得しておきましょう。
なお、サンプルコードにも記載しましたが、
選択中の図形が確実にひとつとわかる場合は以下のコードで実行できます。
' 選択中の図形が確実に1つとわかる場合はこれでもOK Dim シェイプ As Shape Set シェイプ = Selection.ShapeRange(1) ' 処理のサンプル:図形テキストをクリア シェイプ.TextFrame2.TextRange.Text = ""
ShapeRangeがShapesコレクションと同じようなものですので、
ShapeRange(1)でその第1要素をShape型として取得することができます。
こちらで問題なければFor Each文は不要ですので、
このコードを用いて処理を実装してください。
おまけ:Selectionの仕様とDrawingObjects型について
今回はSelectionをShape型として取得する方法を解説しました。
つまりSelectionは図形選択中でもそのままではShape型ではないわけですが、
じゃあ何が入っているかというと、DrawingObjects型が入っています。
より正確に、図形を選択しているときのSelectionは、
- 単独の図形であればTextBoxやRectangleなど図形固有のオブジェクト
- 複数の図形を選択している場合はDrawingObjectsオブジェクト
- DrawingObjectsをForEachで回した際の中身はTextBoxなど図形固有のオブジェクトで、つまりDrawingObjectsの中身の型は統一されていない。
を取得します。
よって、Shape型にストレートに入れようとする↓のようなコードを書いてしまうと、
以下いずれかのエラーとなり、いずれの場合も正しく動きません。
Dim シェイプ As Shape ' ③ ではAs Variant For Each シェイプ In Selection If シェイプ.TextFrame2.HasText Then シェイプ.Delete End If Next
① 選択しているのが単独の図形の場合
SelectionがTextBox型などの単独図形で配列でもコレクションでもないため、
そもそもFor Eachステートメントが動かず以下のエラーとなります。
| 実行時エラー '438': オブジェクトは、このプロパティまたはメソッドをサポートしていません。 |
② 選択しているのが複数の図形の場合
前述の通りDrawingObjects型の中身はShape型ではないため、
For Each の開始と同時に以下のエラーとなります。
| 実行時エラー '13': 型が一致しません。 |
③ ForEach変数をVariantにした場合
選択図形によってTextBox型やRectangle型など固有オブジェクトが入ります。
これらはShape型のプロパティ・メソッドを持っているとは限りません。
「.Top」「.Left」などは持っているため同じコードでも動くのですが、
今回使った「.TextFrame2」は持っていないため、以下のエラーとなります。
| 実行時エラー '438': オブジェクトは、このプロパティまたはメソッドをサポートしていません。 |
このあたりのエラーが出た場合は、
上記のコードを書いてしまっていないか確認してください。
さてこのDrawingObjects型ですが、これはExcelの古いオブジェクトで、
現在は非表示のメンバーになっています。
今はより新しく機能も多いShape型が実装されていますので、
わざわざ使う必要はないということですね。
しかしながら、昔のマクロやマクロの記録機能との互換性を維持するために、
Selectionが取得するオブジェクトはDrawingObjects型のままになっています。
昔の記述を使えばDrawingObjects型のまま処理することもできるはできますが、
機能面、ネットの情報の多さともに、Shape型より不便なことがほとんどです。
Selectionを使用する際は本記事のように、
ShapeRangeを通してShape型を処理するコードを書きましょう。