Powerpoint VBA - 在嵌入式 Excel OLE Object 中编辑 Table 的列名称

Powerpoint VBA - Editting Column Name of Table in Embedded Excel OLE Object

如果你 运行 下面的代码你会得到一个非常有趣的结果(只有 PowerPoint 运行ning,在 运行ning 之前关闭所有 Excel 的实例):

'Optional - Include the "Microsoft Excel 16.0 Object Library"
Option Explicit

Public Sub test()    
    Dim oslide As slide
    Set oslide = ActivePresentation.Slides.add(1, ppLayoutBlank)

    Dim oshape As Shape 
    Set oshape = oslide.Shapes.AddOLEObject(30, 30, 50, 50, "Excel.Sheet")

    oshape.OLEFormat.Object.Sheets(1).ListObjects.add(1) 'xlSrcRange
    oshape.OLEFormat.Object.Sheets(1).Cells(1, 1) = "fewewq"

    oshape.OLEFormat.Object.Close
End Sub

嵌入的 object 已成功创建并且 table 与指定数据一起出现。但是,当您单击嵌入的 object 时,您会收到以下错误:

There isn't enough memory available to read Worksheet.

此 object 无法再以任何其他方式访问,object 的损坏性质在文档 close/open 上持续存在并重新启动。我已验证此问题发生在除我测试过的一个系统之外的所有系统上(PowerPoint/Excel 2016,Windows 7 X64)。

问题

所以我的问题是,任何人都可以重现这个吗?如果可以,为什么会这样?如果将行 "Cells(1, 1)" 更改为 "Cells(2, 1)" 没有问题,似乎编辑 table 的头部会导致某种特殊行为,这与编辑行或其他单元格不同.

研究

更新 1

我用图表尝试了同样的事情:

'Include the "Microsoft Excel 16.0 Object Library"
Option Explicit

Sub test()
    Dim sld As slide
    Dim shp As Shape
    Dim pptWorkbook As Workbook

    Set sld = ActivePresentation.Slides.Add(1, ppLayoutBlank)
    Set shp = sld.Shapes.AddChart
    Set pptWorkbook = shp.Chart.ChartData.Workbook
    pptWorkbook.Close SaveChanges:=True

    Set pptWorkbook = shp.Chart.ChartData.Workbook
    pptWorkbook.Sheets(1).Cells(1, 2) = "fewewq"

    Application.ActivePresentation.Save
    pptWorkbook.Close SaveChanges:=True
End Sub

如果您更改 header 行值,您将无法再访问嵌入的 object("Cells(1, 2)"),如果您更改另一个值 ("Cells(2, 1)") 它 运行很好。我假设这是同样的问题,我无法打开此代码后的图表数据 运行。如果我尝试以编程方式访问它,我会收到以下错误:

Run-time error '-2147467259 (80004005)':

Method 'Workbook' of object 'ChartData' failed

虽然只是 2016 年的问题,但我在 2010 年尝试了一些略有不同的方法,但没有发现任何问题。

更新 2

我终于明白为什么我不能在另一个系统上重现这个问题了。仅当 excel 的所有实例在进行更改后关闭时才会出现此问题。这意味着如果您在 运行 这段代码时打开一个单独的(不相关的)excel window,您将看不到问题。

只有当 PowerPoint 单独 运行 并且没有打开任何其他 Excel 电子表格时才能重现此问题。

它适用于以下内容(不添加 Excel 常量的 Excel 参考和实际值,在 Win7x64 上使用 PowerPoint 2010x86):

Option Explicit

Sub test()
    Dim sld As Slide
    Dim shp As Shape

    Set sld = ActivePresentation.Slides.Add(1, ppLayoutBlank)
    Set shp = sld.Shapes.AddOLEObject(30, 30, 50, 50, "Excel.Sheet")

    Dim listObject As Object ' listObject
    Set listObject = shp.OLEFormat.Object.Sheets(1).ListObjects.Add(1) 'xlSrcRange = 1
    shp.OLEFormat.Object.Sheets(1).Cells(1, 1) = "fewewq"

    shp.OLEFormat.Object.Close

End Sub

只是不确定为什么设置了对象却不使用它们。

我找到了一个真正 "awesome" 的解决方法,至少对于表格而言:

Public Sub CreateTable()

    'Create a dummy excel object in the background(run this before working with OLE objects)
    Dim xlApp As Object
    Set xlApp = CreateObject("Excel.Application")
    xlApp.Workbooks.Add

    Dim slide As slide: Set slide = ActivePresentation.Slides.Add(1, ppLayoutBlank)
    Dim shp As Shape: Set shp = slide.Shapes.AddOLEObject(30, 30, 50, 50, "Excel.Sheet")

    shp.OLEFormat.Object.Sheets(1).ListObjects.Add (1) 'xlSrcRange
    shp.OLEFormat.Object.Sheets(1).Cells(1, 1) = "fewewq"

    shp.OLEFormat.Object.Close

    'Kill it when the work is done
    xlApp.Application.Quit

End Sub

图表版本:

Public Sub CreateChart()

    'Create a dummy excel object in the background(run this before working with OLE objects)
    Dim xlApp As Object
    Set xlApp = CreateObject("Excel.Application")
    xlApp.Workbooks.Add

    Dim sld As slide
    Dim shp As Shape
    Dim pptWorkbook As Workbook

    Set sld = ActivePresentation.Slides.Add(1, ppLayoutBlank)
    Set shp = sld.Shapes.AddChart

    Set pptWorkbook = shp.Chart.ChartData.Workbook
    pptWorkbook.Close SaveChanges:=True

    'Use the Activate code to open the worksheet, typically only need for 2010
    xlApp.Application.Wait Now + TimeValue("0:00:01")
    shp.Chart.ChartData.Activate
    shp.Chart.ChartData.Workbook.Windows(1).Visible = False

    Set pptWorkbook = shp.Chart.ChartData.Workbook
    pptWorkbook.Sheets(1).Cells(1, 2) = "fewewq"

    Application.ActivePresentation.Save

    'Added a wait condition before closing the document, not sure why this works...
    Excel.Application.Wait Now + TimeValue("0:00:01")
    pptWorkbook.Close SaveChanges:=True

    'Kill it when the work is done
    xlApp.Application.Quit

End Sub

当然,我对答案并不满意,因为它只是避免了问题,而不是解决根本原因。我仍然想更好地了解导致此行为的原因。如果失败,以真正的 VBA 方式,这可能是唯一可行的选择。

我可以在 Windows10-64/Excel2013-64 上一致地重现您的问题。这是一个错误,我们只能尝试检查到底出了什么问题。

将table的header更改为VBA时,ListColumn固执地拒绝更新其名称。无论您是更改 header 单元格还是显式更改 ListColumn 的名称,都会发生这种情况!它仅在您编辑工作簿并手动更改单元格时更新,而不是从 VBA:

Public Sub Test()
    Dim oslide As Slide
    Set oslide = ActivePresentation.Slides.Add(1, ppLayoutBlank)

    Dim oshape As Shape
    Set oshape = oslide.Shapes.AddOLEObject(30, 30, 250, 250, "Excel.Sheet")

    With oshape.OLEFormat.Object
      .Sheets(1).ListObjects.Add 1, .Sheets(1).Range("B2:D5") ' <-- put it anywhere


      .Sheets(1).ListObjects(1).ListColumns(1).Name = "fewewq" ' <-- whether like this
      '.Sheets(1).Range("B2").Value = "fewewq"                 ' <-- or like this

      Debug.Print .Sheets(1).ListObjects(1).ListColumns(1).Range.Cells(1).Value 'fewewq

      Debug.Print .Sheets(1).ListObjects(1).ListColumns(1).Name
      ''''''''''''''' Still prints Column1 ! ''''''''''''''''''

      .Close
    End With
End Sub

结果很明显:ListObject table 被损坏,因为它在内部保存了在 [=81= 中找不到的列名(即 Column1) ](header 是 fewewq)。这导致了观察到的错误,不幸的是,显示的错误消息并不总是准确的。

当 Excel 实例已经是 运行 时,行为会改变并且 ListColumn 的名称 更新。似乎在编辑 header 时更新 table 内部数据的 "component" 在 PowerPoint VBA 中编辑时是 "not loaded"。仅当:

  • 您在PPT中编辑工作簿in-place,手写

  • 您有一个 Excel 实例 运行

共同点是加载了一些编辑器组件,而此编辑器是在编辑 header 时更新内部 table 数据的编辑器。

您在答案中找到的很好的解决方法,即在操作之前打开一个 xlApp,然后在操作之后将其关闭,与这些观察结果一致。

重要的是,Chartobject(在Update 1中)出现的"other"问题确实和你一样正确假设 ("I assume it's the same problem")。创建的图表链接到工作表中的 ListObject table,并且 table 在第一行有其 header。因此,当您更改 header 中的单元格时,ListColumn 的名称不会更新,从而导致同样的损坏问题。

更新:另一个轻量级解决方法

在评论提出与打开先前 Excel 应用程序的解决方法相关的问题后,我试图找到一个 "lighter" 解决方法并找到了一个。

确信问题是由于未能在 table 中更新 ListColumn 的名称,我找到了 "force it" 更新其名称的方法。解决方法包括两个步骤:

将table的范围向右扩展一列,然后立即将其缩小回原来的范围。

这个操作只是强制 table 到 re-calculate 它的列名,仅此而已!现在 table 的列名是正确的,问题也消失了。

Public Sub Workaround()
  Dim oslide As Slide: Set oslide = ActivePresentation.Slides.Add(1, ppLayoutBlank)
  Dim oshape As Shape: Set oshape = oslide.Shapes.AddOLEObject(30, 30, 250, 250, "Excel.Sheet")

  With oshape.OLEFormat.Object.Sheets(1)
    .ListObjects.Add 1 ' xlRange
    .Range("A1").Value = "fewewq"

   ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
   ' Expand the table one column to the right than shrink it back
   '
    With .ListObjects(1)
      .Resize .Range.Resize(, .Range.Columns.Count + 1)
      .Resize .Range.Resize(, .Range.Columns.Count - 1)
    End With
   '
   ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  End With

  With oshape.OLEFormat.Object
    Debug.Print .Sheets(1).ListObjects(1).ListColumns(1).Range.Cells(1).Value ' fewewq
    Debug.Print .Sheets(1).ListObjects(1).ListColumns(1).Name ' Now prints fewewq !
    .Close
  End With
End Sub

完成此操作后,您可以验证嵌入的工作表是editable,您可以关闭然后打开演示文稿,不会发现任何问题。我希望这会有所帮助:)