VERSION 5.00 Begin {C62A69F0-16DC-11CE-9E98-00AA00574A4F} CSE_ProgressBar Caption = "Выполнение процесса" ClientHeight = 2850 ClientLeft = 45 ClientTop = 330 ClientWidth = 5280 OleObjectBlob = "CSE_ProgressBar.frx":0000 ShowModal = 0 'False StartUpPosition = 2 'CenterScreen End Attribute VB_Name = "CSE_ProgressBar" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = True Attribute VB_Exposed = False ' =========== Progress Bar ================ ' Shared module version: 20210827 ' Tested in: ' Depends on: ' Required reference: Option Explicit Private thisWindow_& Private isInterrupted_ As Boolean Private canBeInterrupted_ As Boolean Private minA_& Private maxA_& Private valueA_& Private progressA_ As Double Private minB_& Private maxB_& Private valueB_& Private progressB_ As Double Private tStart_& Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long 'Windows API calls to remove the [x] from the top-right of the form Private Declare PtrSafe Function FindWindow Lib "user32" _ Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long Private Declare PtrSafe Function GetWindowLong Lib "user32" _ Alias "GetWindowLongA" (ByVal nHwnd As Long, ByVal nIndex As Long) As Long Private Declare PtrSafe Function SetWindowLong Lib "user32" _ Alias "SetWindowLongA" (ByVal nHwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long 'Windows API calls to bring the progress bar form to the front of other modeless forms Private Declare PtrSafe Function GetForegroundWindow Lib "user32" () As Long Private Declare PtrSafe Function GetWindowThreadProcessId Lib "user32" (ByVal nHwnd As Long, ByRef lpdwProcessId As Long) As Long Private Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal nHwnd As Long) As Long Private Const UDATE_RATE = 0.005 ' Частота обновления - каждый 0.5% Private Sub UserForm_Initialize() isInterrupted_ = False canBeInterrupted_ = False minA_ = 0 maxA_ = 100 valueA_ = 50 progressA_ = 50 Call Init("Загрузка...") If Application.Name <> "Microsoft Visio" Then _ Call PositionInMiddle Call RemoveCloseButton End Sub Public Function Init(sTitle$, _ Optional sHeader$ = vbNullString, _ Optional minVal& = 0, _ Optional maxVal& = 100, _ Optional curVal& = 0, _ Optional canInterrupt As Boolean = False) canBeInterrupted_ = canInterrupt btnInterrupt.Visible = canInterrupt isInterrupted_ = False If thisWindow_ = 0 Then Me.Caption = "VBAProgressBar" thisWindow_ = FindWindow(vbNullString, Me.Caption) End If tStart_ = GetTickCount() minA_ = minVal maxA_ = maxVal minB_ = 0 maxB_ = 100 valueB_ = 0 Call EnableSecondBar(False) Me.Title = sTitle Me.HeaderLbl.Caption = sHeader Me.DescriptionTB.Text = vbNullString Call SetAInternal(curVal, bForceUpdate:=True) End Function Public Function InitSecondBar(Optional minVal& = 0, Optional maxVal& = 100, Optional curVal& = 0) Call EnableSecondBar(True) minB_ = minVal maxB_ = maxVal Call SetBInternal(curVal, bForceUpdate:=True) End Function Public Function ShowModeless() Call UpdateBar Call Me.Show(vbModeless) End Function Private Sub btnInterrupt_Click() HeaderLbl.Caption = "Инициирована отмена, ожидайте..." isInterrupted_ = True btnInterrupt.Enabled = False Call RepaintEx End Sub Private Sub UserForm_QueryClose(ByRef nCancelCode As Integer, ByRef nCloseMode As Integer) If nCloseMode = vbFormControlMenu And canBeInterrupted_ Then _ nCancelCode = True End Sub ' ====== Access ===== Public Property Get Interrupted() As Boolean Interrupted = isInterrupted_ End Property Public Property Get Time() As Double Time = GetTickCount() - tStart_ End Property Public Property Get TimeStr() As String TimeStr = Format(Time / 1000, "# ##0.##") & " сек" End Property Public Property Get Title() As String Title = Me.Caption End Property Public Property Get Description() As String Description = DescriptionTB.Text End Property Public Property Get Header() As String Header = HeaderLbl.Caption End Property Public Property Get SecondBarEnabled() As Boolean SecondBarEnabled = FrameSecBar.Visible End Property Public Property Get ValueA() As Long ValueA = valueA_ End Property Public Property Get MinA() As Long MinA = minA_ End Property Public Property Get MaxA() As Long MaxA = maxA_ End Property Public Property Get ProgressA() As Double If maxA_ = minA_ Then ProgressA = 100 Else ProgressA = VBA.Abs((valueA_ - minA_) / (maxA_ - minA_)) End If End Property Public Property Get ValueB() As Long ValueB = valueB_ End Property Public Property Get MinB() As Long MinB = minB_ End Property Public Property Get MaxB() As Long MaxB = maxB_ End Property Public Property Get ProgressB() As Double If maxB_ = minB_ Then ProgressB = 100 Else ProgressB = VBA.Abs((valueB_ - minB_) / (maxB_ - minB_)) End If End Property ' ===== Modify ===== Public Property Let Title(ByVal newVal$) Me.Caption = newVal Call RemoveCloseButton End Property Public Property Let Description(ByVal newVal$) DescriptionTB.Text = newVal Call RepaintEx End Property Public Property Let Header(ByVal newVal$) HeaderLbl.Caption = newVal Call RepaintEx End Property ' ====== Actions === Public Function IncrementA(Optional delta& = 1) If Me.Visible Then _ Call SetAInternal(valueA_ + delta, bForceUpdate:=False) End Function Public Function IncrementB(Optional delta& = 1) If Me.Visible Then _ Call SetBInternal(valueB_ + delta, bForceUpdate:=False) End Function Public Function HideSecondBar() Call EnableSecondBar(False) End Function Public Function SetA(ByVal newVal&, Optional bForceUpdate As Boolean = False) If Me.Visible Then _ Call SetAInternal(newVal, bForceUpdate) End Function Public Function SetB(ByVal newVal&, Optional bForceUpdate As Boolean = False) If Me.Visible Then _ Call SetBInternal(newVal, bForceUpdate) End Function ' ======= Private Function EnableSecondBar(bEnable As Boolean) FrameSecBar.Visible = bEnable Call RepaintEx End Function Private Function PositionInMiddle() Dim nTopOffset&: nTopOffset = (Application.UsableHeight / 2) - (Me.Height / 2) Dim nLeftOffset&: nLeftOffset = (Application.UsableWidth / 2) - (Me.Width / 2) Me.StartUpPosition = 0 Me.Top = Application.Top + IIf(nTopOffset > 0, nTopOffset, 0) Me.Left = Application.Left + IIf(nLeftOffset > 0, nLeftOffset, 0) End Function Private Function SetAInternal(ByVal newVal&, bForceUpdate As Boolean) valueA_ = newVal If valueA_ > maxA_ Then _ valueA_ = maxA_ If valueA_ < minA_ Then _ valueA_ = minA_ Call SetProgressA(Me.ProgressA, bForceUpdate) End Function Private Function SetBInternal(ByVal newVal&, bForceUpdate As Boolean) valueB_ = newVal If valueB_ > maxB_ Then _ valueB_ = maxB_ If valueB_ < minB_ Then _ valueB_ = minB_ Call SetProgressB(Me.ProgressB, bForceUpdate) End Function Private Function SetProgressA(newProgress As Double, bForceUpdate As Boolean) If Not bForceUpdate And Abs(newProgress - progressA_) < UDATE_RATE Then _ Exit Function progressA_ = newProgress Call UpdateTime Call UpdateBar Call RepaintEx End Function Private Function SetProgressB(newProgress As Double, bForceUpdate) If Not bForceUpdate And Abs(newProgress - progressB_) < UDATE_RATE Then _ Exit Function progressB_ = newProgress Call UpdateTime Call UpdateBar Call RepaintEx End Function Private Function UpdateBar() fraInside.Width = VBA.Int(lblBack.Width * progressA_) lblSecBar.Width = VBA.Int(FrameSecBar.Width * progressB_) lblBack.Caption = Format(progressA_, "0%") lblFront.Caption = Format(progressA_, "0%") End Function Private Function RemoveCloseButton() Const WS_SYSMENU& = &H80000 Const GWL_STYLE& = (-16) Dim lStyle&: lStyle = GetWindowLong(thisWindow_, GWL_STYLE) If lStyle And WS_SYSMENU > 0 Then _ Call SetWindowLong(thisWindow_, GWL_STYLE, (lStyle And Not WS_SYSMENU)) End Function Private Function BringToFront() Dim lFocusThread&: lFocusThread = GetWindowThreadProcessId(GetForegroundWindow(), 0) Dim lThisThread&: lThisThread = GetWindowThreadProcessId(thisWindow_, 0) If lFocusThread = lThisThread Then Call SetForegroundWindow(thisWindow_) Else DoEvents End If End Function Private Function UpdateTime() TimeLabel.Caption = "Длительность: " & TimeStr() End Function Private Function RepaintEx() If Me.Visible Then Call Me.Repaint ' BringToFront ' Раскомментировать, чтобы окно само всплывало при каждом обновлении (ужасно бесит в фоновом режиме) End If DoEvents End Function