VBA - 正确销毁无模式用户窗体实例 [英] VBA - destroy a modeless UserForm instance properly

查看:52
本文介绍了VBA - 正确销毁无模式用户窗体实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

简介:

我知道 - 显示用户表单 - 最好的做法是

I am aware that - showing UserForms - it's best practice to

  • 在用户表单代码中处理QueryClose (If CloseMode = vbFormControlMenu ...)
  • 没有在其中执行Unload Me,只是一个胆小的Me.Hide指令(在通过 Cancel = True 防止 [x]-itting 和最终自毁之后)
  • 在[class]代码中设置相关变量/[property](例如.IsCancelled=True)
  • 为了能够通过调用代码卸载 UF.
  • handle QueryClose within the userform code (If CloseMode = vbFormControlMenu ...)
  • doing no Unload Me therein, just a timid Me.Hide instruction (after preventing [x]-itting and eventual self-destruction via Cancel = True )
  • setting a related variable/[property] within the [class] code (e.g. .IsCancelled=True)
  • in order to be able to have the UF unloaded by the calling code.

有用的链接

可以在 UserForm1.Show?"="noreferrer">https://rubberduckvba.wordpress.com/2017/10/25/userform1-show/以及许多示例性 SO 答案(感谢 Mathieu Guindon aka Mat's Mug 和 RubberDuck).

An outstanding overview "UserForm1.Show?" can be found at https://rubberduckvba.wordpress.com/2017/10/25/userform1-show/ as well as in numerous examplary SO answers (thx to Mathieu Guindon aka Mat's Mug and RubberDuck).

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

1) 模态用户表单的工作示例

据我所知 - 我确实尝试学习 - 以下代码应该适用于 modal UF:

As far as I understood - and I do try to learn -, the following code should be okay for modal UF's:

案例 1a) .. 使用 局部变量 用于 UF 实例,如常见:

Case 1a) .. with a local variable for the UF instance, as often seen:

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 代码块:

Case 1b) .. without a local variable, but using a With New codeblock:

' ----------------------------------------------------------
' >> 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 实例时出现问题.

Problems arise using a MODELESS UserForm instance.

好的,with block 方法(参见 1b)应该足以在 x-iting 之后销毁任何对象引用:

Okay, the with block method (cf. 1b) should be sufficient to destroy any object reference after x-iting it:

  With New UserForm1
      .Show vbModeless  ' << show modeless uf
  End With

如果我尝试,但是

  • a) 获取有关可能取消订阅的用户的信息以及
  • b) 如果在 Show 指令之后使用局部变量(例如ufA")进行洗礼,则卸载一个表单,
  • a) get information about a possible user cancelling as well as
  • b) to Unload a form if baptized using a local variable (e.g. "ufA") after the Show instruction,

所有代码行都将立即执行,正是因为表单是无模型的:

all code lines will be executed at once for precisely the reason that the form is MODELESS:

  • 代码显示表单,下一刻..
  • 代码发现没有用户取消,因为没有时间进行任何用户操作,下一刻..
  • [如果对用户表单使用局部变量,代码会卸载表单]

3) 问题

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

How can I handle a) correctly reported UserForm cancels by the calling code of a MODELESS form as well as b) a (necessary?) unloading if using a local variable?

推荐答案

确实,我一直在关注模态形式——因为这是最常用的形式.感谢您对该文章的反馈!

Indeed, I've been focusing quite a lot on modal forms - because that's what's most commonly used. Thanks for the feedback on that article!

但非模态表单的原则是相同的:只需扩展链接文章和 此处.

The principles are the same for non-modal forms though: simply expand on the Model-View-Presenter pattern roughly outlined in the linked article and here.

区别在于非模态形式需要范式转换:您不再响应预设的事件序列 - 相反,您需要响应一些可能发生的异步事件在任何给定的时间,或不.

The difference is that a non-modal form needs a paradigm shift: you're no longer responding to a preset sequence of events - rather, you need to respond to some asynchronous events that may happen at any given time, or not.

  • 在处理模态表单时,有一个显示前"和一个隐藏后",在表单隐藏后立即运行.您可以使用事件处理展示时"发生的任何事情.
  • 处理非模态表单时,有显示前",然后显示时"和显示后"两者都需要通过事件处理.
  • When handling a modal form, there's a "before showing" and then an "after hiding" that runs immediately after the form is hidden. You can handle anything that happens "while showing" using events.
  • When handling a non-modal form, there's a "before showing", and then "while showing" and "after showing" both need to be handled through events.

让你的 Presenter 类模块负责在模块级和 WithEvents 保持 UserForm 实例:

Make your presenter class module responsible for holding the UserForm instance, at module-level and WithEvents:

Option Explicit
Private WithEvents myModelessForm As UserForm1

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

The presenter's Show method will Set the form instance and display it:

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块可以't 工作:对象将超出范围,然后才有意义.这就是您将实例存储在模块级别的私有字段中的原因:现在表单的生命周期与演示者实例的生命周期一样长.

You don't want the form instance to be local to the procedure here, so a local variable or a With block can't work: the object will be out of scope before you mean it to. That's why you store the instance in a private field, at module level: now the form lives as long as the presenter instance does.

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

Now, you need to make the form "talk" to the presenter - the easiest way is to expose events in the UserForm1 code-behind - for example if we want the user to confirm cancellation, we'll add a ByRef parameter to the event, so the handler in the presenter can pass the information back to the event source (i.e. back to the form code):

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 事件:

Now the presenter can handle that FormCancelled event:

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 来执行其他操作 - 同样,您只需为其公开一个事件,并在演示者中处理它:

A non-modal form wouldn't typically have "ok" and "cancel" buttons though. Rather, you'd have a number of functionalities exposed, for example one that brings up some modal dialog UserForm2 that does something else - again, you just expose an event for it, and handle it in the presenter:

Public Event ShowGizmo()

Private Sub ShowGizmoButton_Click()
    RaiseEvent ShowGizmo
End Sub

主持人说:

Private Sub myModelessForm_ShowGizmo()
    With New GizmoPresenter
        .Show
    End With
End Sub

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

Note that the modal UserForm2 is a concern of a separate presenter class.

这篇关于VBA - 正确销毁无模式用户窗体实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆