Paski postępu w MS Access
Funkcja SysCmd obiektu Application umożliwia wyświetlenie wskaźnika zaawansowania operacji w dolnej części okna MS Access gdzie znajduje się pasek stanu, który służy do wyświetlania różnych informacji na temat danych w formularzu. Na przykład może zawierać opis pola, komunikaty o błędach, ostrzeżenia lub wspomniany wcześniej pasek postępu.

Wygląd paska postępu w różnych wersjach MS Access
Własny pasek postępu w formularzu
Najprostszy pasek postępu, to pojedyncza kolorowa etykieta, której szerokość zmienia się proporcjonalnie do zaawansowania operacji. Przykładowo wykonanie 1% zadania powoduje zwiększenie szerokości 0,01*Maksymalna_szerokość_etykiety. Po każdej zmianie szerokości etykiety możemy również zmieniać tekst etykiety na: „Wykonano x% operacji”. lub „Wykonano x z zaplanowanych Y operacji. W takim przypadku etykietą musimy podzielić na część opisową, (o stałej szerokości) i zwiększać rozmiar etykiety proporcjonalnie do zaawansowania wykonania operacji.
Na stronie Modalny pasek postępu dość dokładnie opisałem paski postępu i przedstawiłem przykładową klasę clsProgressBar dzięki której możemy sterować paskiem postępu utworzonym za pomocą modalnego formularza. Formularz ten był ustawiany nad formularzem wywołującym, w sposób stwarzający wrażenie, że jest integralną częścią formularza z poziomu którego został wywołany.
Jeżeli pasek postępu wykorzystywany będzie w kilku formularzach naszej bazy, to aby nie powielać kodu i mieć większą kontrolę nad kodem, utworzy moduł klasy, za pomocą którego będziemy mogli sterować podstawowymi właściwościami i metodami formularza spełniającego rolę paska postępu.
Projekt prostego pasek postępu osadzonego w formularzu wywołującym.
Pasek postępu tworzyć będą 3 formanty typu Label, etykieta lblProgBack będąca tłem właściwego paska postępu lblProg. Poniżej znajduje się etykieta opisowa lblProgDscr. Ramkę wokół etykiet tworzy formant typu Rectangle o nazwie rctProgBorder. Formanty tworzące pasek postępu umieszczamy w dowolnej sekcji formularza. Moim zdaniem najbardziej oczywistą jest sekcja stopka formularza (acFooter)

Rozmieszczenie formantów tworzących prosty pasek postępu.
Umieszczamy formanty tworzące pasek postępu w sekcji Stopka formularza (acFooter) dopasowujemy położenie, rozmiar i wygląd poszczególnych formantów i możemy już uruchamiać pasek postępu. Dodatkowo możemy sekcję Stopka formularza ukryć, by na czas działania paska postępu ją odkryć wraz z formantami które się w niej znajdują.
Option Compare Database Option Explicit ' referencyjne zmienne do formularza ' i formantów tworzących pasek postępu Private m_frm As Access.Form Private m_rctProg As Access.Rectangle Private m_lblBack As Access.Label Private m_lblProg As Access.Label Private m_lblDscr As Access.Label Private m_lblSect As Access.Section ' tekst początkowy na pasku postępu Private m_sPrefix As String 'ilość przewidywanych operacji Private m_lMaxCount As Long ' poprawka na szerokość etykiety paska (m_lblProg), gdyż ' etykieta ta jest przesunięta w prawo w/m etykiety m_lblBack Private m_lCorrection As Long Private Sub Class_Initialize() ' End Sub Private Sub Class_Terminate() ' ukryj elementy paska postępu m_rctProg.Visible = False m_lblBack.Visible = False m_lblProg.Visible = False m_lblDscr.Visible = False ' zniszcz zmienne obiektowe Set m_lblDscr = Nothing Set m_lblProg = Nothing Set m_lblBack = Nothing Set m_rctProg = Nothing Set m_frm = Nothing End Sub ' przypisz do zmiennych odwołania do formantów tworzących pasek postępu Public Sub pgSetProgBar(frmPrg As Access.Form, _ lblProgBack As Access.Label, _ lblProgBar As Access.Label, _ lblProgDescr As Access.Label, _ rctProgBar As Access.Rectangle) ' utwórz odwołania do elementów paska Set m_frm = frmPrg Set m_rctProg = rctProgBar Set m_lblBack = lblProgBack Set m_lblProg = lblProgBar Set m_lblDscr = lblProgDescr ' poprawka na szerokości paska postępu bo jest przesunięty ' w/m etykiety lblProgBack w prawo m_lCorrection = (m_lblProg.Left - m_lblBack.Left) * 2 'pokaż elementy paska m_rctProg.Visible = True m_lblBack.Visible = True m_lblProg.Visible = True m_lblProg.Width = 0 m_lblDscr.Visible = True End Sub ' aktualizacja paska postępu w/m ilości wykonanych operacji Public Sub pgUpdateProgBar(ByVal lOperation As Long) Dim lPercent As Long Dim lWidthProgBar As Long ' oblicz % wykonania lPercent = (lOperation / m_lMaxCount) * 100 With m_frm ' ustaw teksty "przenikających się" etykiet m_lblProg.Caption = "Wykonano " & lPercent & "% zadania" m_lblBack.Caption = "Wykonano " & lPercent & "% zadania" ' oblicz szerokość paska postępu lWidthProgBar = m_lblBack.Width * (lPercent / 100) ' dla ostatnich operacji zmniejsz maksymalną szerokość paska postępu ' o 50 twipów (przekracza prawą krawędź etykiety pod spodem) If lWidthProgBar > m_lblBack.Width - m_lCorrection Then m_lblProg.Width = m_lblBack.Width - m_lCorrection Else m_lblProg.Width = lWidthProgBar End If ' ustaw tekst paska postępu m_lblDscr.Caption = prpPrefix & _ Format$(CStr(lOperation), "### ### ### ###") & _ " z " & Format$(m_lMaxCount, "### ### ### ###") ' czasami występuje nieprzyjemne migotanie paska postępu. ' Możemy na czas przemalowania formularza zablokować odświeżanie ' okien kosztem nieco dłuższego czasu działania paska postępu 'On Error Resume Next 'Application.Echo False DoEvents 'Application.Echo True 'On Error GoTo 0 End With End Sub ' maksymalna ilość operacji Public Property Let prpMaxCount(prpMaxCount As Long) If prpMaxCount = 0 Then prpMaxCount = 1 m_lMaxCount = Abs(prpMaxCount) End Property Public Property Get prpMaxCount() As Long prpMaxCount = m_lMaxCount End Property ' tekst początkowy na pasku postępu Public Property Let prpPrefix(sPrefix As String) End Property Public Property Get prpPrefix() As String prpPrefix = m_sPrefix End Property
Przykładowe wywołanie prostego paska postępu osadzonego w sekcji Stopka formularza
W tym konkretnym przypadku ilość wykonywanych operacji (powtórzeń pętli) wynosi 1 000 000. Pasek postępu aktualizowany jest co 1% (cPercentUpdate As Single = 0.01) liczby operacji tj. po każdych 10 000 wykonanych operacjach.
Option Compare Database Option Explicit #If VBA7 Then Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) #Else Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) #End If Private Sub Form_Load() Me.Section(acFooter).Visible = False End Sub ' uruchom pasek postępu Private Sub btnProgBar_Click() Dim cls As clsProgressSimple ' referencyjna zmienna obiektowa do klasy Dim lModUpdate As Long ' numer operacji aktualizowania paska postępu Dim i As Long ' licznik Const cRepeat As Long = 100 ' ilość powtórzeń Const cPercentUpdate As Single = 0.01 ' częstotliwość odświeżania paska Const cSleep As Long = 20 ' wydłużenie czasu działania pętli w milisekundach ' pokaż sekcję Stopka formularza Me.Section(acFooter).Visible = True Set cls = New clsProgressSimple With cls ' zainicjuj pasek postępu (formanty znajdują się w bieżącym formularzu) .pgSetProgBar Me, Me.lblProgBack, Me.lblProg, Me.lblProgDscr, Me.rctProgBorder ' ustaw tekst początkowy na pasku postępu .prpPrefix = "Operacja:" ' ustaw ilość powtórzeń pętli .prpMaxCount = cRepeat ' oblicz co ile wykonanych operacji ma być aktualizowany pasek postępu lModUpdate = CLng(cRepeat * cPercentUpdate) ' wykonaj prostą pętlę cRepeat razy For i = 0 To cRepeat ' tutaj wykonuj swoje instrukcje ! ' ... ' wydłuż czas działania pętli Call Sleep(cSleep) ' aktualizuj co lModUpdate wywołań If i Mod lModUpdate = 0 Then .pgUpdateProgBar i End If Next End With Set cls = Nothing ' ukryj sekcję Stopka formularza Me.Section(acFooter).Visible = False End Sub

Wywołanie prostego paska postępu.
			Moim skromnym zdaniem ten „prosty pasek postępu” spełnia podstawowe zadanie jakiego
			oczekuje się od paska postępu. Jest prosty, w trakcie projektowania ustawia się jego podstawowe właściwości
			takie jak: położenie, rozmiar, styl obramowania, wielkość i kolor czcionki. Właściwości te można także zmienić za pomocą VBA.
			Pasek jest po prostu„prosty” ☺
		
Zaawansowany pasek postępu osadzony w formularzu
Życie można sobie ułatwić, bądź skomplikować. By nie dopracowywać co do piksela, a raczej twipa, położenia elementów paska postępu, możemy w dowolnym miejscu wybranej sekcji formularza wstawić elementy paska,

Projekt zaawansowanego paska postępu.
a resztą zajmie się klasa clsProgressBarInFrm, która ustawi domyślne położenie i rozmiar elementów paska. Można także sterować wymiarami paska i jego położeniem przekazując w metodzie pgMoveSize(...) wszystkie potrzebne dane dotyczące rozmiaru i położenia.
Public Sub pgMoveSize( _
              Optional ByVal lLeft As Long = -1, _
              Optional ByVal lTop As Long = -1, _
              Optional ByVal lWidth As Long = -1, _
              Optional ByVal lHeight As Long = -1)
		Poniżej kilka słów o niedostatkach projekty zaawansowanego paska postępu. Dlatego też kod jest ukryty, ale nie zabraniam go oglądać ☺. Wystarczy kliknąć w odsyłacz.
Minimalna wysokość sekcji w której znajdują się formanty tworzące pasek postępu powinna być większa od 700 twipów tj. ok. 1,25cm. Dla mniejszych wysokości sekcji zmniejszona będzie wysokość paska postępu i wielkość czcionki. Ale niestety zmniejszy się czytelność tekstu na pasku postępu. W skrajnym przypadku może pojawić się pionowy pasek przewijania formularza. Dodatkowo należy zapamiętać w zmiennych klasy niektóre początkowe właściwości formantów by je przywrócić przed następnym wywołaniem paska postępu. No i na dodatek cała masa empirycznych (na tzw. „oko”) poprawek na położenie i wymiary elementów paska.
Klasa clsProgressBarInFrm i przykładowe wywołanie zaawansowanego paska postępu.
Option Compare Database
Option Explicit
' referencyjne zmienne do formularza
' i formantów tworzących pasek postępu
Private m_frm               As Access.Form
Private m_rctProg           As Access.Rectangle
Private m_lblBack           As Access.Label
Private m_lblProg           As Access.Label
Private m_lblDscr           As Access.Label
Private m_lblSect           As Access.Section
' wysokość sekcji z paskiem postępu
Private m_lLblSectHeight    As Long
' identyfikator sekcji z paskiem  postępu
Private m_lInSection        As Long
' rozmiar fontu w etykietach paska
Private m_iLblBackFontSize  As Integer
Private m_iLblProgFontSize  As Integer
Private m_iLblDscrFontSize  As Integer
' tekst początkowy na pasku postępu
Private m_sPrefix           As String
'ilość przewidywanych operacji
Private m_lMaxCount         As Long
Private Sub Class_Terminate()
	' ukryj elementy paska postępu
	m_rctProg.Visible = False
	m_lblBack.Visible = False
	m_lblProg.Visible = False
	m_lblDscr.Visible = False
	' zmniejsz i przesuń do góry elementy paska
	m_rctProg.Height = 0: m_rctProg.Top = 0
	m_lblBack.Height = 0: m_lblBack.Top = 0
	m_lblProg.Height = 0: m_lblProg.Top = 0
	m_lblDscr.Height = 0: m_lblDscr.Top = 0
	' przywróć rozmiar sekcji paska postępu
	m_frm.Section(m_lInSection).Height = m_lLblSectHeight
	' przywróć rozmiar czcionki etykiet paska
	m_lblBack.FontSize = m_iLblBackFontSize
	m_lblProg.FontSize = m_iLblProgFontSize
	m_lblDscr.FontSize = m_iLblDscrFontSize
	' zniszcz zmienne obiektowe
	Set m_lblDscr = Nothing
	Set m_lblProg = Nothing
	Set m_lblBack = Nothing
	Set m_rctProg = Nothing
	Set m_frm = Nothing
End Sub
' przypisz do zmiennych odwołania do formantów tworzących pasek postępu
Public Sub pgSetProgBar(frmPrg As Access.Form, _
                            lblProgBack As Access.Label, _
                            lblProgBar As Access.Label, _
                            lblProgDescr As Access.Label, _
                            rctProgBar As Access.Rectangle)
	' utwórz odwołania do elementów paska
	Set m_frm = frmPrg
	Set m_rctProg = rctProgBar
	Set m_lblBack = lblProgBack
	Set m_lblProg = lblProgBar
	Set m_lblDscr = lblProgDescr
	' pobierz identyfikator sekcji i jej wysokość
	m_lInSection = m_frm.Controls(m_lblProg.Name).Section
	Set m_lblSect = m_frm.Section(m_lInSection)
	m_lLblSectHeight = m_lblSect.Height
	'pokaż elementy paska
	m_rctProg.Visible = True
	m_lblBack.Visible = True
	m_lblProg.Visible = True
	m_lblProg.Width = 0
	m_lblDscr.Visible = True
	' pobierz rozmiary czcionek etykiet paska postępu
	m_iLblBackFontSize = m_lblBack.FontSize
	m_iLblProgFontSize = m_lblProg.FontSize
	m_iLblDscrFontSize = m_lblDscr.FontSize
End Sub
' przesuwa i zmienia rozmiar formantów tworzących pasek postępu
Public Sub pgMoveSize( _
              Optional ByVal lLeft As Long = -1, _
              Optional ByVal lTop As Long = -1, _
              Optional ByVal lWidth As Long = -1, _
              Optional ByVal lHeight As Long = -1)
Dim lBottom    As Long
Dim lLblHeight As Long
Dim fDefHeight As Boolean
	' ustaw pasek postępu przy lewej krawędzi sekcji
	If lLeft <= -1 Then lLeft = 75
	' ustaw domyślną wysokość paska postępu
	If lHeight <= -1 Then
		fDefHeight = True
		lHeight = 620
	End If
	' jeżeli sekcja jest za niska zmniejsz wysokość paska postępu i czcionkę
	With m_frm.Section(m_lInSection)
		' sprawdź tylko dla domyślnej wysokości
		If fDefHeight = True Then
			If .Height < (lHeight + 75) Then
				m_lblBack.FontSize = (m_iLblBackFontSize * .Height / lHeight) - 1
				m_lblProg.FontSize = (m_iLblProgFontSize * .Height / lHeight) - 1
				m_lblDscr.FontSize = (m_iLblDscrFontSize * .Height / lHeight) - 1
				lHeight = .Height - 175
			End If
		End If
	End With
	' ustaw domyślną szerokość paska postępu
	If lWidth <= -1 Then lWidth = m_frm.InsideWidth - 125
	' empiryczna poprawka na szerokość selektora rekordów (gdy jest widoczny)
	If m_frm.RecordSelectors = True Then
		lWidth = lWidth - 300
	End If
	' ustaw domyślne położenie paska postępu w pionie
	If lTop = -1 Then
		If m_lInSection = acDetail Then
			' ustaw pasek postępu na dole sekcji Szczegóły
			lBottom = m_frm.InsideHeight
			On Error Resume Next
				' pułapkuj błędy, sumuj wysokości sekcji
				lBottom = lBottom - m_frm.Section(acHeader).Height
				lBottom = lBottom - m_frm.Section(acFooter).Height
			On Error GoTo 0
			lTop = lBottom - lHeight - 100
			If lTop < 0 Then lTop = 100
		Else
			' pasek postępu nie znajduje się w sekcji Szczegóły
			' ustaw pasek 50 twipów poniżej górnej krawędzi sekcji
			lTop = 50
		End If
	End If
	' oblicz wysokość pojedynczego paska
	lLblHeight = (lHeight - 3 * 50) \ 2
	' Dla bardzo małych wysokości może być mniejsze od zera
	If lLblHeight - 35 < 0 Then lLblHeight = 100
	' ramka paska postępu
	m_rctProg.Left = lLeft
	m_rctProg.Top = lTop
	m_rctProg.Width = lWidth
	m_rctProg.Height = lHeight
	' etykieta tła
	m_lblBack.Left = lLeft + 50
	m_lblBack.Top = lTop + 50
	m_lblBack.Width = lWidth - 100
	m_lblBack.Height = lLblHeight
	' pasek postępu
	m_lblProg.Left = lLeft + 55
	m_lblProg.Top = lTop + 63
	m_lblProg.Width = 0
	m_lblProg.Height = lLblHeight - 35
	' etykieta opisowa
	m_lblDscr.Left = lLeft + 50
	m_lblDscr.Top = m_lblBack.Top + m_lblBack.Height + 50
	m_lblDscr.Width = lWidth - 100
	m_lblDscr.Height = lLblHeight
End Sub
' aktualizacja paska postępu w/m ilości wykonanych operacji
Public Sub pgUpdateProgBar(ByVal lOperation As Long)
Dim lPercent        As Long
Dim lWidthProgBar   As Long
	' oblicz % wykonania
	lPercent = (lOperation / m_lMaxCount) * 100
		
	With m_frm
		' ustaw teksty "przenikających się" etykiet
		m_lblProg.Caption = "Wykonano  " & lPercent & "%  zadania"
		m_lblBack.Caption = "Wykonano  " & lPercent & "%  zadania"
		
		' oblicz szerokość paska postępu
		lWidthProgBar = m_lblBack.Width * (lPercent / 100)
		
		' dla ostatnich operacji zmniejsz maksymalną szerokość
		' paska postępu o 60 twipów (jest za długi)
		If lWidthProgBar > m_lblBack.Width - 60 Then
			m_lblProg.Width = m_lblBack.Width - 60
		Else
			m_lblProg.Width = lWidthProgBar
		End If
		' ustaw tekst paska postępu
		m_lblDscr.Caption = prpPrefix & _
												Format$(CStr(lOperation), "### ### ### ###") & _
												"  z " & Format$(m_lMaxCount, "### ### ### ###")
		' czasami występuje nieprzyjemne migotanie paska postępu.
		' Możemy na czas przemalowania formularza zablokować odświeżanie
		' okien kosztem nieco dłuższego czasu działania paska postępu
		'On Error Resume Next
			'Application.Echo False
				DoEvents
			'Application.Echo True
		'On Error GoTo 0
	End With
End Sub
' maksymalna ilość operacji
Public Property Let prpMaxCount(prpMaxCount As Long)
		If prpMaxCount = 0 Then prpMaxCount = 1
		m_lMaxCount = Abs(prpMaxCount)
End Property
Public Property Get prpMaxCount() As Long
		prpMaxCount = m_lMaxCount
End Property
' tekst początkowy na pasku postępu
Public Property Let prpPrefix(sPrefix As String)
		m_sPrefix = sPrefix
End Property
Public Property Get prpPrefix() As String
		prpPrefix = m_sPrefix
End Property
Option Compare Database Option Explicit #If VBA7 Then Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) #Else Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) #End If Private Sub Form_Load() Me.Section(acFooter).Visible = False End Sub ' uruchom pasek postępu Private Sub btnProgBar_Click() Dim cls As clsProgressBarInFrm ' referencyjna zmienna obiektowa do klasy Dim lModUpdate As Long ' numer operacji aktualizowania paska postępu Dim i As Long ' licznik Const cRepeat As Long = 100 ' ilość powtórzeń Const cPercentUpdate As Single = 0.01 ' częstotliwość odświeżania paska Const cSleep As Long = 20 ' wydłużenie czasu działania pętli w milisekundach Me.Section(acFooter).Visible = True Set cls = New clsProgressBarInFrm With cls ' zainicjuj pasek postępu (formanty znajdują się w bieżącym formularzu) .pgSetProgBar Me, Me.lblProgBack, Me.lblProg, Me.lblProgDscr, Me.rctProgBorder ' zainicjuj domyślne ustawienia paska postępu '.pgMoveSize '---------------------------------------------------------------------------------------- ' można też ustalić swoje wymiary paska: wycentruj pasek ' w poziomie i przesuń pasek postępu pod formularz .pgMoveSize Me.InsideWidth / 4, 75, Me.InsideWidth / 2, Me.Section(acFooter).Height - 120 '---------------------------------------------------------------------------------------- ' ustaw tekst początkowy na pasku postępu .prpPrefix = "Operacja:" ' ustaw ilość powtórzeń pętli .prpMaxCount = cRepeat ' oblicz co ile wykonanych operacji ma być aktualizowany pasek postępu lModUpdate = CLng(cRepeat * cPercentUpdate) ' wykonaj prostą pętlę cRepeat razy For i = 0 To cRepeat ' tutaj wykonuj swoje instrukcje ! ' ... ' wydłuż czas działania pętli Call Sleep(cSleep) ' aktualizuj co lModUpdate wywołań If i Mod lModUpdate = 0 Then .pgUpdateProgBar i End If Next End With Set cls = Nothing Me.Section(acFooter).Visible = False End Sub
 
		.pgMoveSize Me.InsideWidth / 4, 75, Me.InsideWidth / 2, _
Me.Section(acFooter).Height - 120
 
	
 Do pobrania:
		•
		   Do pobrania:
		•