Worksheet_Calculate()事件处理程序中的全局变量值始终不正确

时间:2018-03-25 13:29:39

标签: excel vba excel-vba

我的全局变量存在问题,其值并不总是正确的。

我已经宣布这样:

Public NumRows As Long

它用于保存Sheet 1上已使用的行数。该变量是公共的,因为多个宏使用它。

它在这里初始化:

Private Sub Workbook_Open()
  NumRows = Worksheets("TableSize").Range("A1").Value
  MsgBox "NumRows = " & NumRows
End Sub
插入了

MsgBox只是为了验证代码是否正常工作。工作表TableSize仅包含A1中的信息。此时,NumRows = 32是正确的。

这是Sheet TableSize中导致问题的事件处理程序:

Private Sub Worksheet_Calculate()    

    Dim n As Long
    n = Worksheets("TableSize").Range("A1").Value 'A1 contains the formula "=ROW(INDEX(Sheet1,1,1))+ROWS(Sheet1)-1" used to count rows on Sheet 1
    MsgBox n & "NumRows=" & NumRows
    If n = NumRows Then Exit Sub
    If n > NumRows Then Call NewDatabaseEntry

    NumRows = n    

End Sub

MsgBox会为32返回n,但0会为NumRows,即使NumRows已在Workbook_Open()中正确设置{1}}处理程序!

希望n总是等于NumRows,除非我实际添加一行,这样才会NewDataEntry(正确地)在另一张纸上创建新数据条目。< / p>

我错过了一些关键的东西吗?

2 个答案:

答案 0 :(得分:1)

我假设您不在模块中使用Option Explicit,并将Private Sub Workbook_Open()放入Workbook模块,将Worksheet_Calculate放入工作表模块。由于缺少Option Explicit,您将不会收到NumRows是未定义变量的错误消息。您无法像在类模块中那样定义和使用全局变量。为了修复代码,您可以将以下代码放入工作簿模块

Option Explicit

Public NumRows As Long

Private Sub Workbook_Open()
  NumRows = Worksheets("TableSize").Range("A1").Value
  MsgBox "NumRows = " & NumRows
End Sub

Private Sub Workbook_SheetCalculate(ByVal Sh As Object)

Dim n As Long

    If Sh.Name = "TableSize" Then

        n = Worksheets("TableSize").Range("A1").Value    'A1 contains the formula "=ROW(INDEX(Sheet1,1,1))+ROWS(Sheet1)-1" used to count rows on Sheet 1
        MsgBox n & "NumRows=" & NumRows
        If n = NumRows Then Exit Sub
        If n > NumRows Then
            Debug.Print "NewDatabaseEntry"
        End If

        NumRows = n
    End If

End Sub

另一种选择是使用以下代码添加标准模块

Option Explicit

Public NumRows As Long

Private Sub Auto_Open()
  NumRows = Worksheets("TableSize").Range("A1").Value
  MsgBox "NumRows = " & NumRows
End Sub

在工作表模块中,您可以保留代码

Option Explicit

Private Sub Worksheet_Calculate()

Dim n As Long
n = Worksheets("TableSize").Range("A1").Value    'A1 contains the formula "=ROW(INDEX(Sheet1,1,1))+ROWS(Sheet1)-1" used to count rows on Sheet 1
    MsgBox n & "NumRows=" & NumRows
    If n = NumRows Then Exit Sub
    If n > NumRows Then
        Debug.Print "NewDatabaseEntry"
    End If

    NumRows = n

End Sub

答案 1 :(得分:1)

实际上,解决方案非常简单。它涉及两个部分。

第一部分解决了您的直接问题。您需要做的就是将NumRows全局变量声明从“ThisWorkbook”模块(现在看起来像你现在的位置)移动到标准模块。 (您在上一个问题中提到的“Module1”模块很好。)

您的Workbook_Open()事件处理程序仍然可以访问该变量,并且每个其他模块中的代码都是如此,并且所有内容仍然可以正常工作(或者如果有其他问题,则仍然可以正常工作)。

第二部分解决了为什么你所做的不会导致错误的更普遍的问题。它还有助于避免将来出现许多其他问题。正如Storax建议的那样,虽然我会说它更强一些,但必须使用每个代码模块顶部的Option Explicit语句。

执行此操作的最佳方法是设置VBIDE以自动执行此操作。转到Tools > Options… > Editor并查看Require Variable Declaration选项。 (确保单击确定按钮;-))

从现在开始,Option Explicit将自动添加到您创建的所有新模块以及您创建的新工作簿中的所有模块。但是,现有的模块需要手动更新。

根据DRY原则,另一个好主意是将表大小代码放在函数中。与错误修复一起,您的代码将如下所示:

' In any standard module
Option Explicit

Public NumRows As Long

Public Function GetTableSize() As Long
  GetTableSize = Worksheets("TableSize").Range("A1").Value2
End Function
' In the "ThisWorkbook" module
Option Explicit

Private Sub Workbook_Open()
  NumRows = GetTableSize()
End Sub
' In the "TableSize" sheet module
Option Explicit

Private Sub Worksheet_Calculate()

    Dim n As Long
    n = GetTableSize()
    If n > NumRows Then NewDatabaseEntry
    ' Always set NumRows so that even after entries are deleted (n < NumRows),
    ' adding new entries will work correctly.
    NumRows = n

End Sub


更好的想法是省去“TableSize”助手工作表及其Worksheet_Calculate()事件处理程序,而是使用Sheet 1的Worksheet_Change()处理程序直接检测添加的行。 (不是您之前尝试使用的Worksheet_Calculate()处理程序。)