VBA - 正确销毁无模式UserForm实例

时间:2017-11-17 19:12:27

标签: vba excel-vba userform excel

说明:

我知道 - 显示UserForms - 这是

的最佳做法
  • 在userform代码(QueryClose
  • 中处理If CloseMode = vbFormControlMenu ...
  • 其中没有Unload Me,只是胆小的Me.Hide指令 (在通过Cancel = True
  • 阻止[x] -ititting和最终自我毁灭之后
  • 在[class]代码中设置相关变量/ [property](例如.IsCancelled=True
  • 为了能够通过调用代码卸载UF。

有用的链接

可以在https://rubberduckvba.wordpress.com/2017/10/25/userform1-show/找到一个出色的概述" UserForm1.Show?" 以及许多示例性的答案(对Mathieu Guindon来说也是 Mat' Mug 和RubberDuck)。

进一步选择(►编辑自2019年5月1日起

1)模态UserForms的工作示例

据我了解 - 我确实尝试学习 - ,以下代码应该适用于模态 UF&#39>:

案例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)Unload表格如果在Show指令后使用本地变量(例如&#34; ufA&#34;)进行了接受,

所有代码行都将立即执行,原因正是表单是MODELESS:

  • 代码显示表单,下一刻......
  • 代码找不到用户取消,因为下一刻没有时间进行任何用户操作..
  • [代码在使用userform的局部变量时卸载表单]

3)问题

我如何处理a)正确报告的UserForm取消了MODELESS表单的调用代码以及b)如果使用局部变量(必要的?)卸载?

3 个答案:

答案 0 :(得分:7)

事实上,我一直非常关注模态形式 - 因为这是最常用的。感谢您对该文章的反馈!

非模态形式的原则是相同的:只需扩展链接文章和here中粗略概述的模型 - 视图 - 演示者模式。

不同之处在于非模态形式需要转变范式:您不再响应预设的事件序列 - 而是需要响应某些异步事件可能在任何特定时间发生,或不发生。

  • 在处理模式表单时,在显示&#34;之前显示&#34;然后隐藏&#34;&#34;隐藏表单后立即运行。你可以处理发生的任何事情&#34;同时显示&#34;使用活动。
  • 处理非模态表单时,在显示&#34;之前会显示&#34;然后显示&#34;然后显示&#34;并且&#34;在显示&#34; 这两个需要通过事件处理。

让您的演示者类模块负责在模块级和UserForm保持WithEvents实例:

Option Explicit
Private WithEvents myModelessForm As UserForm1

演示者的Show方法将Set表单实例并显示它:

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块可以&#39 ; t work:在你指的是之前,对象将超出范围。这就是为什么你在模块级别将实例存储在私有字段中的原因:现在,只要演示者实例的存在,表单就会存在。

现在,您需要制作表格&#34; talk&#34;对于演示者 - 最简单的方法是在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

非模态形式通常 &#34; ok&#34;和&#34;取消&#34;按钮虽然。更确切地说,您已经暴露了许多功能,例如,提供了一些其他功能的模态对话框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是单独的演示者类的关注点。

答案 1 :(得分:3)

我通常将无模式用户表单实例的生命周期与工作簿相关联,方法是将代码放在ThisWorkbook后面的那些行中:

Option Explicit

Private m_MyForm As UserForm1

Private Sub Workbook_BeforeClose(Cancel As Boolean)
    If Not m_MyForm Is Nothing Then
        Unload m_MyForm
        Set m_MyForm = Nothing
    End If
End Sub

Friend Property Get MyForm() As UserForm1
    If m_MyForm Is Nothing Then
        Set m_MyForm = New UserForm1
    End If

    Set MyForm = m_MyForm
End Property

然后,您可以使用例如

在整个代码中引用无模式代码
ThisWorkbook.MyForm.Show vbModeless

答案 2 :(得分:3)

对于无模式窗体,请使用DoEvents以及自定义userform属性。

2.0.1.RELEASE

&#39; /用户表单

2.0.x