use*_*216 5 excel vba calculated-columns listobject
如果Excel中的表(ListObject)的特定列是计算列(如http://office.microsoft.com/en-us/excel-help/use-calculated-columns-),是否可以在VBA中进行检查在-excel-表-HA010342380.aspx)?
请注意,计算列不仅每行具有相同的 R1C1 公式集,而且还会在添加新行时自动扩展(如果删除整个数据主体范围然后重新创建一些新行,它甚至会重新填充)。因此,检查具有一致公式的列与检查计算公式不同。
甚至可以计算一列,但用某些其他公式或值覆盖其中一行,并保留自动扩展功能。
所以我非常确信这一定是该列的某些属性,我只是不确定在哪里可以通过 VBA 访问它。如果它没有通过 VBA 对象模型公开,是否有一些解决方法来获取此信息?
预先感谢,卡洛斯
编辑:我对 Excel Office Open XML 文件进行了一些挖掘,结果发现我正在寻找的是xl\tables\table*.xml 文件定义<calculatedColumnFormula>上的元素。<tableColumn>有什么办法可以通过VBA实现这一点吗?
EDIT2:这是一个示例文件,其中包含我可以提出的测试用例。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
Run Code Online (Sandbox Code Playgroud)
如果 IsNull(r.FormulaArray) 则它没有计算列,否则它有。
哈
好的,玩了一下,我看到使用上面获得的范围对象与任何给定单元格的范围对象不同,所以如果你有一个给定的单元格,我认为你将需要获取相应的 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
Run Code Online (Sandbox Code Playgroud)