Excel VBA:如何检查计算列?

问题描述 投票:0回答:2

是否有任何方法可以在 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 列不是。

excel calculated-columns listobject vba
2个回答
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。 因此,我添加了一个测试来检测我认为的误报。


2
投票

这个为您的示例提供了适当的答案。

不幸的是,它有一些潜在的致命缺点,具体取决于个人的情况。 其一,它会导致重新计算,因此样本中通过

=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
© www.soinside.com 2019 - 2024. All rights reserved.