热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

Intro:Iamawarethat-showingUserForms-itsbestpracticeto我知道-显示UserForms-这是最好的做法

Intro:

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

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

  • handle QueryClose within the userform code (If CloseMode = vbFormControlMenu ...)
  • 在userform代码中处理QueryClose(如果CloseMode = vbFormControlMenu ...)

  • doing no Unload Me therein, just a timid Me.Hide instruction (after preventing [x]-itting and eventual self-destruction via Cancel = True )
  • 不要在其中卸载我,只是一个胆小的Me.Hide指令(在通过Cancel = True防止[x] -it和最终自毁之后)

  • setting a related variable/[property] within the [class] code (e.g. .IsCancelled=True)
  • 在[class]代码中设置相关变量/ [property](例如.IsCancelled = True)

  • in order to be able to have the UF unloaded by the calling code.
  • 为了能够通过调用代码卸载UF。

Useful link

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 @Mats'Mug and RubberDuck).

一个出色的概述“UserForm1.Show?”可以在https://rubberduckvba.wordpress.com/2017/10/25/userform1-show/以及许多示例性的SO答案中找到(thx到@ Mats'Mug和RubberDuck)。


1) Working examples for modal UserForms

1)模态UserForms的工作示例

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

据我了解 - 我确实尝试学习 - ,对于模态UF,以下代码应该没问题:

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

案例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

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

情况1b)..没有局部变量,但使用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) Problem

Problems arise using a MODELESS UserForm instance.

使用MODELESS UserForm实例会出现问题。

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

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

  With New UserForm1
      .Show vbModeless  ' <

If I try, however to

如果我尝试,但是

  • a) get information about a possible user cancelling as well as
  • a)获取有关可能的用户取消的信息以及

  • b) to Unload a form if baptized using a local variable (e.g. "ufA") after the Show instruction,
  • b)如果在Show指令后使用局部变量(例如“ufA”)进行洗礼,则卸载表格,

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

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

  • code shows the form, the next moment ..
  • 代码显示表单,下一刻..

  • code finds no user cancelling, as there was no time for any user action, the next moment ..
  • 代码找不到用户取消,因为没有时间进行任何用户操作,下一刻..

  • [code unloads the form if using a local variable for the userform]
  • [如果为userform使用局部变量,代码将卸载表单]


3) Question

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?

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

3 个解决方案

#1


3  

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.
  • 处理非模态形式时,有“显示前”,然后“显示时”和“显示后”两者都需要通过事件处理。

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

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

Option Explicit
Private WithEvents myModelessForm As UserForm1

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

演示者的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

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.

您不希望表单实例在此处的过程是本地的,因此局部变量或With块无法工作:对象将超出范围之前您的意思。这就是为什么您在模块级别将实例存储在私有字段中的原因:现在只要演示者实例执行,表单就会存在。

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):

现在,您需要使表单与演示者“对话” - 最简单的方法是在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

Now the presenter can handle that FormCancelled event:

现在,演示者可以处理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

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:

非模态形式通常不会有“ok”和“cancel”按钮。相反,你会暴露出许多功能,例如一个带来一些模态对话框UserForm2的东西 - 再次,你只是为它公开一个事件,并在演示者中处理它:

Public Event ShowGizmo()

Private Sub ShowGizmoButton_Click()
    RaiseEvent ShowGizmo
End Sub

And the presenter goes:

演示者说:

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

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

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

#2


2  

I typically tie the lifetime of a modeless userform instance to the workbook's by putting code along those lines behind ThisWorkbook:

我通常将无模式用户表单实例的生命周期与工作簿相关联,方法是将代码放在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

You can then refer to the modeless code throughout your code using e.g.

然后,您可以使用例如代码参考代码中的无模式代码。

ThisWorkbook.MyForm.Show vbModeless

etc.

#3


2  

For modeless forms, Use DoEvents coupled with custom userform property.

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


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

'/ User Form

'/用户表格

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

推荐阅读
author-avatar
xzh
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有