如何处理弹出、模态、可调整大小的表单

Posted

技术标签:

【中文标题】如何处理弹出、模态、可调整大小的表单【英文标题】:How to Handle a Popup, Modal, Resizable form 【发布时间】:2017-10-07 02:15:11 【问题描述】:

我需要为屏幕不如其他人大的用户设置一个可调整大小的弹出表单 - 将表单设置为 Popup 和 Modal 和 BorderStyle Resizable 有一个主要限制 - 现在启动弹出窗口的表单中的代码不会等待让表单返回。

那么如何等待表单变为不可见呢?我尝试使用 sleep 和 doevents 进行循环,但这会使弹出表单的响应速度不快,并且会占用 CPU 周期。我已经尝试设置启动表单的 form.gotfocus 事件,但这不会触发,这意味着我必须将打开弹出表单的代码与关闭弹出表单后执行的代码分开。

什么是最好的解决方案?

谢谢

【问题讨论】:

【参考方案1】:

DoEvents / Sleep 50 循环我从来没有遇到过任何问题。 CPU 负载保持最小且表单响应迅速。

使用非常旧的计算机,可能使用Sleep 100

示例代码:

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

Sub TestOpenForm()

    If FormOpenWait("frmPopup") Then
        MsgBox "Form was hidden.", vbInformation
    Else
        MsgBox "Form was closed.", vbInformation
    End If

End Sub

' Open fName, wait until it is
' - closed : return False
' - hidden : return True
Public Function FormOpenWait(fName As String, Optional sOpenArgs As String = "") As Boolean

    If IsFormLoaded(fName) Then DoCmd.Close acForm, fName, acSaveNo

    DoCmd.OpenForm FormName:=fName, OpenArgs:=sOpenArgs

    ' default: signal "closed"
    FormOpenWait = False

    ' Wait until form is closed or made invisible
    Do While IsFormLoaded(fName)
        If Not Forms(fName).Visible Then
            ' Signal "hidden"
            FormOpenWait = True
            Exit Do
        End If

        ' Wait 50ms without hogging the CPU
        DoEvents
        Sleep 50
    Loop

End Function

Public Function IsFormLoaded(fName As String) As Boolean
    IsFormLoaded = (SysCmd(acSysCmdGetObjectState, acForm, fName) And acObjStateOpen) > 0
End Function

【讨论】:

我不同意。它工作得很好。拆分代码更丑陋。如果你例如想要等到查询(数据表视图)关闭,这是唯一的选择。 @Enigmativity 我看到你有这些的样板。 :) 但是你知道这个问题是关于 MS-Access 和 VBA 的吗?不是 C#。没有***.com/questions/5721564/multi-threading-in-vba 啊,好点子。我确实错过了。然后我可能会删除这些 cmets,因为它们具有误导性。 这段代码正是我获得模态行为并保持打开表单的大小调整可用所需的代码。谢谢!【参考方案2】:

您可以使用 acDialog 选项打开表单:

DoCmd.OpenForm "MyFormName", WindowMode:=acDialog

它将等到表单关闭或隐藏。

【讨论】:

但这会使表单无法调整大小。【参考方案3】:

这里的问题是弹出和模态表单不会停止调用代码。

但更糟糕的是您需要停止调用代码。这需要一个对话框形式。

更糟糕的是对话框表单不允许调整大小。

您不想在这里混淆这 3 种类型的表单。

模态表单——它们不同于弹出表单,也与对话框表单非常不同。

弹出式表单也是如此。它们不是对话形式,实际上它们也不是模型。

如果您的 Access 应用程序设置为使用选项卡式文档或重叠窗口,您还必须考虑到这一点。 (这将再次限制您的选择)。

如果您使用选项卡式界面,那么如果您想要重新调整大小的能力,则必须使用弹出表单 - 但这种类型的表单不会停止调用代码。

如果您使用重叠窗口,那么我推荐使用模态表单。 (但同样它不会停止调用代码,但会允许重新调整大小)。

虽然您“可以”在调用表单中采用一些循环代码来“等待”第二个表单被解除,但这样的循环会占用处理器,并且通常会导致鼠标和打字方面的“糟糕”响应。

所以我建议改变你的代码方法。让调用表单将表单作为弹出窗口启动(如果您不使用选项卡式界面,则为模式)。

然后,当您关闭表单时,它会调用一些您希望在调用表单中运行的代码。这确实意味着您必须在关闭第二个表单后拆分要运行的代码 - 并让关闭表单调用 + 运行该代码。

“通用”方法应避免对表单名称进行硬编码,因为“许多”例程和表单可以调用您的第二个表单,并且可以重复使用这些表单。

所以试试这个: 在第二个表单中,创建一个模块级表单变量,如下所示:

Option Compare Database
Option Explicit

Dim frmPrevious     As Form

在此表单的表单打开事件中,GRAB 调用表单引用如下:

Set frmPrevious = screen.ActiveForm

现在在表单关闭事件中,执行以下操作:

frmPrevious.AfterClose

并在调用表单中,创建一个名为

的公共函数

关门后

因此,在第一种形式的公共函数中,您放置了在关闭第二种形式时要运行的代码。这是“不太理想”的理想程序代码,它调用第二种形式,等待并继续 - 但我认为它仍然是最佳选择。

在关闭之前,您可以传递或“设置”值以返回调用表单,例如:

frmPreviuos.SomePubicVar = me!LastNameSelected
frmPrevious!Company = me!CompanySelected
frmPrevious.AfterClose

在上面的第一行中,在调用表单中设置了一个公共变量(如果您想这样做并将值传递回第一个表单中的模块级变量)。下一行设置调用表单中控件的值(如果您想将值传递回调用表单上的某些控件)。

第 3 行执行调用表单中的 MyClose 代码。如果您在第二个表单中有某种“确定”或选择按钮,则将上面的代码移动到该按钮后面——因为如果表单有一个取消按钮,您可能不希望这样的代码运行(所以取消按钮会简单关闭表单——但不要调用+运行第一个表单中的上述代码)。并且点击 X 也将被假定为取消或退出。所以你的“保存”或“确定”或“选择”按钮会有上面的代码。

【讨论】:

感谢您的详尽解释-您提供了一个可行的解决方案和一些更好编码的提示-我将尝试提供缩短睡眠时间的解决方案,看看是否可行,因为程序方法有助于更清洁的代码,但如果响应时间或 CPU 循环太高,我将实施您的解决方案 - 谢谢 作为一个新的 Access 程序员,我需要问很多关于这种技术的问题,因为我很难使用它。对于初学者,我是否需要在调用和被调用表单中都添加“Set frmPrevious = Screen.ActiveForm”以使其正常工作?我如何确定或证明 frmPrevious 指向哪个表单,以确保引用是正确的表单? Screen.ActiveForm 仅用于您正在调用的表单(打开)。您可以使用 on-open 事件,甚至可以使用 on-load 事件。在这两个事件 100% 完成之前,活动表单将是前一个(调用表单)。因此,您在启动第二个表单的表单中没有做任何特别的事情。但是,请考虑使用对话框表单,因为这样会更容易一些,并且会暂停调用代码。你最好提出一个新问题。 感谢您提供的所有信息。我已经开始了一个关于这个的线程:***.com/q/61806180/10913860。

以上是关于如何处理弹出、模态、可调整大小的表单的主要内容,如果未能解决你的问题,请参考以下文章

如何使具有动态内容的reactstrap模态可调整大小和可拖动?

为啥手动调整 Visjs 时间线图的高度(可调整大小)不起作用?

当浏览器调整大小时,不断在视频上移动可调整大小/可拖动的图像

检测单击可调整大小元素的调整大小手柄

如何使用纯 Javascript 使 HTML 元素可调整大小?

如何使用纯 Javascript 使 HTML 元素可调整大小?