简介:
我知道 - 显示用户表单 - 最佳实践是
- 在用户表单代码中处理
QueryClose
(If CloseMode = vbFormControlMenu ...
) - 其中没有执行任何
Unload Me
操作,只是一条胆怯的Me.Hide
指令 (通过Cancel = True
防止 [x]-itting 和最终自毁之后) - 在[类]代码中设置相关变量/[属性](例如
.IsCancelled=True
) - 以便能够通过调用代码卸载 UF。
有用的链接
出色的概述“UserForm1.Show?”可以在 https://rubberduckvba.wordpress.com/2017/10/25/userform1-show/ 找到 以及许多示例性的 SO 答案(感谢 Mathieu Guindon 又名 Mat's Mug 和 RubberDuck)。
进一步选择(►截至 2019 年 5 月 1 日编辑)
- Disadvantages in putting code into userforms instead of modules
- Passing variable from form to module
- Apply logic for userform dialog (Rubberduck)
- The perfect userform (Vitosh academy)
1) 模态用户表单的工作示例
据我了解 - 并且我确实尝试学习 -,以下代码对于 modal UF 来说应该没问题:
情况 1a) .. UF 实例有一个局部变量,如常见的那样:
Public Sub ShowFormA
Dim ufA As UserForm1
Set ufA = New UserForm1
' show userform
ufA.Show ' equivalent to: ufA.Show vbModal
' handle data after user okay
If Not ufA.IsCancelled Then
' do something ...
End If
' >> object reference destroyed expressly (as seen in some examples)
unload ufA
End Sub
情况 1b) .. 没有局部变量,但使用 With New
代码块:
' ----------------------------------------------------------
' >> no need to destruct object reference expressly,
' as it will be destroyed whenever exiting the with block
' ----------------------------------------------------------
With New UserForm1
.Show ' equivalent to: ufA.Show vbModal
' handle data after user okay
If Not .IsCancelled Then
' do something ...
End If
End With
<小时/>
2)问题
使用MODELESS UserForm 实例会出现问题。
好的,with block 方法(参见 1b)应该足以在 x-iting 后销毁任何对象引用:
With New UserForm1
.Show vbModeless ' << show modeless uf
End With
如果我尝试,但是
- a) 获取有关可能的用户取消的信息以及
- b) 如果在
Show
指令之后使用局部变量(例如“ufA”)进行洗礼,则卸载
表单,
所有代码行将立即执行,这正是表单为 MODELESS 的原因:
- 代码显示表格,下一刻..
- 代码发现没有用户取消,因为没有时间进行任何用户操作,下一刻..
- [如果对用户表单使用局部变量,则代码会卸载表单]
3)问题
如何处理 a) 通过 MODELESS 表单的调用代码正确报告的 UserForm 取消以及 b) 如果使用局部变量则(必要吗?)卸载?
最佳答案
确实,我一直非常关注模态形式 - 因为这是最常用的形式。感谢您对该文章的反馈!
不过,非模态形式的原理是相同的:只需扩展链接文章和 here 中粗略概述的模型- View -演示者模式即可。 .
不同之处在于,非模态表单需要范式转变:您不再响应预设的事件序列 - 相反,您需要响应一些可能发生的异步事件在任何给定时间,或不。
- 处理模态表单时,有一个“显示之前”,然后是在隐藏表单后立即运行的“隐藏之后”。您可以使用事件处理“显示时”发生的任何情况。
- 处理非模态表单时,有一个“显示前”,然后是“显示时”和“显示后”都都需要通过事件来处理。
让您的演示者类模块负责在模块级别和 WithEvents
保存 UserForm
实例:
Option Explicit
Private WithEvents myModelessForm As UserForm1
演示者的 Show
方法将设置
表单实例并显示它:
Public Sub Show()
'If Not myModelessForm Is Nothing Then
' myModelessForm.Visible = True 'just to ensure visibility & honor the .Show call
' Exit Sub
'End If
Set myModelessForm = New UserForm1
'...
myModelessForm.Show vbModeless
End Sub
您不希望表单实例位于此处的过程本地,因此局部变量或 With
block 可以不起作用:该对象将在您有意为之之前超出范围。这就是为什么您将实例存储在模块级别的私有(private)字段中:现在表单的生命周期与演示者实例的生命周期一样长。
现在,您需要使表单与演示者“对话” - 最简单的方法是在 UserForm1
代码隐藏中公开事件 - 例如,如果我们希望用户确认取消,我们将向事件添加一个 ByRef
参数,以便演示器中的处理程序可以将信息传递回事件源(即返回到表单代码):
Option Explicit
'...private fields, model, etc...
Public Event FormConfirmed()
Public Event FormCancelled(ByRef Cancel as Boolean)
'returns True if cancellation was cancelled by handler
Private Function OnCancel() As Boolean
Dim cancelCancellation As Boolean
RaiseEvent FormCancelled(cancelCancellation)
If Not cancelCancellation Then Me.Hide
OnCancel = cancelCancellation
End Function
Private Sub CancelButton_Click()
OnCancel
End Sub
Private Sub OkButton_Click()
Me.Hide
RaiseEvent FormConfirmed
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
Cancel = Not OnCancel
End If
End Sub
现在演示者可以处理 FormCancelled
事件:
Private Sub myModelessForm_FormCancelled(ByRef Cancel As Boolean)
'setting Cancel to True will leave the form open
Cancel = MsgBox("Cancel this operation?", vbYesNo + vbExclamation) = vbNo
If Not Cancel Then
' modeless form was cancelled and is now hidden.
' ...
Set myModelessForm = Nothing
End If
End Sub
Private Sub myModelessForm_FormConfirmed()
'form was okayed and is now hidden.
'...
Set myModelessForm = Nothing
End Sub
非模态表单通常不会有“确定”和“取消”按钮。相反,您将公开许多功能,例如,一个会弹出一些执行其他操作的模态对话框 UserForm2
的功能 - 同样,您只需为其公开一个事件,并在演示者中处理它:
Public Event ShowGizmo()
Private Sub ShowGizmoButton_Click()
RaiseEvent ShowGizmo
End Sub
主持人说道:
Private Sub myModelessForm_ShowGizmo()
With New GizmoPresenter
.Show
End With
End Sub
请注意,模态 UserForm2
是一个单独的演示者类的关注点。
关于VBA - 正确销毁无模式用户窗体实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47357708/