W tej lekcji nauczymy się wywoływać procedury. Poznamy także sposoby zatrzymywania makra w trakcie jego działania.
Wywoływanie procedur
Jak dowiedzieliśmy się w
drugiej lekcji, cały kod wykonujący zadania musi być umieszczony w procedurach (Sub
) lub funkcjach
(Function
). W tej samej lekcji wspomniano też, że każde zadanie powinno znaleźć się w oddzielnej
procedurze. Nauczyliśmy się również kilku sposobów ręcznego uruchamiania makr.
Wyobraźmy więc teraz sobie, że chcemy napisać makro wykonujące dziesięć różnych czynności.
Zgodnie ze wspomnianą wyżej zasadą oddzielania od siebie zadań, trzeba w tym celu stworzyć dziesięć różnych procedur.
Sam przyznasz, że ręczne uruchamianie każdej z tych dziesięciu procedur nie byłoby zbyt wygodne - musielibyśmy uruchomić
pierwszą procedurę, zaczekać aż skończy się wykonywać, następnie uruchomić drugą, zaczekać aż skończy się wykonywać itd.
Na szczęście Visual Basic umożliwia automatyczne uruchamianie potrzebnych procedur, jedna po drugiej.
Aby pokazać jak działa automatyczne uruchamianie procedur wykorzystamy oklepany już nieco przykład wyświetlania w arkuszu
kolejnych potęg dwójki i trójki. Dla utrudnienia załóżmy, że makro ma ponadto wyświetlać kolejne potęgi czwórki i piątki.
Aby być wiernym zasadzie rozdzielania od siebie poszczególnych zadań, należy stworzyć oddzielną procedurę dla każdej
liczby wyjściowej, łącznie trzeba więc napisać cztery różne procedury:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Sub potegiDwojki()
Dim potega As Long
potega = 1
Worksheets("Arkusz1").Cells(1, 1) = potega
potega = potega * 2
Worksheets("Arkusz1").Cells(2, 1) = potega
potega = potega * 2
Worksheets("Arkusz1").Cells(3, 1) = potega
potega = potega * 2
Worksheets("Arkusz1").Cells(4, 1) = potega
potega = potega * 2
Worksheets("Arkusz1").Cells(5, 1) = potega
End Sub
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Sub potegiTrojki()
Dim potega As Long
potega = 1
Worksheets("Arkusz1").Cells(1, 2) = potega
potega = potega * 3
Worksheets("Arkusz1").Cells(2, 2) = potega
potega = potega * 3
Worksheets("Arkusz1").Cells(3, 2) = potega
potega = potega * 3
Worksheets("Arkusz1").Cells(4, 2) = potega
potega = potega * 3
Worksheets("Arkusz1").Cells(5, 2) = potega
End Sub
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Sub potegiCzworki()
Dim potega As Long
potega = 1
Worksheets("Arkusz1").Cells(1, 3) = potega
potega = potega * 4
Worksheets("Arkusz1").Cells(2, 3) = potega
potega = potega * 4
Worksheets("Arkusz1").Cells(3, 3) = potega
potega = potega * 4
Worksheets("Arkusz1").Cells(4, 3) = potega
potega = potega * 4
Worksheets("Arkusz1").Cells(5, 3) = potega
End Sub
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Sub potegiPiatki()
Dim potega As Long
potega = 1
Worksheets("Arkusz1").Cells(1, 4) = potega
potega = potega * 5
Worksheets("Arkusz1").Cells(2, 4) = potega
potega = potega * 5
Worksheets("Arkusz1").Cells(3, 4) = potega
potega = potega * 5
Worksheets("Arkusz1").Cells(4, 4) = potega
potega = potega * 5
Worksheets("Arkusz1").Cells(5, 4) = potega
End Sub
Zwróć uwagę, że zmienna potega
w każdej procedurze jest deklarowana na nowo.
Dzieje się tak, ponieważ zmienna zadeklarowana za pomocą słowa kluczowego Dim
działa tylko
wewnątrz procedury, w której jest zadeklarowana. W późniejszej części kursu poznasz inne sposoby
deklaracji zmiennych, dzięki którym zmienne są widoczne w wielu procedurach równocześnie.
Gdybyś chciał teraz ręcznie uruchomić wszystkie te procedury, zajęłoby to trochę czasu - musiałbyś po kolei
ustawiać kursor w każdej z nich, wciskać klawisz F5 (albo uruchamiać je innym
sposobem omówionym w części kursu poświęconej uruchamianiu makr), czekać aż zakończy ona swoje działanie, przejść do
następnej, itd. Krótko mówiąc - niepotrzebne marnowanie czasu i energii.
Poniżej znajduje się dodatkowa procedura, której jedynym zadaniem jest kolejne wywoływanie każdej z czterech
stworzonych przed chwilą procedur, dzięki czemu proces ich uruchamiania zostaje skrócony do uruchomienia
jednego makra.
1
2
3
4
5
6
Sub wywolajWszystkieProcedury()
Call potegiDwojki
Call potegiTrojki
Call potegiCzworki
Call potegiPiatki
End Sub
Jak łatwo się domyślić patrząc na powyższy kod, wywołanie jakiejś procedury z wnętrza innej procedury odbywa
się poprzez napisanie słowa kluczowego Call
oraz nazwy wywoływanej procedury.
Przy wywoływaniu procedur można pominąć słowo kluczowe Call
i napisać samą nazwę procedury.
Jednak dla jasności kodu, warto każdorazowo stosować polecenie Call
i tak też będzie to
praktykowane podczas niniejszego kursu.
Oprócz czytelności kodu, inną zaletą wynikającą ze stosowania słowa kluczowego Call
jest możliwość
bardzo łatwego wyszukiwania wszystkich wywołań danej procedury.
Wywoływanie procedur z argumentami
Jeżeli przyjrzysz się omówionym powyżej procedurom wyświetlającym potęgi poszczególnych liczb, zauważysz,
że wszystkie są niemal identyczne i różnią się tylko liczbą podnoszoną do potęgi oraz numerem kolumny,
w której wyświetlane są wyniki.
Sprawia to, że wielokrotnie powtarzane są te same fragment kodu, czego kategorycznie powinno się unikać
poprzez tworzenie procedur uogólniających.
Poniżej zostało wypisanych kilka fragmentów kodu, z powyższego przykładu, które powtarzają się w każdej
z czterech procedur:
Worksheets("Arkusz1").Cells(2, _) = potega
W takiej sytuacji doskonałym wyjściem byłoby napisanie jakiejś ogólnej procedury, w której zmianie ulegałaby
tylko liczba podnoszona do potęgi i numer kolumny z danymi, a cała reszta byłaby wspólna.
Taka ogólna procedura dla wyświetlania potęg została przedstawiona w poniższej ramce:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Sub wyswietlajPotegi(liczba As Byte, numerKolumny As Byte)
Dim potega As Long
potega = 1
Worksheets("Arkusz1").Cells(1, numerKolumny) = potega
potega = potega * liczba
Worksheets("Arkusz1").Cells(2, numerKolumny) = potega
potega = potega * liczba
Worksheets("Arkusz1").Cells(3, numerKolumny) = potega
potega = potega * liczba
Worksheets("Arkusz1").Cells(4, numerKolumny) = potega
potega = potega * liczba
Worksheets("Arkusz1").Cells(5, numerKolumny) = potega
End Sub
Wykasuj więc teraz z edytora VBA wszystkie cztery procedury, które wcześniej stworzyłeś i zastąp je powyższą procedurą wyswietlajPotegi
. Procedury z poprzedniego przykładu były użyte tylko po to, aby pokazać ideę wywoływania procedur. Zasadniczo w takich sytuacjach należy tworzyć właśnie takie ogólne procedury, jak prezentowana tutaj procedura wyswietlajPotegi
.
Spróbuj teraz uruchomić procedurę wywolajWszystkieProcedury
, która uruchamia pozostałe procedury.
Oczywiście procedura ta nie zadziała! A to dlatego, że procedury, które wywoływała, zostały już usunięte.
Jeżeli makro będzie próbowało wywołać procedurę, która nie istnieje, edytor wyświetli błąd
Compile error: Sub or Function not defined.
Przed uruchomieniem makra w nowej postaci, procedura wywolajWszystkieProcedury
musi jeszcze zostać dostosowana do nowych
okoliczności. Jej nowa postać będzie wyglądała tak:
1
2
3
4
5
6
Sub wywolajWszystkieProcedury()
Call wyswietlajPotegi(2, 1)
Call wyswietlajPotegi(3, 2)
Call wyswietlajPotegi(4, 3)
Call wyswietlajPotegi(5, 4)
End Sub
Teraz nie powinno już być żadnych problemów i po uruchomieniu makro wyświetli w arkuszu
Arkusz1oczekiwane wyniki.
Zwróć uwagę, jak bardzo udało się skrócić cały kod, dzięki zastąpieniu każdej z poszczególnych procedur jedną
procedurą uogólniającą.
W zmodyfikowanej wersji procedury wyswietlajPotegi
pojawiły się nowe elementy języka VBA, które należy
wyjaśnić.
W pierwszym wierszu procedury musi być ona oczywiście otwarta słowem kluczowym Sub
.
Sub wyswietlajPotegi(liczba As Byte, numerKolumny As Byte)
Tę część znasz już bardzo dobrze, bo pojawiła się już wielkrotnie podczas tego kursu.
Jednak oprócz tradycyjnego otwarcia procedury pojawia się w niej jeszcze dodatkowy element -
argumenty wejściowe, które zostały podane w nawiasie po nazwie procedury.
Wiersz otwarcia procedury z argumentami wejściowymi ma następującą postać ogólną:
Sub nazwaProcedury(arg1 As typ, arg2 As typ..., argN As typ)
W opisywanym przykładzie zostało określone, że procedura wyswietlajPotegi
posiada dwa
argumenty wejściowe:
-
argument
liczba
typu Byte
(a więc liczba z zakresu 0-255),
który będzie określał jaka liczba jest podnoszona do potęgi,
-
argument
numerKolumny
typu Byte
(a więc liczba z zakresu 0-255),
który będzie określał w której kolumnie arkusza wyświetlone zostają wyniki,
a więc od teraz przy każdym wywołaniu tej procedury, po słowie kluczowym Call
i jej nazwie, w nawiasie
będą musiały zostać również podane wartości tych dwóch argumentów.
Zadeklarowanie argumentów wejściowych oznacza, że aby wywołać potem daną procedurę
konieczne będzie podanie tych argumentów.
Jeżeli będziesz próbował wywołać procedurę wymagającą argumentów, bez podania tych argumentów,
edytor wyświetli błąd Compile error: Argument not optional
.
Podanie zmiennej jako argumentu w linii otwarcia procedury jest równoznaczne z jej normalnym zadeklarowaniem w danej
procedurze
(więcej informacji na temat deklarowania zmiennych). Oznacza to, że w tej procedurze nie może już zostać zadeklarowana zmienna o takiej samej nazwie.
W drugim wierszu procedury wyswietlajPotegi
zostaje zadeklarowana zmienna potega
typu
Long
. W tym momencie w procedurze są więc już zadeklarowane trzy zmienne: potega
oraz
dwie zmienne, które pojawiły się w wierszu otwarcia - liczba
i numerKolumny
.
W wierszu 4 do zmiennej potega
zostaje przypisana początkowa wartość 1.
W kolejnej linijce jest ona wyświetlana w arkuszu Arkusz1, w pierwszym wierszu tego arkusza i kolumnie
o takim numerze, jaki jest podany jako argument wejściowy numerKolumny
.
W wierszu 7 wartość zmiennej potega
zostaje pomnożona przez wartość argumentu
liczba
, który jest podawany przy wywoływaniu procedury.
W dalszej części tej procedury wszystko odbywa się według scenariusza znanego już z poprzednich przykładów.
Zmianie uległa również procedura wywolajWszystkieProcedury
.
Zamiast wywoływać cztery różne procedury, tak jak miało to miejsce wcześniej, wywołuje ona teraz czterokrotnie
tę samą procedurę wyswietlajPotegi
, zmieniając tylko wartości podawanych do nich argumentów.
I tak na przykład w wierszu 2, procedura wyswietlajPotegi
wywoływana jest z
argumentami wejściowymi 2 oraz 1:
2
Call wyswietlajPotegi(2,1)
co oznacza, że liczbą, która będzie podnoszona do potęgi będzie 2, a wyniki będą wypisywane w pierwszej
kolumnie arkusza Arkusz1.
Analogicznie w trzecim wierszu wywoływana jest procedura wyswietlajPotegi
, która podnosi do potęgi
liczbę 3 i wyświetla wyniki w drugiej kolumnie, itd.
Mimo, że w procedurze wyswietlajPotegi
argumenty wejściowe nie zmieniają się w trakcie wykonywania
tej procedury, sytuacja taka byłaby całkowicie poprawna.
Zmodyfikuj teraz procedurę wyswietlajPotegi
, aby wyglądała tak jak poniżej (nowe wiersze zostały
podświetlone na czerwono):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Sub wyswietlajPotegi(liczba As Byte, numerKolumny As Byte)
Dim potega As Long
liczba = 2
potega = 1
Worksheets("Arkusz1").Cells(1, numerKolumny) = potega
potega = potega * liczba
Worksheets("Arkusz1").Cells(2, numerKolumny) = potega
potega = potega * liczba
Worksheets("Arkusz1").Cells(3, numerKolumny) = potega
potega = potega * liczba
Worksheets("Arkusz1").Cells(4, numerKolumny) = potega
potega = potega * liczba
Worksheets("Arkusz1").Cells(5, numerKolumny) = potega
End Sub
Jak widzisz, w wierszu 4 do argumentu liczba
jest przypisywana wartość 2.
W takiej sytuacji, niezależnie od tego, jaka wartość argumentu liczba
zostanie podana przy wywoływaniu
tej procedury, zawsze będzie ona podnosić do potęgi liczbę 2.
Na koniec poruszymy jeszcze jedną ważną kwestię, związaną z wywoływaniem procedur z argumentami.
Wklej do edytora poniższy kod:
1
2
3
4
5
6
7
8
Sub zarobki()
Dim pensja As Long
pensja = 2500
Call odliczeniePodatku(pensja)
Cells(1, 1) = pensja 'To jest Twoja wypłata netto
End Sub
1
2
3
4
5
Sub odliczeniePodatku(podstawa As Long)
podstawa = podstawa - (podstawa * 0.18)
'tu mogą dziać się jeszcze jakieś inne rzeczy
'na przykład wpłacenie podatku do urzędu skarbowego
End Sub
Do przedstawienia tego zagadnienia celowo wybrałem tak życiowy przykład, który każdy odczuwa co miesiąc na
własnej skórze, aby dobrze przemówił do Twojej wyobraźni.
Procedura zarobki
jest główną procedurą, natomiast odliczeniePodatku
, jest wywoływane
z poziomu procedury zarobki
w trakcie jej wykonywania.
Co się stanie po uruchomieniu tego kodu? Poniżej znajduje się szczegółowa analiza wydarzeń.
W drugim wierszu procedury zarobki
zadeklarowano zmienną liczbową pensja
, a po chwili
zostaje do niej przypisana wartość 2500.
W wierszu 5 wywoływana jest procedura odliczeniePodatku
, która wymaga podania
argumentu liczbowego podstawa
(aby odliczyć podatek trzeba przecież znać wysokość pensji brutto). Jako
ten argument podawana jest oczywiście zmienna pensja
, która cały czas ma jeszcze wartość 2500.
W momencie wywołania procedury odliczeniePodatku
wykonywanie całego makra przenosi się do tej właśnie
procedury i dopiero po jej zakończeniu powróci z powrotem do procedury zarobki
, do miejsca, w którym
ją opuściło.
W tym momencie kompilator znajduje się więc w procedurze odliczeniePodatku
, w którym zadeklarowana jest
jedna zmienna podstawa
(nie była ona zadeklarowana za pomocą słowa kluczowego Dim
, ale
występuje jako argument w wierszu startowym procedury, co jak wspomniano kilka
akapitów wcześniej jest równoznaczne z jej zadeklarowaniem).
Pamiętaj, że w momencie wywołania procedury, jako argument podstawa
nie została podana liczba 2500, ale
zmienna pensja
, która miała wartość 2500. O tym, że jest to zasadnicza różnica możesz się przekonać w
wierszu 2 procedury odliczaniePodatku
. Wtedy to od argumentu podstawa
zostaje odjęte 18% jej wartości, a więc przyjmie on teraz wartość 2050.
Fakt, że jako argument podstawa
podana została zmienna pensja
, sprawia, że filozoficznie
mówiąc, jest to jeden i ten sam byt.
A więc jeżeli wartość argumentu podstawa
zmieniła się na 2050, to w tym samym momencie wartość zmiennej
pensja
znajdującej się w procedurze zarobki
również zmieniła się na 2050, ponieważ argument
podstawa
i zmienna pensja
to w rzeczywistości jedno i to samo.
Gdyby tak nie było, po odliczeniu podatku od podstawy, którą jest pensja, pensja pozostawałaby na niezmienionym poziomie.
Niestety, jak widzisz, takie piękne rzeczy nie zdarzają się nawet w programowaniu.
Następnie w procedurze odliczeniePodatku
mogą dziać się jeszcze jakieś operacje, które zostały tylko
opisane komentarzem w wierszach 3-4, ponieważ nie są istotne dla omawianego tu zagadnienia.
Istotne jest natomiast to, co dzieje się po powrocie makra do wykonywania procedury zarobki
. Makro wraca
do niej, po zakończeniu procedury odliczeniePodatku
i rozpoczyna dalsze działanie od następnego wiersza
(czyli w tym przypadku od wiersza 7).
W procedurze zarobki
zmienna pensja
nie ma już wartości 2500, ale 2050 (bo taka wartość została
jej nadana wewnątrz procedury odliczaniePodatku
, do której ta zmienna została przekazana).
W wierszu 6 wartość zmiennej pensja
zostaje wyprintowana do arkuszaArkusz1,
tak, abyś po uruchomieniu makra mógł się przekonać na własne oczy, że w komórce A1 rzeczywiście widnieje liczba 2050.
Podsumowując opisany powyżej przykład: jeżeli jakaś zmienna zostaje przekazana jako argument do wywoływanej procedury,
to po powrocie do funkcji bazowej może mieć już całkiem inną wartość.
W tym miejscu jeszcze raz trzeba poruszyć kwestię używania słowa kluczowego Call
przy wywoływaniu procedur.
Jak wspomniałem wcześniej, procedura może być wywołana poprzez napisanie w linijce samej nazwy, bez poprzedzania jej
słowem kluczowym Call
.
W sytuacji, gdy procedura nie posiada żadnych argumentów wejściowych, oba sposoby wywołania nie różnią się zbytnio od
siebie, a wiersz jej wywołania może wyglądać tak:
lub tak:
Jeżeli jednak wywoływana procedura wymaga podania jakichś argumentów, musisz pamiętać o jednej zasadzie -
pomijając słowo kluczowe Call
, nie umieszczasz argumentów w nawiasie, tylko wypisujesz po
nazwie procedury oddzielając przecinkami.
Poniżej znajduje się przykład poprawnego wywołania procedury z argumentami przy użyciu słowa kluczowego
Call
oraz bez tego słowa:
Call odliczaniePodatku(pensja, stawka)
odliczaniePodatku pensja, stawka
Pamiętaj jednak, że stosowanie słowa kluczowego Call
jest o wiele lepszym wyjściem, ponieważ to tylko 4 znaki,
a pomaga zachować porządek i przejrzystość w kodzie oraz ułatwia późniejsze wyszukiwanie wywołań danej procedury.
Wstrzymywanie działania makra
Pisząc dłuższe programy często będziesz miał potrzebę zatrzymać w którymś momencie ich działanie, żeby przekonać się
czy wszystkie polecenia do określonego momentu wykonują się poprawnie lub aby sprawdzić aktualną wartość zmiennych.
W tym celu możesz ustawiać tzw. punkty zatrzymania (toogle breakpoints). Jeżeli ustawisz w makrze punkty
zatrzymania, to po jego uruchomieniu zawsze zawiesza ono swoje działanie w miejscu, w którym ustawiony jest taki punkt.
Punkty zatrzymania oznaczone są w edytorze VBA poprzez podświetlenie całego wiersza kodu na brązowo oraz umieszczenie
brązowej kropki z lewej strony wiersza.
Ustawienie punktu zatrzymania jest bardzo proste i może być wykonane na kilka sposobów:
-
najszybszym sposobem jest ustawienie kursora w wierszu, w którym chcesz ustawić punkt zatrzymania i
kliknięcie klawisza F9. Sposób ten jest zdecydowanie najwygodniejszy, gdyż nie wymaga
odrywania rąk od klawiatury.
- drugim sposobem jest ustawienie się w wierszu, w którym ma być ustawiony punkt zatrzymania i kliknięcie ikony przedstawiającej białą dłoń na pasku narzędzi Edit (na poniższym rysunku otoczona czerwoną ramką).
- trzecim sposobem jest kliknięcie na szarym pasku z lewej strony edytora kodu (na poniższym rysunku otoczony czerwoną ramką), na wysokości wiersza, w którym ma zostać ustawiony punkt zatrzymania.
Jedną z ogromnych zalet zatrzymywania makra w trakcie jego działania jest możliwość sprawdzenia aktualnej wartości
zmiennych.
Na poniższym rysunku widzisz printscreena z wykonywania makra odliczającego podatek z poprzedniego przykładu. W makrze
tym punkt zatrzymania został ustawiony w szóstej linijce procedury zarobki, tuż przed wyprintowaniem wartości zmiennej
pensja
do arkusza.
W momencie zrzucania printscreena kursor był ustawiony na słowie pensja
w drugiej linijce kodu, dzięki czemu
nieco niżej pojawiła się żółta ramka przedstawiająca aktualną wartość tej zmiennej (niestety przy zrzucaniu
printscreena nie wiedzieć czemu znika kursor, dlatego nie widać go na powyższym obrazku).
Do zaprezentowania tej funkcjonalności specjalnie wybrałem fragment kodu z innej linijki niż punkt zatrzymania, aby pokazać
Ci, że po zatrzymaniu makra można podpatrywać nie tylko wartości z tego wiersza, ale też wszystkie inne.
Dodatkowym plusem jest fakt, że zadeklarowane zmienne to nie jedyne wartości, które możesz podejrzeć - równie dobrze,
możesz ustawić się kursorem na przykład na poleceniu Cells(1,1)
(które, jak już się zdążyłeś dowiedzieć w
poprzednich lekcjach, jest odniesieniem do komórki A1), aby zobaczyć jaka wartość znajduje się aktualnie w komórce
A1.
Zatrzymywanie makr jest więc bardzo przydatną funkcją, która w wielu sytuacjach ułatwi Ci pracę przy tworzeniu aplikacji.
Teraz, kiedy umiemy już zatrzymywać makro i podglądać wartości zmiennych, zupełnie niepotrzebny staje się na przykład
wiersz szósty omawianego wcześniej makra zarobki
:
Jego zadaniem było tylko wyświetlenie w arkuszu wartości zmiennej pensja
, tak aby móc potem sprawdzić
ile ona wynosiła. Ale po co to robić, skoro znasz już o wiele szybszy, wygodniejszy i łatwiejszy w użyciu sposób na
wykonanie tej samej czynności, czyli ustawienie punktu zatrzymania i najechanie kursorem na nazwę zmiennej.
Punktów zatrzymania nie można ustawiać w pustych wierszach oraz w wierszach, w których znajduje się tylko deklaracja
zmiennych.