vba 仍然暂停代码执行的无模式形式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16859038/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-11 21:22:52  来源:igfitidea点击:

Modeless form that still pauses code execution

vbamodal-dialoguserformmodeless

提问by lfrandom

Is there anyway to have a userform that acts modeless, while still pausing code execution like a modal form?

是否有一个无模式的用户表单,同时仍然像模态表单一样暂停代码执行?

I'd like the userform to show, but still allow interaction with the parent program. Modal forms block interaction with the parent program. A modeless form would work, but I would like the code execution to pause while the form is up.

我希望显示用户表单,但仍允许与父程序进行交互。模态形式块与父程序的交互。无模式表单可以工作,但我希望代码执行在表单启动时暂停。

I've worked around this by creating an infinite loop that checks if the form is visible, but that seems a bit hacky.

我通过创建一个检查表单是否可见的无限循环来解决这个问题,但这似乎有点麻烦。

Public Sub GetFormInfoAndDoStuff    
  ufForm.show vbModeless

  Do while ufForm.Visible
    DoEvents
  Loop

  ' Do other stuff dependent on form 
End Sub

EDITED to clarify that code after .show exists which must execute after the user form is done

编辑以澄清 .show 之后的代码存在,该代码必须在用户表单完成后执行

回答by David Zemens

You should be able display the form as vbModelessand only execute code when specifically requested, i.e., from a CommandButtonor other control.

您应该能够将表单显示为vbModeless并且仅在特别请求时才执行代码,即,来自某个CommandButton或其他控件。

You then leave the form visible/shown until it is specifically closed, via the "X" button or via another control which calls the UserForm_Terminateevent.

然后,通过“X”按钮或通过调用该UserForm_Terminate事件的另一个控件,使表单保持可见/显示,直到它被明确关闭。

In order to achieve this, you may need to move some of your executable code in to another subroutine and/or module, and call this subroutine for example from a CommandButton_Clickevent.

为了实现这一点,您可能需要将一些可执行代码移动到另一个子例程和/或模块中,并例如从CommandButton_Click事件中调用该子例程。

You already have a subroutine somewhere that contains a line like:

您已经在某处有一个包含以下行的子程序:

Sub ShowTheForm()

    UserForm1.Show vbModeless
End Sub

So the form is displayed properly to allow user-input to the parent application.

因此该表单被正确显示以允许用户输入到父应用程序。

You don't really need to put any other code in the above module. We will put the other code in other modules/subs, and then call it from user controls like command buttons.

你真的不需要在上面的模块中放入任何其他代码。我们会将其他代码放在其他模块/子中,然后从命令按钮等用户控件中调用它。

Example:

例子:

Take all of your executable code, and put it in another subroutine (and if it suits your organizational preference, another module), like:

把你所有的可执行代码放在另一个子程序中(如果它适合你的组织偏好,另一个模块),比如:

Sub MyMacro(msg$)
    MsgBox msg
End Sub

On the UserForm, add a command button and assign it the following code:

在用户窗体上,添加一个命令按钮并为其分配以下代码:

Sub CommandButton1_Click()
    MyMacro "hello"
End Sub

Now, the form will display until the user clicks the "X" button. Code will only run when called from the command button.

现在,表单将一直显示,直到用户单击“X”按钮。代码只会在从命令按钮调用时运行。

EDIT FOR CLARIFICATION

编辑澄清

You don't need to "pause" the execution using this method. Execution ends once the form is displayed modelessly, and the form persists. The object has some events which you may use to trigger further execution of code.

您不需要使用此方法“暂停”执行。一旦表单无模式显示,执行就会结束,并且表单仍然存在。该对象有一些事件,您可以使用它们来触发代码的进一步执行。

回答by Greg Lovern

Here's what I do.

这就是我所做的。

This example is for a form I called "Find Header". The code tries to find several column headers, but the markers for a few of them may be missing (and the header text may have been overwritten with something random), so I may need to pause and ask the user to locate (click on) some of the headers for me:

此示例适用于我称为“查找标题”的表单。代码尝试查找多个列标题,但其中一些的标记可能丢失(并且标题文本可能已被随机覆盖),因此我可能需要暂停并要求用户定位(单击)我的一些标题:

First, put this declaration in a standard module:

首先,将此声明放在标准模块中

Public bDlgFindHeaderIsShowingModeless As Boolean

Then, put this in the event procedure for any button or other control that dismisses the modeless dialog, such as the Click events for the form's OK and Cancel buttons:

然后,将它放在任何按钮或其他关闭无模式对话框的控件的事件过程中,例如窗体的 OK 和 Cancel 按钮的 Click 事件:

bDlgFindHeaderIsShowingModeless = False

Then, put this wherever in your code you want to show the modeless form while paused for user interactivity:

然后,将它放在代码中您想要在暂停以进行用户交互时显示无模式表单的任何位置:

bDlgFindHeaderIsShowingModeless = True 'init
frmFindHeader.Show vbModeless
Do
    If Not bDlgFindHeaderIsShowingModeless Then Exit Do
    DoEvents
Loop

Yes, it churns the CPU, so you might not want to do it if you're on a single-core processor and there are critically important background processes running. But it works; the user is able to easily and smoothly interact with Excel while the modeless form displays. The user doesn't feel like they are fighting an endless loop.

是的,它会搅动 CPU,因此如果您使用的是单核处理器并且正在运行非常重要的后台进程,您可能不想这样做。但它有效;用户可以在显示无模式表单时轻松顺畅地与 Excel 交互。用户并不觉得他们在与无休止的循环作斗争。

回答by Nate

The Best method would be to use two different subs. I was able to solve this problem without splitting my sub as follows:

最好的方法是使用两个不同的潜艇。我能够在不拆分子程序的情况下解决这个问题,如下所示:

Public Mode as Boolean

Sub Stuff()
    If Mode Then
        Goto Continue
    End If

    'Code before Userform

    Mode = True
    Userform.Show vbModeless
    Exit Sub

Continue:
    Mode = False

   'Rest of your code

End Sub

I made "Mode" a global variable because I use this userform for multiple subs. If you are using a single sub you can use it locally. I also made "Mode" false when opening this workbook by going under "ThisWorkbook" Tab and adding the following code:

我将“模式”设为全局变量,因为我将此用户表单用于多个订阅者。如果您使用的是单个 sub,则可以在本地使用它。通过转到“ThisWorkbook”选项卡并添加以下代码打开此工作簿时,我还使“模式”为假:

Private Sub Workbook_Open()
    Mode = False
End Sub

This again will only be needed if you use your userform for more than one sub. Last add this code under your Userform code when your proceed button is pressed.

仅当您将用户表单用于多个子时才需要再次这样做。当您按下继续按钮时,最后将此代码添加到您的用户表单代码下。

Private Sub Confirm_Click()
    Userform.hide
    if Mode Then
        Call Stuff
    End If 
End Sub

If you are only are using the one sub method skip the if statement and just call the sub.

如果您只使用 one sub 方法,请跳过 if 语句并调用 sub。