在表单打开时将对象保存在内存中(访问)

Keep object in memory while is form is open (access)

我正在编写一个访问应用程序,我只是花时间设置了一个 class 以使该应用程序易于理解和高效。

但是,当我第二次点击事件需要该对象时,该对象已不在内存中。

我让用户点击一个按钮来设置对象并进行一些测试:

Public this_renewal As Renewal

Private Sub cmdMA_Click()
    
        Set this_renewal = Factory.CreateRenewal(cMA)
        
        Call BranchLabelVisibility(True)
        Me.lblBranchToAdd.Caption = this_renewal.Abb
        Call DateLabelVisibility(True)
        Me.lblYearToAdd.Caption = this_renewal.Year
        Me.lblMonthToAdd.Caption = this_renewal.Month
        Call TestMonth
    
End Sub

这是我在名为 factory 的常规模块中的 CreateRenewal 函数。我从另一个关于如何使用属性初始化 class 的线程中得到这个想法:VBA: Initialize object with values?

 Public Function CreateRenewal(strFileName As String) As Renewal

    Dim renewal_obj As Renewal
    Set renewal_obj = New Renewal
    
    Call renewal_obj.InitiateProperties(strFileName)
    Set CreateRenewal = renewal_obj

End Function

并在更新中调用 InititateProperties class:

Public Sub InitiateProperties(ByVal strFileName As String)

    strRenewalFile = strFileName
    strRenewalFullFileName = fnGetFullFileName()
    strRenewalFileAndPath = cPath & strRenewalFullFileName

    strBranchLetter = fnGetLetterFromFile(strRenewalFile)

    strAbb = DLookup("BranchAbb", "tblBranches", "BranchLetter = '" & strBranchLetter & "'")
    strBranchName = DLookup("Branch", "tblBranches", "BranchAbb = '" & strAbb & "'")
    
    If Len(Mid(strRenewalFullFileName, 10, 2)) = 1 Then
        strRenewalMonth = "0" & Mid(strRenewalFullFileName, 10, 2)
    Else
        strRenewalMonth = Mid(strRenewalFullFileName, 10, 2)
    End If
    strRenewal2DigitYear = Mid(strRenewalFullFileName, 12, 2)
    strRenewalYear = "20" & strRenewal2DigitYear
    
    strRenewalTable = strAbb & " " & strRenewalYear & " Renewals"

End Sub

然后用户决定是否要导入 this_renewal 文件(这是内存中的对象),如果要导入则单击导入按钮运行此代码:

Private Sub cmdImport_Click()

    DoCmd.SetWarnings False
    
    Call FixExcelFile
    
    Dim strTableName As String
    Dim strImportName As String
    strTableName = this_renewal.Table
    'strImportName = Left(fnGetFileName(Me.tbFileToImport), 8)

    Call ImportTable
    
    'Count and Display Values of the two Tables
    Call TableLabelVisibility(True)
    Call GetTableValues
    
    'Create Backup of Original
    Call CreateBackup
    
    'Run Queries to Update Data of Import
    Call AppendQuery
    Call UpdateMonth
    Call UpdateStatus
    Call UpdateUnderWriter
    
    Call ShowResults
    
    DoCmd.SetWarnings True

End Sub

如果我在 cmdMA_Click 事件期间单步执行代码,则会创建对象并在 class 的 InitiateProperties 子项中初始化这些属性。单击事件完成后,我触发了下一个单击事件(在下一个按钮上,在同一表单上),我收到该对象的“对象变量或未设置变量”。在原始点击事件中我没有问题。

由于未显示 Factory.CreateRenewal(cMA) 的相关代码,我创建了一个如何 re-produce 错误的示例。

我有一个 class this_renewal,代码如下

Option Explicit

Public Function myTest() As String
    myTest = "Test"
End Function

然后我用 Attribute VB_PredeclaredId = True 和下面的代码

创建了一个 class Factory
Option Explicit

Public Function CreateRenewal(cMA As String) As this_renewal

    ' Doing nothing here
    ' result will be that CreateRenewal will return Nothing
    ' which will lead to the error
    '  "Object Variable or With Variable not set"

End Function

在用户表单中,我有两个带有代码的按钮

Option Explicit
Public this_renewal As this_renewal

Private Sub btn1_Click()
    Set this_renewal = Factory.CreateRenewal("cMA")
End Sub

Private Sub btn2_Click()
    Debug.Print this_renewal.myTest
End Sub

如果我将 Factory class 中的代码更改为

Public Function CreateRenewal(cMA As String) As this_renewal
    Set CreateRenewal = New this_renewal
End Function

如果我先按 btn1 然后再按 btn2,一切都会正常。

如果 class var 是 public 到表单代码并创建?然后它将并且应该在该表单打开的整个生命周期内持续存在。您当然不能在全局级别或以其他形式引用您以该形式创建的 class 变量。

因此您可以将 var 声明移动到标准 public 模块(在表格之外),或者实际上在其他表格中这样做:

在我们的表单中 - 在表单级模块中声明 class - 作为 public

Option Compare Database
Option Explicit

Public this_renewal As Renewal

所以,现在假设您的表单运行代码来设置对象,就像这样:

Set this_renewal = Factory.CreateRenewal(cMA)

现在,如果您打开任何其他表格?好吧,您有两种选择来获得 class.

您可以参考上面的表格,并在代码中这样做:

dim this_renewal As Renewal
set this_renewal = forms!ThatFirstFormabove.this_renewal.

因此,class 实例仅存在于您声明 var (this_renewal) 的表单的上下文中。

你也可以这样做。用第一种形式说(public this_renewal)。

您将在目标表单中同时在表单级别代码中声明 this_renewal。

那么你会并且可以这样做:

docmd.OpenForm("frmB")
set forms("frmB").this_renewal = me.this_renewal

因此,要么传递 class,要么直接从具有 class 的当前表单引用它,或者将 class 的 var 声明移出形成模块代码,放置在全球标准平面jane模块中。在模块 1 中说。

如果您这样做,那么所有表单和所有代码都可以使用 class.

的那个实例

我明白了;发生这种事情真是太愚蠢了。所以在第一次点击事件期间调用的最后一个过程中:

Private Sub TestMonth()

    If this_renewal.IsNewMonth Then
        Me.cmdImport.Enabled = True
        Me.lblFileMistake.Visible = False
    Else
        GoTo WrongFile
    End If
    
endSub:
    Exit Sub
    
WrongFile:
    
    Me.cmdImport.Enabled = False
    Me.lblFileMistake.Visible = True
    Me.lblFileMistake.Caption = "Month " & this_renewal.Month & " was already added for branch " & this_renewal.Name & "!!"
    GoTo endSub
    
End Sub

就在我 'Exit Sub' 的地方,它是 'End'。这将清除所有变量。一旦我将其更改为 'Exit Sub',它现在可以正常工作了。