如何在子表单打开时冻结父表单

Posted

技术标签:

【中文标题】如何在子表单打开时冻结父表单【英文标题】:How to freeze Parent Form while Child Form is open 【发布时间】:2012-09-06 17:30:52 【问题描述】:

我有一种情况,在某些情况下,我需要打开另一个表单并保持该表单的焦点(模态 = true),当它们没有关闭时,父表单上的控件会刷新可能的数据可能已经改变了。

最初我有一个方法可以在子表单打开时DoEvents,但它会导致一些子表单无法用于数据输入/编辑(它们在表单级别没有数据绑定)。

ShowForm 方法 - 最初

Public Sub ShowForm(par As Form, nm As String, _
                    Optional whr As String = "", _
                    Optional args As String = "", _
                    Optional mode As AcFormOpenDataMode = acFormPropertySettings)
    DoCmd.OpenForm nm, acNormal, , whr, mode, , args
    
    While IsOpen(nm)
        DoEvents
    Wend
End Sub

为了让子表单始终可用,我必须注释掉 While...Wend 循环。

我是否可以在此方法或调用控件的 OnClick 中使用另一种逻辑模式,这样当他们关闭子窗体时,我可以在子窗体关闭后执行代码?

【问题讨论】:

如果你打开了表单模态,打开代码之后的任何代码都不会运行,直到表单关闭。另请注意,您可以从打开的表单更新调用表单。 好的,所以 Popup + Modal True 将在父表单的调用事件中提供固定暂停? 使用 acdialog 参数打开表单。 我可以有一个对话框的继承链吗?父母 -> 孩子 -> 等等? @GoldBishop:您必须维护自己的继承链。请参阅下面我的最新答案以了解一种方法。 【参考方案1】:

最简单的方法是简单地以对话模式打开表单。例如,

DoCmd.OpenForm nm, acNormal, , whr, mode, acDialog, args

这将暂停执行调用模块中的代码,直到表单关闭。它还将阻止用户与任何其他表单交互,直到“对话框”表单关闭。


我发现在对话框模式下打开绑定表单,更新数据,关闭表单,然后刷新调用表单上的对象(例如,组合框的行源)并不总是可靠的。

下面是我编写的一个通用函数,用于“暂停”调用代码,而无需在对话框模式下打开表单,也不会显着影响用户界面的性能。它适用于表单和报告。请注意,睡眠 API 声明必须位于代码模块的顶部(在模块声明部分)。

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

'-------------------------------------------------------------------------------
' Procedure : WaitTilObjClosed
' Author    : Mike
' Date      : 1/7/2009
' Purpose   : Halts program execution until user closes object.  User is 
'               generally unaffected by the loop.
' Requires  : Sleep API sub
' Notes - while Sleeping other programs can use processor but access cannot;
'       - during DoEvents, other parts of Access can use processor;
'       - without the Sleep call, processor usage stays at 100% for MSACCESS.EXE
'       - with a long Sleep call, Access becomes noticeably sluggish
'-------------------------------------------------------------------------------
'
Sub WaitTilObjClosed(ObjType As AcObjectType, ObjName As String)
    Do
        DoEvents
        Sleep 1
        If (SysCmd(acSysCmdGetObjectState, ObjType, ObjName) = 0) Then Exit Do
    Loop
End Sub

您可以按如下方式使用它:

DoCmd.OpenForm "MyForm"
WaitTilObjClosed acForm, "MyForm"
MsgBox "MyForm is now closed"

DoCmd.OpenReport "MyReport", acViewPreview
WaitTilObjClosed acReport, "MyReport"
MsgBox "MyReport is now closed"

【讨论】:

问题是,当表单为弹出式 - True & Modal - False 时,它​​会将子表单冻结到我可以单击控件但未进行选择的程度,并且几乎看起来它没有响应。正如您之前看到的,我让DoEvents 在与您类似的循环中运行,只是使用了不同的模式进行验证 我承认我从未使用 Popup = True 的表单测试过这段代码。如果我想要一个表单作为弹出窗口,我只需使用 acDialog 选项打开它:DoCmd.OpenForm "MyForm", WindowMode:=acDialog。当我想等待用户关闭特定表单时,我使用我的 WaitTilObjClosed 代码,但仍允许用户导航到其他打开的表单(这在 acDialog 模式下当然是不可能的)。我以为你在这里尝试做类似的事情。 不,我希望他们能够访问应用程序,而不是打开表单的点击层次结构。 Parent -> Child -> GrandChild -> etc,因为他们打开其他表单,然后允许最终涓滴关闭回到父表单。不担心可见设置只想在打开子表单时“锁定”父表单。【参考方案2】:

我想我现在明白你的问题了。不幸的是,没有简单的方法可以做你想做的事。我会建议以下算法:

'Grandparent/parent form module
Dim ChildForm As String

Private Sub ShowFormBtn_Click()
    DoCmd.OpenForm "MyForm"
    ChildForm = "MyForm"
End Sub    

Private Sub ShowOtherFormBtn_Click()
    DoCmd.OpenForm "OtherForm"
    ChildForm = "OtherForm"
End Sub

Private Sub Form_Activate()
    On Error Resume Next
    Forms(ChildForm).SetFocus
End Sub

Private Sub Form_Unload(Cancel As Integer)
    On Error Resume Next
    Forms(ChildForm).SetFocus
    If Err.Number = 0 Then Cancel = True
End Sub

基本上,每次您尝试切换到父表单时,它都会尝试将用户发送到其子表单(如果存在)。如果它没有子节点,则会生成一个被静默忽略的错误。如果孩子确实存在,则将其切换到该表单。

这也支持多个级别。因此,假设您有一个祖父表单,该表单生成一个父表单,该父表单生成一个子表单。这三种形式都是开放的。您单击将焦点发送到父表单的祖父表单,父表单将焦点发送到子表单。

【讨论】:

将此作为单独的答案发布,因为我的原始答案更笼统(因此对通过 Google 找到此问题的未来访问者更有帮助),而这个更直接地处理您的具体案例。 DoCmd.OpenForm nm, acNormal, , whr, mode, acDialog, args 工作得很好......它应用了我需要的继承链。我有多个表单可以相互打开,但任何“子”表单都不会打开已经打开的表单,所以我不必担心“子”表单已经打开的情况并且是孩子以不同的形式。 IE 没有一个“out”分支会向后路由到不同的分支,并且总是单向的父子关联。【参考方案3】:

您不想锁定FORM 本身,而是想锁定属于FORM 的控件。下面解释了我进行控制锁定的首选方法。目标是让每个用户可编辑的控件都是只读的

在您父级FORM 的 VBA 代码中创建此子:

public sub LockControls_[Enter Form Name Here]()

  ME.txt1.Locked = True
  ME.txt2.Locked = True
  ME.txt3.Locked = True
...

end sub

您可以锁定文本框、组合框、按钮等...

如果您的控件遵循我上面概述的命名结构,您实际上可以通过执行以下操作来循环:

public sub LockControls_[Enter Form Name Here]()

  Dim s_ctrl As String
  Dim s_obj As String
  Dim i as Int

  i = 0
  s_ctrl = "txt"
  s_obj = s_ctrl & i

  do while me(s_obj).Name <> ""

      Me(s_obj).Locked = True
      i = i+1
      s_obj = s_ctrl & i

  loop
end sub

从启动子表单的操作中调用此方法。它将锁定(只读)表单上的所有控件,同时让用户可以访问表单。

当从子窗体上的关闭事件调用时,同样的方法可用于解锁您的父 FORM。请务必不要使用Me(s_obj).Locked = False。相反,请使用以下语法完全限定您的表单:

public sub UnLockControls_[Enter Parent Form Name Here]()

  Dim s_ctrl As String
  Dim s_obj As String
  Dim i as Int

  i = 0
  s_ctrl = "txt"
  s_obj = s_ctrl & i

  do while me(s_obj).Name <> ""

      FORMS![form_name](s_obj).Locked = False
      i = i+1
      s_obj = s_ctrl & i

  loop
end sub

【讨论】:

【参考方案4】:

这是我最初问题中该方法的最终形式:按我的意愿工作(现在;)),感谢@Remou 和@mwolfe02。

最终形式 - 当然是暂定

'@frm - String value, Name of the form to Open
Public Sub OpenForm(frm As String, _
                    Optional vw As AcFormView = acNormal, _
                    Optional whr As String = "", _
                    Optional mode As AcFormOpenDataMode = acFormPropertySettings, _
                    Optional args As String = "")
    If FormExists(frm) Then
        DoCmd.OpenForm frm, vw, , whr, mode, acDialog, args
    Else
        RaiseError "Form ( " & frm & " ) does not exist!" & vbCrLf & vbCrLf & _
                    "Alert your IT Support for further assistance."
    End If
End Sub

【讨论】:

以上是关于如何在子表单打开时冻结父表单的主要内容,如果未能解决你的问题,请参考以下文章

当子窗口打开时关闭父窗口

有没有办法在关闭并再次打开时不重新创建表单的实例?

如何返回表单最高父级的名称

Access 2007 拆分表单 VBA:打开时 acNewRec 可防止在表单中跳动 - 就像第一个字段未“选择”一样

当我在结构视图中保存表单然后在表单视图中打开时,它给我一个错误。每个新文件都一样

当通过链接或后退按钮打开时,强制 JSF 刷新页面/视图/表单