Pasted Shape not seen as “Latest” Shape

假如想象 提交于 2019-12-02 05:07:08

问题


I'm in the process of automating the production of a PowerPoint report from and Excel spreadsheet. I've got the process working up until I paste a table.

I'm pasting the table to PowerPoint using PPApp.CommandBars.ExecuteMso ("PasteSourceFormatting") and the table appears as a shape on my slide (the third shape).

To refer to the new shape I was using Set pShape = Slide2.Shapes(Slide2.Shapes.Count) but now now when I paste, the pShape is assigned "Shape 2" (not "Shape 3"). Is there something that needs to be done between the pasting and the assignment of the object?

Code below, commented where the issue occurs. (Full code removed; viewable here)

'Copy tables from Excel
Set rng = ws.Range("A:A")
rng.ColumnWidth = 22.75
Set rng = ws.Range("A4:C27")

'Copy the table range
Application.CutCopyMode = False
rng.Copy
Application.Wait (Now + TimeValue("0:00:02"))

'The issue occurs here!!! '-------------------------------------
'Paste the table in to the slide
Slide2.Select
PPApp.CommandBars.ExecuteMso ("PasteSourceFormatting")

'Name the new shape object
Set pShape = Slide2.Shapes(Slide2.Shapes.Count)
pShape.Name = "Slide_2_Table_1"
pShape.LockAspectRatio = False

回答1:


'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:

  • MSDN : Shapes Object (PowerPoint/VBA)
  • MSDN : Shapes Object (Excel/VBA)
  • MSDN : MsoShapeType Enumeration (Office)
  • Stack Overflow : Overview of working with Form Controls and ActiveX Controls
  • MSDN : Working with Shapes (Drawing Objects)
  • Office.com : How to add Shapes
  • BreezeTree : Programming Shapes (AutoShapes) with VBA
  • WiseOwl : Working with Shapes (Tutorial)

Other ways to copy from Excel to Powerpoint:

  • SpreadsheetGuru : Copy & Paste An Excel Range Into PowerPoint With VBA
  • ExcelOffTheGrid : Controlling Powerpoint from Excel using VBA
  • mvps.org : Paste Excel chart as pictures in PowerPoint (Paste Special)


来源:https://stackoverflow.com/questions/51169904/pasted-shape-not-seen-as-latest-shape

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!