是否有任何方法可以在 VBA 中检查 Excel 中表格(ListObject)的特定列是否是计算列(如 https://support.microsoft.com/en-us/office/use-calculated-columns -in-an-excel-table-873fbac6-7110-4300-8f6f-aafa2ea11ce8)?
请注意,计算列不仅每行具有相同的 R1C1 公式集,而且还会在添加新行时自动扩展(如果删除整个数据主体范围然后重新创建一些新行,它甚至会重新填充)。因此,检查具有一致公式的列与检查计算公式不同。
甚至可以计算一列,但用其他公式或值覆盖其中一行,并保留自动扩展功能。
所以,我非常确信这一定是列的某些属性,我只是不确定在哪里可以通过 VBA 访问它。如果它没有通过 VBA 对象模型公开,是否有一些解决方法来获取此信息?
提前致谢, 卡洛斯
编辑: 我对 Excel Office Open XML 文件进行了一些挖掘,结果发现我正在寻找的是 xlablesable*.xml 文件的
<calculatedColumnFormula>
定义上的 <tableColumn>
元素。有什么办法可以通过VBA实现这一点吗?
编辑2: 这是一个示例文件,其中包含我可以提出的测试用例。 VBA 应指示这些列 1、2 和 3 是计算列,而第 4 和 5 列不是。
之前没有看过这个,但它似乎是 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
好吧,玩了一下,我看到使用上面获得的范围对象与任何给定单元格的范围对象不同,所以如果你有一个给定的单元格,我想你将需要获取相应的 ListColumn通过.DataBodyRange 的范围。
(例如,如果您在上面插入
Set r = r.Cells(1,1)
,那么 IsNull(r.FormulaArray)
测试不再用于测试计算列,而只是表示范围是否有公式,但可以计算或不计算。 )
此外,虽然在计算列时 r.FormulaArray 看起来是一个字符串,但如果它不是(计算列),则 .FormulaArray 会生成 null,这不是有效的字符串值(使其难以使用要捕获值,您必须使用变体而不是布尔值);我发现 IsNull(r.FormulaArray) 似乎工作正常。
如果我在已计算列的右侧添加一列,则 r.FormulaArray = "" 对于新添加的列。 如果您将一个值放入其中一个单元格中,公式数组会立即恢复为更预期的 NULL。 因此,我添加了一个测试来检测我认为的误报。
这个为您的示例提供了适当的答案。
不幸的是,它有一些潜在的致命缺点,具体取决于个人的情况。 其一,它会导致重新计算,因此样本中通过
=RAND()
公式生成的随机数会发生变化。
第二个缺点是它修改工作表以获得答案(它删除了所做的修改,但仍然被修改)。 我可以想到一些仅部分有用的解决方法:(a)根据需要很少执行此操作并缓存所有列的结果,以及(b)将表复制到新工作簿并运行例程(并删除新工作簿) 。 虽然后者可以避免修改缺点,但它仍然会触发原始工作簿的重新计算(并且有其自身的缺点)。 除此之外,将表复制到新工作簿会丢失表/ListObject,除非复制整个范围(而不仅仅是标题);那么它似乎还将第四列(非计算一致公式)提升为计算公式。 可悲的是,复印整张纸时也会发生这种促销。
嗯,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