如果Excel中的表(ListObject)的特定列是计算列(如http://office.microsoft.com/en-us/excel-help/use-calculated-columns-in-an-excel-table-HA010342380.aspx中所示),是否还要检入VBA?
请注意,计算列不仅会为每行设置相同的R1C1公式,而且还会在添加新行时自动展开(如果删除整个数据正文范围,它甚至会重新填充,然后重新创建一些新行)。因此,检查具有一致公式的列与检查计算公式不同。
甚至可以计算一列,但要用其他一些公式或值覆盖其中一行,并保持自动扩展功能。
所以我很确信这必须是专栏的一些属性,我只是不确定我可以通过VBA访问它。如果它没有通过VBA对象模型公开,是否有一些工作空间来获取此信息?
提前致谢, 卡洛斯
编辑:
我对Excel Office Open XML文件进行了一些挖掘,结果发现我正在寻找的是xl \ tables \ table * .xml文件的<calculatedColumnFormula>
定义上的<tableColumn>
元素。有没有办法通过VBA达到目的?
EDIT2: 这是一个example file,其中包含我可以提出的测试用例。 VBA应该表示列1,2和3是计算列,第4列和第5列不是。
答案 0 :(得分:4)
之前没有看过这个,但它似乎是ListObject列范围的属性,如下所示:
Dim wks As Worksheet
Set wks = ActiveSheet
Dim li As ListObject
Set li = wks.ListObjects(1)
Dim col As ListColumn
Set col = li.ListColumns(2) ' assuming column 2 of the table has a calculated formula
Dim r As Range
Set r = col.DataBodyRange
Let b = Not IsNull(r.FormulaArray)
if b then
Let b = Len(r.FormulaArray) > 0 ' case where r.FormulaArray = "", suspect it's not a calculated column
End If
MsgBox b
如果是IsNull(r.FormulaArray)那么它没有计算列,否则就会有。
HTH
好的,玩了一下,我看到的是使用上面获得的范围对象与任何给定单元格的范围对象不同,所以如果你有一个给定的单元格,我认为你需要获得通过.DataBodyRange对应ListColumn的范围。
(例如,如果您在上面插入Set r = r.Cells(1,1)
,那么IsNull(r.FormulaArray)
测试不再适用于测试计算列,而只是说该范围是否有公式,但是可以计算或不计算。)
此外,虽然r.FormulaArray在计算列时似乎是一个字符串,但如果它不是(计算列),则.FormulaArray会产生null,这不是一个有效的字符串值(使其变硬)用来捕获你必须使用变量而不是布尔值的值;我发现IsNull(r.FormulaArray)似乎工作正常。
如果我在已计算的列的右侧添加一列,则为该新添加的列添加r.FormulaArray =“”。如果将值放入其中一个单元格中,则公式数组会立即恢复为更符合预期的NULL。所以,我添加了一个测试,我认为这是误报。
答案 1 :(得分:2)
这个为你的例子提供了适当的答案。
不幸的是,根据一个人的情况,它有一些潜在的致命缺点。首先,它会导致重新计算,因此样本中通过=RAND()
公式生成的随机数会发生变化。
第二个缺点是它修改了工作表以获得答案(它删除了它所做的修改,但它仍然被修改)。我可以想到一些只是部分有用的解决方法:(a)根据需要执行此操作并缓存所有列的结果,以及(b)将表复制到新工作簿并运行例程(并删除新工作簿) 。虽然后者会避免修改缺陷,但它仍会触发原始工作簿的重新计算(否则会有其自身的缺点)。除此之外,将表复制到新工作簿会丢失表/ ListObject,除非您复制整个范围(不仅仅是标题);那么它似乎也将第4列(非钙一致公式)推广为计算公式。可悲的是,这种促销也发生在复制整张纸时。
嗯,FWIW:
Sub TestTable()
Dim ans As String
Let ans = ""
Dim li As ListObject
Set li = ActiveSheet.ListObjects(1)
Dim rowCountBefore As Long
Let rowCountBefore = li.ListRows.Count
Dim lr As ListRow
Set lr = Nothing
On Error Resume Next
Set lr = li.ListRows.Add(AlwaysInsert:=True)
On Error GoTo 0
Dim rowCountAfter As Long
Let rowCountAfter = li.ListRows.Count
If Not (lr Is Nothing) And rowCountAfter = rowCountBefore + 1 Then
Dim c As Long
For c = 1 To li.DataBodyRange.Columns.Count
Dim b As Boolean
Let b = lr.Range.Cells(1, c).HasFormula
ans = ans & "col " & c & ": " & b & "; "
Next
li.ListRows(rowCountAfter).Delete
End If
MsgBox ans
End Sub