【发布时间】:2019-02-04 20:14:14
【问题描述】:
我正在同时做几件我认为可能会导致问题的事情。我已经在 Windows 10 下的 Office 2013 和 Office 2016 中测试了这个 VBA。
我有许多工作表,每个工作表的标题都基于月份和年份(例如:“2018 年 11 月”、“2018 年 12 月”等)。我正在使用 VBA 做两件(单独的)事情:
- 获取活动工作表的名称
- 遍历前一个工作表的数据
VBA 代码:
Public Function RelSheet(iPos As Integer, zRange As String)
'Relative Worksheet Reference Facility
'eg: =RelSheet(-1,"A3") = Cell A3 in Previous (Left) WSheet
'eg: =RelSheet(1,"A3") = Cell A3 in Next (Right) WSheet
'eg: "#Error" when reference does not exist
'eg: Can do maths =RelSheet(1,"A3")*2
Dim shtActive As Worksheet
Application.Volatile True
Set shtActive = Application.Caller.Worksheet
On Error GoTo BadSheetReference
RelSheet = Sheets(shtActive.Index + iPos).Range(zRange).Value
GoTo ExitFunction
BadSheetReference:
RelSheet = "#Error"
ExitFunction:
End Function
Function TabName()
TabName = ActiveSheet.Name
End Function
在我的工作表中,我逐月统计总和,直到创建近一年,然后再次从 0(或 Jannuary 包含的任何值)开始统计。单元格 C8 是当月的值,单元格 C9 是上个月的值(上一个工作表的 C9)+ C8 中当前工作表单元格的值的总和。该单元格 (C9) 的公式如下:
==IF(ISNUMBER(SEARCH("January", TabName())), C8, RelSheet(-1, "C9")+C8)
不幸的是,一旦工作表名称确实包含“January”的文本,所有以前的工作表也会恢复为总和 0。我相信它与 RelSheet 函数有关,本质上是递归检查本身,但是当我在纸上逻辑地单步执行代码时,我看不出它是如何做到的。我目前的解决方法是手动将 1 月份工作表中特定单元格的值设置为 0,然后在后续电子表格中继续该公式。
【问题讨论】:
-
默认情况下
Sheets()将引用活动工作簿 - 您是否打开了多个工作簿?TabName = ActiveSheet.Name也可能是非活动工作表的问题 -
Sheets是一个可枚举的,并且不能保证索引以任何方式排序。迭代Worksheets集合并检查名称会更好。如果用户重新订购床单会怎样? -
@Comintern - 你确定吗?我从来没有遇到过(例如)
Sheets(2)没有引用工作簿中的第二个选项卡的情况(假设至少有两个选项卡并且没有隐藏的选项卡) -
注意“活动工作表”在这里具有误导性 - 它不是
ActiveSheet,而是调用 UDF 的任何工作表:该工作表是否“活动”没有区别。我建议将shtActive重命名为例如callerSheet和不合格的Sheets集合,正如 Tim 在上面正确警告的那样,隐含地引用了ActiveWorkbook是什么,这意味着 UDF 将根据预期的书当前是否处于活动状态进行不同的评估。请改用ThisWorkbook.Worksheets,以保证确定性结果。 -
没有 - 但
Sheets集合包含非工作表;当你知道你得到一个Worksheet对象时使用Worksheets集合(而不是,比如说,一个Chart表)。重要的部分是ThisWorkbook限定符,没有它,您获得的工作表取决于当前处于活动状态的工作簿,或者(在其他情况下)甚至 VBA 项目中的哪个位置编写代码:相同的代码在标准模块中编写的行为可能与在工作表模块中编写时的行为方式不同,除非它使用Workbook对象引用正确/明确地限定。