'Shapes.Count' ≠ Shape Index# !
The .Count
is not the same as the upper limit of current shape .Index
numbers.
The numbering system is easier understood by listing all the shapes within the document:
Sub ListShapes()
'hit CTRL+G to view output in Immediate Window
Dim sh As Shape, sld As Slide, idx As Long
Set sld = ActivePresentation.Slides(1) '<-- change to your slide number
For Each sh In sld.Shapes
idx = idx + 1
Debug.Print "Shape ID#" & sh.Id, "Index #" & idx, "Name: " & sh.Name
Next sh
Debug.Print "Count of shapes: " & sld.Shapes.Count
End Sub
NOTE: There is alternative code for Excel at the bottom of this post!
To demonstrate, we can add shapes to a new document:
- First, add one rectangle manually by clicking
Insert
(on the ribbon)
- [If using Excel, click
Illustrations
], then Shapes
, and the rectangle symbol.
- Draw the shape, then hit Ctrl+C to copy it, and hit Ctrl+C four times to paste 4 copies.
Run the above procedure, and the output will be:
Shape ID#2 Index #1 Name: Rectangle 1
Shape ID#3 Index #2 Name: Rectangle 2
Shape ID#4 Index #3 Name: Rectangle 3
Shape ID#5 Index #4 Name: Rectangle 4
Shape ID#6 Index #5 Name: Rectangle 5
Count of shapes: 5
Note that the Index is not a property of this object, but it counted in order that Excel's storing the shapes in memory (same as the order returned by the For Each..Next
statement.
You can prove this by running:
Debug.Print ActivePresentation.Slides(1).Shapes(5).Name
...which in this case return Rectangle 5
.
Another way to understand how Excel is storing the shapes is with the Watch Window. Add a breakline or Stop
in the middle of the loop, then highlight ws.Shapes
, right-click it, choose Add Watch...
and click OK. Browse through the tree to discover the varies properties/attributes of the shapes within the document.
Next, if we delete the "middle rectangle" and run the above procedure again, we will get:
Shape ID#2 Index #1 Name: Rectangle 1
Shape ID#3 Index #2 Name: Rectangle 2
Shape ID#5 Index #3 Name: Rectangle 4
Shape ID#6 Index #4 Name: Rectangle 5
Count of shapes: 4
The ID
and Name
of remaining shapes do not change, but the Index is renumbered to reflect the new "order".
...thus to return the name Rectangle 5
we now need to use:
Debug.Print ActivePresentation.Slides(1).Shapes(4).Name
Referring to shapes (including controls)
When you refer to a shape by number, like .Shapes()
, you're referring to the shape Index Number , not the ID
number. Index numbers are dynamically assigned as needed as therefore are not a stable method to refer to a shape.
- Therefore,
.Count
is irrelevant to the shape Index number.
Ideally, you should refer to the shape by the .Name
or .ID
number. If generating shapes dynamically, you'd ideally store a list of shapes in an array or collection, so you can look at the list as required.
Retrieve "Last Shape Created"
If the only reason for using the Index Number is to retrieve the "last shape created", then you could use a function like this to get the index number:
Function idxLastShape(slideNum As Long) As Long
Dim sh As Shape
For Each sh In ActivePresentation.Slides(slideNum).Shapes
idxLastShape = idxLastShape + 1
Next sh
End Function
Example Usage:
Debug.Print idxLastShape(1) 'Returns index of last shape on slide#1
NOTE: There is alternate code for Excel at the bottom of this post!
Alternatively, you could have the function return a reference to the actual shape object, rather than the number, like this:
Function LastShape(slideNum As Long) As Shape
Dim sh As Shape
For Each sh In ActivePresentation.Slides(slideNum).Shapes
Set LastShape = sh
Next sh
End Function
...so you could get the name of the "last shape" with:
Debug.Print LastShape(1).Name
Delete the most recently created shape
Using the function above, you can use any methods you would normally use with shapes. For example, you can delete the "last shape" that was created on Slide #1:
LastShape(1).Delete
CAUTION!
The examples in the post (including the deletion example!) are indiscriminate of what type of shape they're returning/editing/deleting!
There are dozens of types of shapes, from graphics to sound/video and controls. You can filter the shapes being enumerated by these procedures using the .Type
property of the Shape
object, as well as other methods. There is a partial list here, and more information in the links below.
Alternative code for Excel:
List all shapes on worksheet (Excel)
Sub ListShapes()
'hit CTRL+G to view output in Immediate Window
Dim sh As Shape, ws As Worksheet, idx As Long
Set ws = Sheets("Sheet1") '<-- change to your worksheet name
For Each sh In ws.Shapes
idx = idx + 1
Debug.Print "Shape ID#" & sh.ID, "Index #" & idx, "Name: " & sh.Name
Next sh
Debug.Print "Count of shapes: " & Sheets("Sheet1").Shapes.Count
End Sub
Return index number of "last shape" (Excel)
Function idxLastShape(shtName As String) As Long
Dim sh As Shape
For Each sh In Sheets(shtName).Shapes
idxLastShape = idxLastShape + 1
Next sh
End Function
Example Usage: Debug.Print idxLastShape("Sheet1")
Return reference to "last shape" object (Excel)
Function LastShape(shtName As String) As Shape
Dim sh As Shape
For Each sh In Sheets(shtName).Shapes
Set LastShape = sh
Next sh
End Function
Example Usage: Debug.Print LastShape("Sheet1").Name
More Information:
Other ways to copy from Excel to Powerpoint: