我有一个进度条,一个小的弹出窗体,链接到一个耗时的子程序的进度。
我正在尝试在进度条上放置取消按钮。当子程序在后台运行时,我无法点击进度条表单中的任何内容。
有没有办法在子程序正在进行时单击不同表单上的按钮?
是的,这是可能的。使用DoEvents
告诉VBA继续抽取/处理Windows消息;结果可能不像真正的异步代码那样响应,但应该足以启用单击[取消]按钮并处理取消。
this article中的代码(免责声明:我写的)最初是为Excel编写的,使用的是UserForm
(当主机是Access时隐藏在VBE中,但Access VBA项目绝对可以包含和使用UserForm
模块)。
您将要删除特定于Excel的位,例如QualifyMacroName
:
Private Function QualifyMacroName(ByVal book As Workbook, ByVal procedure As String) As String
QualifyMacroName = "'" & book.FullName & "'!" & procedure
End Function
然后修改Create
工厂方法以要求instance
参数,如下所示:
Public Function Create(ByVal procedure As String, ByVal instance As Object, Optional ByVal initialLabelValue As String, Optional ByVal initialCaptionValue As String, Optional ByVal completedSleepMilliseconds As Long = 1000, Optional canCancel As Boolean = False) As ProgressIndicator
Dim result As ProgressIndicator
Set result = New ProgressIndicator
result.Cancellable = canCancel
result.SleepMilliseconds = completedSleepMilliseconds
If Not instance Is Nothing Then
Set result.OwnerInstance = instance
Else
Err.Raise 5, TypeName(Me), "Invalid argument: 'instance' must be a valid object reference."
End If
result.ProcedureName = procedure
If initialLabelValue <> vbNullString Then result.ProgressView.ProgressLabel = initialLabelValue
If initialCaptionValue <> vbNullString Then result.ProgressView.Caption = initialCaptionValue
Set Create = result
End Function
编译完成后,可以通过注册执行实际工作的worker方法来使用ProgressIndicator
,如下所示:
With ProgressIndicator.Create("Run", New MyLongRunningMacro, canCancel:=True)
.Execute
End With
其中MyLongRunningMacro
是一个带有Run
方法的类模块,可能看起来像这样:
Public Sub Run(ByVal progress As ProgressIndicator)
Dim thingsDone As Long
For Each thing In ThingsToDo
Application.Run thing
thingsDone = thingsDone + 1
progress.UpdatePercent thingsDone / ThingsToDo.Count
If ShouldCancel(progress) Then
' user confirmed they want to cancel the whole thing.
' perform any clean-up or rollback here
Exit Sub
End If
Next
End Sub
Private Function ShouldCancel(ByVal progress As ProgressIndicator) As Boolean
If progress.IsCancelRequested Then
If MsgBox("Cancel this operation?", vbYesNo) = vbYes Then
ShouldCancel = True
Else
progress.AbortCancellation
End If
End If
End Function
例如,ThingsToDo
可能是要执行的宏的集合。使用循环可以更轻松地报告进度百分比,但是虽然它也可以用于一系列操作,但是干净地处理取消有点困难:
Public Sub Run(ByVal progress As ProgressIndicator)
Dim thingsDone As Long
DoThingOne
If Not UpdateAndContinue(progress, 0.33) Then Exit Sub
DoThingTwo
If Not UpdateAndContinue(progress, 0.66) Then Exit Sub
DoThingThree
If Not UpdateAndContinue(progress, 1) Then Exit Sub
End Sub
Private Function UpdateAndContinue(ByVal progress As ProgressIndicator, ByVal percentCompleted As Double) As Boolean
progress.UpdatePercent percentCompleted
If ShouldCancel(progress) Then
' user confirmed they want to cancel the whole thing.
' perform any clean-up or rollback here
Exit Function
Else
UpdateAndContinue = True
End If
End Function