如何:工作宏期间进度条上的动画(三点)

How to: Animation (triple dot) on progress bar during working macro

我有一个进度条,它以百分比和 'animated' 矩形显示进度。

我知道如何在代码中根据“标记”显示宏的进度,但事实并非如此。在代码中称为“标记”的代码示例,用于更改进度条上的百分比:

Sub progress(pctCompl As Long)

 Progression.Text.Caption = pctCompl & "% Completed"
 Progression.Bar.Width = pctCompl * 2

 DoEvents 'update the userform

End Sub

我想知道是否可以在该进度条上的“请稍候”后面做额外的动画 - 三重 dot:1 点,1 秒停顿,2 点,1 秒停顿,3 点,1 秒停顿。这是该动画的 1 个循环。

我正在尝试做一些事情,主要是实现无限循环或宏,除了三点动画之外什么都不做,这会冻结 Excel 应用程序。

Private Sub UserForm_Activate()

Do Until Progression.Bar.Width = 200
    Progression.Text2.Caption = "Please wait."
    Progression.Repaint
    Application.Wait Now + TimeValue("0:00:01")
    Progression.Text2.Caption = "Please wait.."
    Progression.Repaint
    Application.Wait (Now + TimeValue("0:00:01"))
    Progression.Text2.Caption = "Please wait..."
    Progression.Repaint
    Application.Wait (Now + TimeValue("0:00:01"))
Loop

End Sub

我认为这是提出此类问题的好地方 - 是否可行,如果可行,如何实现?

有时我喜欢在 UserForm 上 'animate' 作为进度指示器的图像,为此我使用 Win API 计时器。下面的代码可能有点 'overkill' 满足您的需求,因为图像更改需要由事件或 Repaint 触发,后者会导致闪烁。我相信 Labels 会在 属性 值更改后立即更新。如果是这种情况,那么您可以省略下面显示的侦听器 class 并相应地调整代码。

根据上述警告,框架实现可能如下所示:

用户表单代码

注意:我的用户表单有一个开始按钮、一个停止按钮和一个名为 lblWait 的标签。

Option Explicit

Private WithEvents mTimerListener As cTimerListener

Private Sub btnStart_Click()
    HandleStartTimer mTimerListener
End Sub

Private Sub btnStop_Click()
    HandleStopTimer
End Sub

Private Sub mTimerListener_DotCountIncremented(count As Long)
    Me.lblWait = "Please wait" & String(count, ".")
End Sub

Private Sub UserForm_Initialize()
    Set mTimerListener = New cTimerListener
End Sub

Class代码

注意:我将此称为 class cTimerListener。

Option Explicit

Public Event DotCountIncremented(count As Long)

Private mDotCount As Long

Public Property Let DotCount(RHS As Long)
    mDotCount = RHS
    If mDotCount > 3 Then mDotCount = 0
    RaiseEvent DotCountIncremented(mDotCount)
    DoEvents
End Property

Public Property Get DotCount() As Long
    DotCount = mDotCount
End Property

和模块代码

Option Explicit

#If VBA7 Then
    Private Declare PtrSafe Function SetTimer Lib "user32" ( _
        ByVal HWnd As LongPtr, _
        ByVal nIDEvent As Long, _
        ByVal uElapse As Long, _
        ByVal lpTimerFunc As LongPtr) As Long

    Private Declare PtrSafe Function KillTimer Lib "user32" ( _
        ByVal HWnd As LongPtr, _
        ByVal nIDEvent As Long) As Long

#Else
    Private Declare Function SetTimer Lib "user32" ( _
        ByVal HWnd As Long, _
        ByVal nIDEvent As Long, _
        ByVal uElapse As Long, _
        ByVal lpTimerFunc As Long) As Long

    Private Declare Function KillTimer Lib "user32" ( _
        ByVal HWnd As Long, _
        ByVal nIDEvent As Long) As Long
#End If

Private mTimerId As Long
Private mTimerListener As cTimerListener

Public Sub HandleStartTimer(timerListener As cTimerListener)
    Set mTimerListener = timerListener
    #If VBA7 Then
        mTimerId = SetTimer(0&, 0&, 0.5 * 1000, AddressOf TimerProc64)
    #Else
        mTimerId = SetTimer(0&, 0&, 0.5 * 1000, AddressOf TimerProc32)
    #End If
End Sub

Public Sub HandleStopTimer()
    KillTimer 0&, mTimerId
End Sub

#If VBA7 Then
    Private Sub TimerProc64(ByVal HWnd As LongPtr, ByVal uMsg As Long, _
        ByVal nIDEvent As Long, ByVal dwTimer As Long)

        TimerProc
    End Sub
#Else
    Private Sub TimerProc32(ByVal HWnd As Long, ByVal uMsg As Long, _
        ByVal nIDEvent As Long, ByVal dwTimer As Long)

        TimerProc
    End Sub
#End If

Private Sub TimerProc()
    If Not mTimerListener Is Nothing Then
        With mTimerListener
            .DotCount = .DotCount + 1
        End With
    End If
End Sub