在VBA中,显示进度条UserForms是模态还是非模态更好?

8
在VBA中,显示ProgressBar UserForms是模态的还是非模态的更好?在开发VBA进度指示器方面有哪些最佳实践?
非模态的UserForms需要使用Application.Interactive = False,而模态的UserForms则会阻止与应用程序的任何交互,直到核心过程完成或被取消。
然而,如果使用了Application.Interactive = False,那么Esc键将中断代码执行,因此在UserForm和调用过程中都需要使用Application.EnableCancelKey = xlErrorHandler和错误处理(Err.Number = 18)。
资源密集型的调用过程也可能导致非模态UserForms中的CommandButton_ClickUserForm_Activate事件失灵。
一般来说,使用模态UserForms的进度指示器似乎更简单,因为正在执行的代码完全包含在UserForm模块中,并且不需要传递变量。
然而,使用模态UserForms作为进度指示器的问题在于,每个需要进度指示器的过程都需要一个单独的UserForm模块,因为调用过程必须在UserForm_Activate过程内部。
因此,虽然可以在非模态UserForm中拥有一个可重用的进度指示器,但它的可靠性将低于从多个模态UserForms中执行代码的可靠性。
哪种方式更好?
谢谢!
5个回答

3

还有第三种方法,使用 Application.StatusBar。 您甚至可以通过使用 U+25A0 和 U+25A1 字符序列来模拟真正的进度条。


感谢您的建议GSerg,但我之前尝试过使用它,但Excel 2007停止更新窗口并在应用程序窗口顶部显示“未响应”。 - Kuyenda
3
我对Office 2007不是很有经验,但如果您在将值设置为“StatusBar”之后立即调用DoEvents会怎样呢? - GSerg
哦,那个起作用了。哇,这是一个不错的选项,不是吗!谢谢GSerg! - Kuyenda
1
使用状态栏作为进度指示器的一个问题是没有办法提供取消按钮。 - Kuyenda
不错的技巧,但它并没有真正回答关于模态/非模态最佳实践问题,需要在使用状态栏方面给出技术性答案。 - Anonymous Type
DoEvents存在的问题是,当宏正在运行时,用户可以与电子表格进行交互,这可能会导致问题...不太好! - Raf

1

我打算结束这个话题并宣布Modal是胜利者。我已经尝试过两种方式,但使用modeless用户窗体时,你最终会试图关闭太多的漏洞。Modal更难一些,因为它更加严格,但它鼓励你将代码分解成更小的块,这在长期来看是更好的。


1

绝对是模态的。 如果你要考虑非模态,你应该在一个独立的进程线程上运行它,而不是在Excel.exe的主线程上。


1
I think that the initial topic is worth of replying since the question was formulated so nicely that google finds it first.

第一部分 - 理论

首先,在模块之间传递变量并不困难。

你需要做的唯一一件事情就是创建一个独立的模块,并将所有全局变量放在那里。然后,在所有表单、工作表和模块中你都能够读取它们。

第二点 是窗口应该是非模态的。为什么呢?因为要保持代码的灵活性,即:

  1. 最常规的过程的函数不应该位于用户窗体模块中。
  2. 你可以从任何地方调用带有进度条的窗口。
  3. 常规函数/过程之间的唯一连接是全局变量。

这里的多功能性是一个很大的优势。

第二部分 - 实践

1)创建一个名为 "Declaration" 的模块,并添加全局变量:

Public StopForce As Integer '这个变量将被用作指示器,表示用户按下了取消按钮。

公共单精度变量PCTDone '这是已完成工作的百分比

公共字符串变量CurrentFile '我们想要传递到表单的任何其他参数。

2)创建带有按钮的表单。在按钮的OnClick事件中,应该有一个代码,其中我们引用Declaration模块中的全局变量StopForce

 Private Sub CommandButton1_Click()

 Declaration.StopForce = 1
  End Sub

3) 添加一个过程,用于更新进度条

Sub UpdateProgressBar(PCTDone_in As Single)
With UserForm1
    ' Update the Caption property of the Frame control.
    .FrameProgress.Caption = Format(PCTDone_in, "0%")
    ' Widen the Label control.
    .LabelProgress.Width = PCTDone_in * _
        (.FrameProgress.Width)
    ' Display the current file from global variable   
    .Label1.Caption = Declaration.CurrentFile
End With
End Sub

4) 在任何其他模块中,我们必须拥有执行该例程的函数或过程/子程序:

 For i=1 to All_Files

 Declaration.CurrentFile = myFiles (i)

 FormFnc.UpdateProgressBar (i / .Range("C11").Value)


 DoEvents

 If Declaration.StopForce = 1 Then
    GoTo 3
 End If

 Next i

请注意,无模式 UserForms 在 Mac OS 上似乎根本无法工作。 - Wolfgang Fahl

0

实际上,您拥有以下属性,根据您的需求会产生优缺点:

Type      | Impact on UI | Impact on caller execution
----------|--------------|-----------------------------
Modal     | Blocked      | Blocked until Form is closed
Modeless  | Not blocked  | Continues

如果您想阻止用户界面并让调用者继续执行,则需要使用Application.OnTime以模态模式打开表单。

我尝试使用Application.OnTime打开表单时收到错误消息(我尝试了不同的变体)。您使用什么语法来实现这个? - Albin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接