VBA - 正确销毁无模式用户窗体实例

标签 vba excel userform

简介:

我知道 - 显示用户表单 - 最佳实践是

  • 在用户表单代码中处理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 日编辑)

<小时/>

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/

相关文章:

vba - 自动打开一个excel文件到所有工作表中的单元格A1(使用VBA)

excel - 在Excel中如何获取指定列中每个单元格的左边5个字符并将它们放入新列中

excel - 向其添加文本时,求和公式的行为很奇怪

excel - 编辑 VBA 脚本用户窗体

VBA 窗体不显示第二次

Excel 2010 中的 VBA 位置图

vba - 两次之间的倒计时器(不是日期)

vba - 将单元格值从一个工作表复制到另一个工作表作为字符串

ChartSpace 中缺少 Excel VBA "AxisBetweenCategories"属性

vba - Excel VBA - 寻找简化循环的方法