DoCmd.Close Current Form from Control Wrapper (WithEvents) C



命令:

DoCmd.Close acForm, lstrParentName, acSaveNo

在组合框包装类(,即,一个将组合框对象分配给声明为WithEvents的组合框变量的类模块(中,Access一直处于崩溃状态,我不知道为什么。

上下文:

  • 表单绑定到表单包装类(clsFrm(,该类将其控件绑定到控件特定的控件包装类(,例如、clsCbo(
  • clsCbo接收绑定组合框的事件,包括OnDblClick
  • clsCbo绑定了一个辅助组合框包装器,用于封装功能相关的代码
  • clsCbo.mCbo_DblClick((调用一个导航过程(nSPer(((,该过程打开另一个表单并用DoCmd.Close acForm, lstrParentName, acSaveNo关闭当前表单,这会导致Access硬崩溃/失败/非自愿关闭

令人困惑的是:

  • 导航过程运行时没有出现错误,并进入其退出过程
  • 源/原始窗体关闭,其所有窗体和控件包装器都无错误地命中了Class_Terminate((过程
  • 目标表单包装器达到其绑定方法,并且开始加载子表单
  • 通过点击窗体窗口的"关闭"按钮手动关闭窗体;X〃;右上角的按钮不会造成任何困难

但是,Access仍然崩溃。尽管如此:

  • 注释掉DoCmd.Close命令,没有问题
  • 跳过绑定辅助组合框包装,没有问题

有什么想法吗?

子((如下:

Private Sub nSuper()
'   Method to navigate from cboSuper~ID to the entity form.
'   Dependencies:
'   -   mstrPath declared in Class_Initialize()
'   -   stdObjectTests.IsSubform()
'   Dependants:
'   -   mCbo_DblClick
On Error GoTo ErrorHandler

200     Dim cboMe As ComboBox
210     Dim frmMe As Access.Form
220     Dim intPK As Long
230     Dim strDest As String
240     Dim strEnt As String
250     Dim strFld As String
260     Dim strPK As String
270     Dim strRS As String
280     Dim strWHERE As String
'       Derive the filter criterion from the control:
290     Set cboMe = mCbo
310     intPK = cboMe.Value
'       Derive Entity from Form.RecordSource:
320     Set frmMe = mParent
350     strRS = frmMe.RecordSource
370     strEnt = Right(strRS, Len(strRS) - 3)
'       Compose the destination Form.Name from Entity:
400     If strEnt = "Party" Then
410         strDest = "frmFirm"
420     Else
430         strDest = "frm" & strEnt
440     End If
'       Compose the filter:
460     strPK = strEnt & "ID"
480     strFld = "tbl" & strEnt & "." & strPK
500     strWHERE = strFld & " = " & intPK
510     If strDest = frmMe.Name Then
'       Re-filter the current form if it is the entity form:
520         frmMe.Filter = strWHERE
530         frmMe.FilterOn = True
540     Else
'       Otherwise, open the entity form:
550         DoCmd.OpenForm strDest, acNormal, , strWHERE
'       Close the current form or its parent:
'       -   Either may crash Access:
560         If IsSubform(frmMe) Then
570             DoCmd.Close acForm, frmMe.Parent.Name, acSaveNo
580         Else
590             DoCmd.Close acForm, frmMe.Name, acSaveNo
600         End If
610     End If
ExitProcedure:
913     Close lintFF
914     Set lFso = Nothing
Set cboMe = Nothing
Set frmMe = Nothing
Exit Sub
ErrorHandler:
890     Resume ExitProcedure
End Sub 'nSuper()

今天早些时候出现了一个解决方案。

这可能是Access.Form对象或垃圾收集/IUnknown中的编码错误或错误。

问题的根源是让主组合框包装器类将控件绑定到辅助包装器类(该类的存在是为了封装功能相关的代码(。然后,做DoCmd.Close acForm, lstrParentName, acSaveNo硬崩溃访问。未绑定辅助类时没有发生故障。

Form.Close上,对象引用都在Termination事件过程中被明确清除,但很明显,辅助类的控制引用持续了足够长的时间,以至于组合框可能已经过时了。当项目继续存在于Controls集合中时,Form对象如何关闭我想不通,但我跑题了。结果是硬崩溃访问,尽管如此:

  • 所有对辅助类的引用都被清除了(所以垃圾回收在任何情况下都应该清除其对象引用,同时销毁对象(
  • 其CCD_ 8过程被触发并且其组合框引用在那里被明确清除

当然,请注意,对象默认在VBA中传递ByRef(,即,通过引用(。

解决方案是将Form.Close放入辅助类中,并在那里清除类的组合框引用,因为在Class_Terminate()中执行此操作需要等待垃圾收集通过其他对象级联,这为时已晚,无法防止Access失败注意尝试从主类中的任何位置清除辅助类的组合框引用也会失败。

具体

  • 在这两个类中,声明一个Form对象变量WithEvents并将组合框的父对象分配给它。是的,特别奇怪的是,如果没有主类也绑定父对象,辅助类就不能接收其绑定对象的父对象的事件,因为所有对象都是通过引用传递的,而且不管怎样,该对象的parent属性都应该是不可变的,但实际情况就是这样
  • 在主类中,除了分配Form对象变量之外,什么都不需要。在主类中下沉Form.Close是不必要的,甚至是不有效的
  • 在辅助类中接收Form.Close,并在那里清除其组合框变量

我的实验测试课程如下:

主要类别:

'wecCboTestPrimary
Option Compare Database
Option Explicit
Private WithEvents mCbo As ComboBox
Private WithEvents mParent As Access.Form
Private mwecCboTestSecondary As New wecCboTestSecondary
Private mstrName As String
Private Sub Class_Initialize()
mstrName = "wecCboTestPrimary"
Debug.Print mstrName & ".Class_Initialize()"
End Sub
Private Sub Class_Terminate()
Debug.Print vbTab & mstrName & ".Class_Terminate()"
'   Fails to avoid an Access hard crash:
'   Set mwecCboTestSecondary.BoundCbo = Nothing
Set mwecCboTestSecondary = Nothing
Set mParent = Nothing
Set mCbo = Nothing
Debug.Print vbTab & mstrName & ".Class_Terminate().End"
End Sub
Public Property Get BoundCbo() As ComboBox
Debug.Print mstrName & ".PPG BoundCbo()"
Set BoundCbo = mCbo
End Property
Public Property Set BoundCbo(lCbo As ComboBox)
Debug.Print mstrName & ".PPS BoundCbo()"

'   Bind the control:
Set mCbo = lCbo

'   Sink its events:
mCbo.OnDblClick = "[Event Procedure]"
'   Bind the control's parent:
Set mParent = lCbo.Parent

'   Sinking parent events is unnecessary to avoid an Access hard crash:
'mParent.OnClose = "[Event Procedure]"
'   Bind the control to the secondary combo box wrapper class:
Set mwecCboTestSecondary.BoundCbo = lCbo

Debug.Print mstrName & ".PPS BoundCbo()" & vbTab & mCbo.Name

End Property
Private Sub mJustCloseMe()
Debug.Print vbCrLf & vbTab & mstrName & ".JustCloseMe()" & vbTab & mCbo.Name & vbCrLf

Dim lstrParentName As String
lstrParentName = mCbo.Parent.Name

'Hard crashes Access absent Goldilocks:
DoCmd.Close acForm, lstrParentName, acSaveNo

End Sub
Private Sub mCbo_DblClick(Cancel As Integer)
Debug.Print mstrName & ".mCbo_DblClick()" & vbTab & mCbo.Name
mJustCloseMe
End Sub
'Private Sub mParent_Close()
'Fails to print:
'Debug.Print mstrName & ".mParent_Close()" & vbTab & mCbo.Name

'Fails to avoid an Access hard crash (any or in combination):
'Set mwecCboTestSecondary.BoundCbo = Nothing
'Set mwecCboTestSecondary = Nothing
'Set mParent = Nothing

'End Sub
'End wecCboTestPrimary

第二类:

'wecCboTestSecondary
Option Compare Database
Option Explicit
Private WithEvents mCbo As ComboBox
Private WithEvents mParent As Access.Form
Private mstrName As String
Private Sub Class_Initialize()
mstrName = "wecCboTestSecondary"
Debug.Print mstrName & ".Class_Initialize()"
End Sub
Private Sub Class_Terminate()
Debug.Print vbTab & vbTab & mstrName & ".Class_Terminate()"

'   Fails to avoid an Access hard crash:
'   Set mCbo = Nothing
Set mParent = Nothing

Debug.Print vbTab & vbTab & mstrName & ".Class_Terminate().End"
End Sub
Public Property Get BoundCbo() As ComboBox
Debug.Print mstrName & ".PPG BoundCbo()"
Set BoundCbo = mCbo
End Property
Public Property Set BoundCbo(lCbo As ComboBox)
Debug.Print mstrName & ".PPS BoundCbo()"
'   Bind the control:
Set mCbo = lCbo
'   Bind the control's parent:
Set mParent = lCbo.Parent
'   Sink the parent's events:
mParent.OnClose = "[Event Procedure]"

Debug.Print mstrName & ".PPS BoundCbo()" & vbTab & mCbo.Name

End Property
Private Sub mParent_Close()
Debug.Print mstrName & ".mParent_Close()"

'   Goldilocks:
Set mCbo = Nothing

End Sub
'End wecCboTestSecondary

最新更新