和風スパゲティのレシピ

日本語でコーディングするExcelVBA

選択中の図形(Shape)を処理する - ShapeRange

選択中の図形(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型を処理するコードを書きましょう。