首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >VBA -正确销毁非模态UserForm实例。

VBA -正确销毁非模态UserForm实例。
EN

Stack Overflow用户
提问于 2017-11-17 19:12:27
回答 3查看 7.4K关注 0票数 13

简介:

我知道--向UserForms展示--这是最好的做法

  • 在用户表单代码( QueryClose )中处理QueryClose
  • 在里面不做Unload Me,只是一个胆小的Me.Hide指令(在防止x-腐蚀和最终通过Cancel = True自毁之后)。
  • 在类代码中设置相关变量/属性(例如,.IsCancelled=True)
  • 以便能够通过调用代码卸载UF。

有用链接

杰出的概述"UserForm1.Show?“可以在https://rubberduckvba.wordpress.com/2017/10/25/userform1-show/找到,也可以在众多的例子中找到,所以答案(thx到Mathieu,Guindon,aka,Mat's Mug和RubberDuck)。

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

1)模态UserForms的工作示例

据我所理解- -而且我确实试图学习-下面的代码对于UF来说应该是可以的:

案例1a) .使用用于UF实例的局部变量,经常可以看到:

代码语言:javascript
复制
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代码块:

代码语言:javascript
复制
' ----------------------------------------------------------
' >> 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块方法(参见。1b)应足以在x处理后销毁任何物体参考资料:

代码语言:javascript
复制
  With New UserForm1
      .Show vbModeless  ' << show modeless uf
  End With

但是,如果我试着

  • ( a)获取有关可能的用户取消的信息以及
  • ( b)在Unload指令之后使用局部变量(例如"ufA")洗礼表单,

由于表单为MODELESS,所有代码行都将同时执行:

  • 代码显示表单,下一刻..。
  • 代码没有发现用户取消,因为没有时间进行任何用户操作,接下来的时间。
  • 如果使用用户窗体的局部变量,则代码卸载窗体。

3)问题

如何处理由MODELESS窗体的调用代码正确报告的UserForm取消,以及( b) a(必要)如果使用局部变量,则卸载?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-11-17 20:22:18

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

但是,对于非模态形式,原则是相同的:简单地扩展模型-视图-演示者模式,在链接文章和这里中大致概述。

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

  • 当处理一个模态表单时,有一个“先显示”,然后一个“隐藏后”,它在表单隐藏后立即运行。您可以在“显示”事件的同时处理任何发生的事情。
  • 在处理非模态表单时,需要通过事件来处理“显示前”、“显示时”和“显示后”。

让演示者类模块负责在模块级和WithEvents级别上保存WithEvents实例。

代码语言:javascript
复制
Option Explicit
Private WithEvents myModelessForm As UserForm1

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

代码语言:javascript
复制
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

您的不希望表单实例是本地的,因此局部变量或a With块无法工作:对象将超出作用域,然后才是真正的对象。这就是为什么在模块级别将实例存储在私有字段中的原因:现在表单与演示者实例一样存在。

现在,您需要对演示者进行表单"talk“--最简单的方法是在UserForm1代码中公开事件--例如,如果我们希望用户确认取消,我们将向事件添加一个ByRef参数,这样演示者中的处理程序可以将信息传递回事件源(即返回到表单代码):

代码语言:javascript
复制
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事件:

代码语言:javascript
复制
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

非模态形式通常不会有"ok“和"cancel”按钮。相反,您将公开一些功能,例如,一个弹出一些模式对话框UserForm2来执行其他操作--同样,您只需为它公开一个事件,并在演示者中处理它:

代码语言:javascript
复制
Public Event ShowGizmo()

Private Sub ShowGizmoButton_Click()
    RaiseEvent ShowGizmo
End Sub

主持人说:

代码语言:javascript
复制
Private Sub myModelessForm_ShowGizmo()
    With New GizmoPresenter
        .Show
    End With
End Sub

请注意,模式UserForm2是一个单独的演示者类的关注点。

票数 13
EN

Stack Overflow用户

发布于 2017-11-17 19:25:42

我通常将非模态用户表单实例的生存期绑定到工作簿的生存期,方法是将代码放在ThisWorkbook后面:

代码语言:javascript
复制
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

然后,您可以在整个代码中引用非模态代码。

代码语言:javascript
复制
ThisWorkbook.MyForm.Show vbModeless

等。

票数 3
EN

Stack Overflow用户

发布于 2017-11-17 19:35:38

对于无模式表单,请使用DoEvents与自定义userform属性耦合。

代码语言:javascript
复制
Sub test()

    Dim frm As New UserForm1

    frm.Show vbModeless

    Do
        DoEvents
        If frm.Cancelled Then
            Unload frm
        Exit Do
    End If
    Loop Until False

    MsgBox "You closed the modeless form."

    '/ Using With
    With New UserForm1
        .Show vbModeless
        Do
            DoEvents
            If .Cancelled Then Exit Do
        Loop Until False
    End With

    MsgBox "You closed the modeless form (with)"

End Sub

'/用户表单

代码语言:javascript
复制
Private m_bCancelled As Boolean

Public Property Get Cancelled() As Boolean
    Cancelled = m_bCancelled
End Property

Public Property Let Cancelled(ByVal bNewValue As Boolean)
    m_bCancelled = bNewValue
End Property
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    Me.Cancelled = True
    Cancel = 1
    Me.Hide
End Sub
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47357708

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档