11 - Select Case


Głównym tematem tej lekcji będzie instrukcja Select Case, która jest swego rodzaju odmianą instrukcji warunkowej, przeznaczoną do obsługi warunków z bardzo dużo ilością możliwych wariantów.

Oprócz tego, podczas tej lekcji poznamy wreszcie wielokrotnie już wspominane wyliczenia.

Instrukcja Select Case

Aby dobrze zrozumieć istotę instrukcji Select Case, powinieneś się najpierw zapoznać z przykładowym kodem, w którym instrukcja ta byłaby idealnym rozwiązaniem, jednak zamiast niej użyto tradycyjnej instrukcji warunkowej If ... Then.

Poniższa funkcja zwraca nazwę miesiąca w zależności od numeru miesiąca podanego jako argument numer (jak zapewne pamiętasz z lekcji o funkcjach wbudowanych, VBA posiada w swoim repertuarze funkcję wykonującą to zadanie, więc nie ma potrzeby pisania jej na nowo, jednak na potrzeby tej lekcji zostanie uczyniony wyjątek).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Function nazwaMiesiaca(numer As Integer) As String

    If numer = 1 Then
        nazwaMiesiaca = "styczeń"
    Else
        If numer = 2 Then
            nazwaMiesiaca = "luty"
        Else
            If numer = 3 Then
                nazwaMiesiaca = "marzec"
            Else
                If numer = 4 Then
                    nazwaMiesiaca = "kwiecień"
                Else
                    If numer = 5 Then
                        nazwaMiesiaca = "maj"
                    Else
                        If numer = 6 Then
                            nazwaMiesiaca = "czerwiec"
                        Else
                            If numer = 7 Then
                                nazwaMiesiaca = "lipiec"
                            Else
                                If numer = 8 Then
                                    nazwaMiesiaca = "sierpień"
                                Else
                                    '(...)
                                End If
                            End If
                        End If
                    End If
                End If
            End If
        End If
    End If
End Function

Funkcja składa się z 12 warunków, z których każdy kolejny zagnieżdżony jest w poprzednim. Funkcja sprawdza najpierw czy podany jako argument wejściowy numer miesiąca to 1. Jeżeli warunek ten jest spełniony, funkcja otrzymuje wartość styczeń i kończy swoje działanie. W przeciwnym razie wykonywanie kodu przekazywane jest do bloku Else tego warunku, gdzie zagnieżdżona jest kolejna instrukcja warunkowa, sprawdzająca z kolei czy podany numer miesiąca to 2, itd.

Sam widzisz, że zaprezentowana powyżej postać tej funkcji jest bardzo rozwlekła i czasochłonna. Ponadto przy zagnieżdżaniu tak dużej ilości instrukcji warunkowych bardzo łatwo o pomyłkę (a przecież miesięcy jest tylko 12, a mogą zdarzyć się funkcje, gdzie w ten sposób trzeba będzie opisać kilkadziesiąt jakichś obiektów).

Dodatkowo, jeżeli chcesz zachować przejrzystość kodu i przy każdej kolejnej instrukcji warunkowej stosować nowe wcięcie, to instrukcja warunkowa opisująca grudzień nie mieściłaby się już na ekranie, a zastosowanie w tej sytuacji przeniesienia tekstu do nowej linii za pomocą operatora  _ wprowadziłoby w kodzie jeszcze większy chaos.

Doskonałym sposobem na ominięcie opisanych niedogodności jest instrukcja Select Case.

Poniżej znajduje się jeszcze jedna wersja funkcji nazwaMiesiaca, wykorzystująca właśnie tę instrukcję. Dokładne wyjaśnienie schematu działania polecenia Select Case znajdziesz pod ramką z kodem.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Function nazwaMiesiaca(numer As Integer) As String

    Select Case numer
        Case 1
            nazwaMiesiaca = "styczeń"
        Case 2
            nazwaMiesiaca = "luty"
        Case 3
            nazwaMiesiaca = "marzec"
        Case 4
            nazwaMiesiaca = "kwiecień"
        Case 5
            nazwaMiesiaca = "maj"
        Case 6
            nazwaMiesiaca = "czerwiec"
        Case 7
            nazwaMiesiaca = "lipiec"
        Case 8
            nazwaMiesiaca = "sierpień"
        Case 9
            nazwaMiesiaca = "wrzesień"
        Case 10
            nazwaMiesiaca = "październik"
        Case 11
            nazwaMiesiaca = "listopad"
        Case 12
            nazwaMiesiaca = "grudzień"
    End Select
End Function

Zastosowanie polecenia Select Case sprawia, że kod jest o wiele bardziej przejrzysty, krótszy i łatwiejszy do modyfikowania.

Instrukcja Select Case jest specyficznym rodzajem instrukcji warunkowej. Różnica pomiędzy zwykłą instrukcją warunkową, a instrukcją Select Case polega na tym, że instrukcja If ... Then zawsze rozpatruje tylko jeden warunek, natomiast w instrukcji Select Caserozpatrywanych może być nieskończenie wiele warunków i do każdego z nich można przypisać operacje, jakie mają być wykonane w przypadku jego spełnienia.

Instrukcja Select Case rozpoczyna się od wiersza zawierającego słowo kluczowe Select Case oraz wyrażenie, którego wartość będzie przez tę instrukcję rozpatrywana. W opisywanym przykładzie rozpatrywany jest po prostu argument wejściowy numer, ale rolę tę, oprócz pojedynczej zmiennej, równie dobrze może pełnić funkcja lub działanie arytmetyczne.

We wnętrzu instrukcji Select Case znajdują się bloki opisujące operacje, jakie mają zostać wykonane dla poszczególnych wartości wyrażenia bazowego. Tak jak wcześniej wspomniano, każdy blok jest jakby oddzielnym warunkiem sprawdzanym przez instrukcję Select Case.

Blok rozpoczyna się od słowa kluczowego Case i wartości, dla której blok ten ma być wykonywany. Jako wartość przypisaną do danego bloku można użyć zmiennej, funkcji lub działania arytmetycznego, jednak w zdecydowanej większości przypadków stosowane są w tej sytuacji po prostu ręcznie wpisane wartości (tak jak w omawianym przykładzie).

Drugą częścią bloku Case są instrukcje, które mają być wykonane przez makro, jeżeli przypisany do niego warunek jest spełniony, czyli wyrażenie rozpatrywane przez instrukcję Select Case (w przykładzie jest to wartość zmiennej numer) jest równe wartości przypisanej do tego bloku (w przykładzie są to numery poszczególnych miesięcy umieszczone po słowach kluczowych Case ). W powyższym przykładzie dla każego bloku zdefiniowano tylko jedno polecenie - przypisanie do zmiennej nazwaMiesiaca nazw poszczególnych miesięcy. Nie oznacza to jednak, że każdy blok musi ograniczać się tylko do jednej operacji - w rzeczywistości nie ma w tym zakresie żadnych ograniczeń i każdy blok może zawierać dowolną liczbę operacji.

Każdy blok kończy się wraz z momentem rozpoczęcia następnego bloku, a więc po napotkaniu przez kod kolejnego słowa kluczowego Case (lub po natrafieniu na zamknięcie całej instrukcji Select Case, jeżeli jest to ostatni blok w całej tej instrukcji).

Instrukcja Select Case kończy się wierszem zamknięcia:
 
End Select

Schemat działania instrukcji Select Case jest następujący: po natrafieniu przez kompilator na wiersz otwarcia instrukcji Select Case, wyliczana jest aktualna wartość określonego w nim wyrażenia (w omawianym przykładzie jest to zmienna numer, będąca argumentem wejściowym całej funkcji, więc jej aktualną wartością będzie wartość podana przy wywołaniu tej funkcji). Następnie kompilator sprawdza kolejno wszystkie bloki opisane we wnętrzu instrukcji Select Case i jeżeli stwierdzi, że wartość określona dla któregoś z bloków jest równa wyliczonej wcześniej wartości wyrażenia bazowego, wykonuje wszystkie polecenia zawarte w tym bloku, a następnie opuszcza całą instrukcję Select Case.

Jeżeli więc przykładowo, wywołując funkcję nazwaMiesiaca, podałeś jako argument numer liczbę 3, to po wejściu do instrukcji Select Case kompilator pominie dwa pierwsze bloki, ponieważ ich wartości nie odpowiadają wartości wyrażenia bazowego, a następnie stwierdzi, że wartość trzeciego bloku spełnia ten wymóg, wykona więc przewidziane dla tego bloku operacje (czyli przypisanie do zmiennej nazwaMiesiaca tekstu marzec), po czym opuści instrukcję Select Case nie sprawdzając już pozostałych bloków. Oprócz instrukcji Select Case opisywana funkcja nie zawiera już żadnych innych poleceń, więc jej działanie w tym momencie dobiegnie końca i zwróci ona wartość taką, jaką posiada zmienna nazwaMiesiaca (czyli przypisany przed chwilą tekst marzec).

W sytuacji, gdy w pojedynczym bloku Case zawarta jest tylko jedna operacja, cały ten blok może zostać zwinięty do jednego wiersza. Wówczas po określeniu wartości dla tego bloku, należy wstawić dwukropek, a następnie wpisać przewidzianą dla tego bloku operację.

Jako przykład mogą tu posłużyć bloki Case w instrukcji Select Case z poprzedniego przykładu, gdyż dla każdego z nich była wykonanywana tylko operacja przypisania odpowiedniej nazwy miesiąca do zmiennej nazwaMiesiaca. Cały poprzedni przykład mógłby więc równie dobrze wyglądać tak:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Function nazwaMiesiaca(numer As Integer) As String

    Select Case numer
        Case 1: nazwaMiesiaca = "styczeń"
        Case 2: nazwaMiesiaca = "luty"
        Case 3: nazwaMiesiaca = "marzec"
        Case 4: nazwaMiesiaca = "kwiecień"
        Case 5: nazwaMiesiaca = "maj"
        Case 6: nazwaMiesiaca = "czerwiec"
        Case 7: nazwaMiesiaca = "lipiec"
        Case 8: nazwaMiesiaca = "sierpień"
        Case 9: nazwaMiesiaca = "wrzesień"
        Case 10: nazwaMiesiaca = "październik"
        Case 11: nazwaMiesiaca = "listopad"
        Case 12: nazwaMiesiaca = "grudzień"
    End Select
End Function

Łączenie kilku wartości w pojedynczym bloku Case

Często zdarza się, że operacje wykonywane przez kilka bloków instrukcji Select Case są dokładnie takie same. Przykładem takiej sytuacji jest poniższa funkcja zwracająca cenę wynajmu miejsca w hotelu w zależności od miesiąca.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Function cenaWynajmu(miesiac As Integer) As Integer
    Select Case miesiac
        Case 1: cenaWynajmu = 40
        Case 2: cenaWynajmu = 40
        Case 3: cenaWynajmu = 40
        Case 4: cenaWynajmu = 55
        Case 5: cenaWynajmu = 55
        Case 6: cenaWynajmu = 70
        Case 7: cenaWynajmu = 70
        Case 8: cenaWynajmu = 70
        Case 9: cenaWynajmu = 60
        Case 10: cenaWynajmu = 40
        Case 11: cenaWynajmu = 40
        Case 12: cenaWynajmu = 40
    End Select
End Function

Jak widać w ramce z kodem, cena wynajmu zmienia się trzykrotnie w ciągu roku i wynosi 40 zł w okresie od października do marca, 55 zł w kwietniu i marcu, 70 zł od czerwca do sierpnia oraz 60 zł we wrześniu.

W powyższym kodzie wielokrotnie powtarzane są te same instrukcje (np. cenaWynajmu = 40), co powinno być ograniczone do absolutnego minimum. Na szczęście w tej sytuacji język VBA umożliwia wyeliminowanie tych powtórzeń, a tym samym znaczne skrócenie całego kodu, poprzez umieszczenie w tym samym bloku Case kilku wartości, dla których wykonywany ma być taki sam zestaw poleceń.

Poniżej przedstawiono zmodyfikowaną postać funkcji cenaWynajmu, w której znajdują się już tylko 4 bloki Case , za to większość z nich obowiązuje równocześnie dla kilku wartości wyrażenia bazowego.
1
2
3
4
5
6
7
8
Function cenaWynajmu(miesiac As Integer) As Integer
    Select Case miesiac
        Case 1, 2, 3, 10, 11, 12: cenaWynajmu = 40
        Case 4, 5: cenaWynajmu = 55
        Case 6, 7, 8: cenaWynajmu = 70
        Case 9: cenaWynajmu = 60
    End Select
End Function

W obecnej postaci kodu, przy niektórych słowach kluczowych Case wymienionych jest kilka wartości, oddzielonych od siebie przecinkami. Kompilator sprawdzając teraz, czy poszczególne bloki powinny być wykonane, bierze pod uwagę wszystkie wartości przypisane do danego bloku. Jeżeli którakolwiek z tych wartości jest równa wartości wyrażenia bazowego, wykonane zostają operacje przewidziane dla tego bloku Case.

Nawet jeżeli jakaś wartość zostanie przypisana równocześnie do dwóch lub więcej bloków Case, tak naprawdę będzie obowiązywała tylko dla pierwszego z nich. Dzieje się tak, ponieważ po natrafieniu na blok zawierający wartość wyrażenia bazowego, kompilator wykonuje operacje przewidziane dla tego bloku, a następnie opuszcza całą konstrukcję Select Case, nie sprawdzając już pozostałych bloków.

Przykładowo w poniższym zapisie:
1
2
3
4
5
6
Sub Makro(numer As Integer)
    Select Case numer
        Case 1, 2, 3: Cells(1,1) = "liczba mniejsza od 4"
        Case 3, 4, 5: Cells(2,1) = "liczba większa od 2"
    End Select
End Sub
nawet jeżeli argument wejściowy numer będzie wynosił 3, wykonana zostanie tylko operacja przewidziana dla pierwszego bloku, mimo że dla wyrażenia bazowego równego 3 warunek zawarty w drugim bloku również byłby spełniony.

Z uwagi na opisany w powyższej ramce schemat działania kodu w instrukcjach Select Case zawsze najlepiej jest umieszczać bloki Case w kolejności od najbardziej do najmniej prawdopodobnego.

Załóżmy przykładowo, że funkcja cenaWynajmu w zdecydowanej większości jest wywoływana, aby sprawdzić cenę pokoju dla miesięcy wakacyjnych, znacznie rzadziej dla miesięcy wiosennych oraz września, a już zupełnie rzadko dla pozostałych okresów roku. W takiej sytuacji warto jest przerobić ją do następującej postaci:
1
2
3
4
5
6
7
8
Function cenaWynajmu(miesiac As Integer) As Integer
    Select Case miesiac
        Case 6, 7, 8: cenaWynajmu = 70
        Case 4, 5: cenaWynajmu = 55
        Case 9: cenaWynajmu = 60
        Case 1, 2, 3, 10, 11, 12: cenaWynajmu = 40
    End Select
End Function

Jedyna różnica pomiędzy obecną, a poprzednią postacią funkcji polega na innej kolejności bloków Case wewnątrz instrukcji Select Case. Zauważ, że w obecnej postaci funkcji, jeżeli zapytanie dotyczy któregoś z miesięcy wakacyjnych (a poczynione zostało założenie, że takie zapytania stanowią zdecydowaną większość), kompilator już przy pierwszym bloku Case stwierdzi, że jest on spełniony, wykona przypisane do niego operacje i opuści instrukcję Select Case nie marnując czasu na sprawdzanie pozostałych bloków. W poprzedniej wersji funkcji, zanim kompilator dotarł do bloku opisującego miesiące wakacyjne, musiał jeszcze wcześniej sprawdzić dwa inne bloki.

Oczywiście w sytuacjach, kiedy jako argument miesiac podana będzie np. liczba 2, to funkcja w tej postaci zadziała minimalnie wolniej niż poprzednio. Ale skoro, jak wcześniej ustalono, zapytania takie stanowią jedynie niewielką część wszystkich zapytań, to korzyści ze skrócenia czasu wykonania funkcji dla miesięcy wakacyjnych znacznie przewyższą straty wynikające z jego wydłużenia dla pozostałych sytuacji.

Zawsze warto jest więc poświęcić trochę czasu na zastanowienie się, które bloki Case będą najczęściej spełnione i umieszczenie ich na samej górze instrukcji Select Case.

Należy w tym miejscu omówić jeszcze jedną właściwość instrukcji Select Case, a mianowicie wykonywanie jakichś operacji w sytuacji, gdy żaden z bloków nie został spełniony (czyli odpowiednik sekcji Else w zwykłej instrukcji warunkowej If ... Then).

Załóżmy, że omawiany w poprzednim przykładzie rozkład ceny wynajmu został zmieniony i teraz cena w czerwcu wynosi 65 zł, w lipcu - 70 zł, w sierpniu - 75 zł, a we wszystkich pozostałych miesiącach - 50 zł. Oczywiście w takiej sytuacji również należy zastosować instrukcję Select Case, a nie If ... Then, ponieważ w tym drugim przypadku konieczne byłoby zagnieżdżenie w sobie trzech warunków.

Kod funkcji cenaWynajmu wyglądałby więc teraz tak:
1
2
3
4
5
6
7
8
Function cenaWynajmu(miesiac As Integer) As Integer
    Select Case miesiac
        Case 1, 2, 3, 4, 5, 9, 10, 11, 12: cenaWynajmu = 50
        Case 6: cenaWynajmu = 65
        Case 7: cenaWynajmu = 70
        Case 8: cenaWynajmu = 75
    End Select
End Function

Zauważ jednak, że w pierwszym bloku Case wypisanych jest aż 9 wartości, a dodatkowo są to wszystkie możliwe wartości jakie może przyjąć argument miesiac oprócz wartości opisanych w pozostałych blokach (ponieważ numer miesiąca może być tylko liczbą naturalną z zakresu 1-12).

W takiej sytuacji wygodnym rozwiązaniem jest wykorzystanie bloku Case Else. Blok Case Else jest odpowiednikiem bloku Else w zwykłej instrukcji warunkowej i określa operacje do wykonania w przypadku niespełnienia żadnego ze zdefiniowanych warunków.

Funkcja cenaWynajmu po umieszczeniu w niej bloku Case Else wyglądałaby tak, jak poniżej:
1
2
3
4
5
6
7
8
Function cenaWynajmu(miesiac As Integer) As Integer
    Select Case miesiac
        Case 6: cenaWynajmu = 65
        Case 7: cenaWynajmu = 70
        Case 8: cenaWynajmu = 75
        Case Else: cenaWynajmu = 50
    End Select
End Function

Teraz, gdy w instrukcji Select Case znajduje się blok Case Else, schemat postępowania kodu w tej instrukcji wygląda następująco: kompilator sprawdza po kolei wszystkie bloki zawarte w instrukcji Select Case i jeżeli wartość przypisana do któregoś z bloku odpowiada wartości wyrażenia bazowego to wykonuje operacje przewidziane dla tego bloku i opuszcza instrukcję Select Case. Jeżeli natomiast żaden z bloków nie posiada wartości równej wyrażeniu bazowemu, to po dotarciu do bloku Case Else kompilator wykonuje czynności opisane w tym bloku, niezależnie od tego jaka jest wartość wyrażenia bazowego.

Jeżeli więc miesiąc podany jako argument przy wywoływaniu powyższej funkcji będzie pochodził z zakresu wrzesień-maj, to w instrukcji Select Case ominięte zostaną trzy pierwsze bloki i po dotarciu kodu do bloku Case Else wykonane zostaną operacje przewidziane dla tego bloku, a więc przypisanie do zmiennej cenaWynajmu wartości 50.

Blok Case Else musi być ostatnim blokiem w instrukcji Select Case.

Jeżeli po bloku Case Else zostanie umieszczony jeszcze jakiś inny blok, przy próbie uruchomienia makra zostanie wyświetlony komunikat Compile error: Case without Select Case.

Wykorzystanie zakresów w blokach Case

Zdarzają się sytuacje, że nawet możliwość umieszczenia kilku wyrażeń w jednym bloku Case nie wystarcza, aby w pełni opisać wszystkie wartości, jakie może przyjąć zmienna rozpatrywana przez instrukcję Select Case.

Za doskonały przykład może tu posłużyć funkcja obliczająca wysokość podatku naliczanego wg skali progresywnej (przy obowiązujących obecnie w Polsce dwóch progach podatkowych nie byłoby potrzeby stosowania instrukcji Select Case, ponieważ świetnie nadaje się do tego celu zwykła instrukcja warunkowa If ... Then; dlatego na potrzeby przykładu załóżmy, że obowiązują trzy progi podatkowe: 20%, 30% i 40% osiągane odpowiednio przy dochodach: 0 - 40 000, 40 000 - 80 000, ponad 80 000).

Oczywiście niemożliwością byłoby ręczne wypisanie wszystkich możliwych wartości, przy których podatnik należy przykładowo do drugiego progu, gdyż zapis taki musiałby wyglądać mniej więcej tak:
 
Case 40000.01, 40000.02, 40000.03, 40000.04 ... 'itd.

Trzeba byłoby w ten sposób wypisać wszystkie liczby, aż do osiągnięcia górnego pułapu tego progu, czyli kwoty 80 000 (na domiar złego trzeba byłoby poczynić założenie, że przy wywoływaniu tej funkcji argument określający wysokość pensji brutto nie zostanie podany z trzema lub więcej miejscami po przecinku).

Ratunkiem jest w tej sytuacji kolejna bardzo przydatna właściwość instrukcji Select Case - możliwość opisania w bloku Case zakresu wartości.

Poniżej znajduje się schemat funkcji wyliczającej podatek, w którym do zdefiniowania poszczególnych bloków instrukcji Select Case wykorzystano zakresy liczb:
1
2
3
4
5
6
7
8
9
Function podatekDochodowy(pensjaBrutto As Single) As Single
    Select Case pensjaBrutto
        Case Is < 40000: podatekDochodowy = pensjaBrutto * 0.2
        Case Is < 80000
            podatekDochodowy = 8000 + (pensjaBrutto - 40000) * 0.3
        Case Else
            podatekDochodowy = 20000 + (pensjaBrutto - 80000) * 0.4
    End Select
End Function

Zwróć uwagę, że każdy z zakresów opisujących poszczególne bloki jest ograniczony tylko z jednej strony. W przypadku pierwszego bloku nie ma w tym nic dziwnego, ponieważ ma on być wykonywany dla wartości pensji brutto mniejszych od 40 000, więc jednostronne ograniczenie zakresu (Is < 40000) jest jak najbardziej wskazane.

Drugi blok ma być jednak wykonywany dla pensji brutto z przedziału 40 000 - 80 000, natomiast, jak widzisz w kodzie, jedyne ograniczenie nałożone na jego wartości jest takie, że muszą być one mniejsze niż 80 000. Czy oznacza to, że blok ten może zostać wykonany także dla wartości mniejszych niż 40 000? Oczywiście taka sytuacja nigdy się nie wydarzy, a wynika to z opisywanego wcześniej schematu zachowania aplikacji po natrafieniu w kodzie na instrukcję Select Case. Jeżeli argument pensjaBrutto będzie mniejszy niż 40 000 to już pierwszy blok (Case Is < 40000) będzie spełniony i po jego wykonaniu kompilator opuści instrukcję Select Case w ogóle nie sprawdzając czy spełnione są kolejne bloki. Aby więc wykonywanie kodu w ogóle dotarło do drugiego bloku, zmienna pensjaBrutto nie może być mniejsza niż 40 000.

Jak więc widzisz, za jedno z ograniczeń zakresu przypisanego do bloku Case mogą posłużyć zakresy zdefiniowane dla poprzedzających go bloków, tak jak ma to miejsce w powyższym przykładzie, gdzie rolę dolnej granicy zakresu wartości dla drugiego bloku (Case Is < 80000) pełni zakres przypisany do pierwszego bloku.

W ostatnim bloku Case nie ma już potrzeby szczegółowego opisywania wartości, ponieważ ma on być wykonywany dla wszystkich pozostałych wartości pensji brutto, dzięki czemu można skorzystać z polecenia Case Else (oczywiście zapis Case Is >= 80 000 również byłby poprawny, jednak użyty w przykładzie zapis Case Else jest wygodniejszy w użyciu).

Ogólny zapis bloku Case z zakresem wartości ograniczonym jednostronnie przedstawia się następująco:
 
 
 
 
Case Is > limit
Case Is >= limit
Case Is < limit
Case Is <= limit

Charakterystycznym elementem bloku z jednostronnym zakresem jest słowo kluczowe Is występujące pomiędzy poleceniem Case, a opisem limitu. Jeżeli jednak przy definiowaniu jakiegoś bloku zapomnisz wpisać tego słowa, a równocześnie użyjesz znaku mniejszości (<) lub większości (>), edytor VBA domyśli się, że chcesz określić wartośc za pomocą jednostronnego zakresu i automatycznie doda w odpowiednim miejscu słowo kluczowe Is.

Instrukcja Select Case umożliwia także wykorzystanie w blokach Case zakresów ograniczonych z obu stron.

Aby przedstawić wykorzystanie tego typu zakresów w instrukcji Select Case jeszcze raz zostanie użyta funkcja cenaWynajmu, zwracająca koszt wynajmu miejsca w hotelu. Tym razem jednak, argumentem podawanym przy wywoływaniu funkcji jest data dzienna, a nie jak poprzednio numer miesiąca. Dzięki temu można bardziej szczegółowo opisać zmianę ceny miejsca hotelowego i wziąć pod uwagę terminy, w których powinna być ona wyższa, np. długie weekendy (dla uproszczenia funkcja zwraca tylko ceny na rok 2011).

Funkcja cenaWynajmu wykorzystująca w blokach Case zakresy danych przedstawia się następująco:
1
2
3
4
5
6
7
8
9
10
Function cenaWynajmu(data As Date) As Integer
    Select Case data
        Case #2011-01-01# To #2011-04-28#: cenaWynajmu = 40
        Case #2011-04-29# To #2011-05-03#: cenaWynajmu = 70
        Case #2011-05-04# To #2011-06-16#: cenaWynajmu = 55
        Case #2011-06-17# To #2011-08-28#: cenaWynajmu = 70
        Case #2011-08-29# To #2011-10-02#: cenaWynajmu = 55
        Case #2011-10-03# To #2011-12-31#: cenaWynajmu = 40
    End Select
End Function

Ogólny zapis bloku Case z dwustronnie ograniczonym zakresem wartości wygląda następująco:
 
Case x To y
gdzie x to dolna granica tego zakresu, a y - górna. Bardzo istotne jest zachowanie kolejności dolnego i górnego limitu, ponieważ w sytuacji gdy najpierw umieścisz wyższą wartość, a potem niższą (np. Case 20 To 10), kompilator potraktuje to jako pusty zbiór i blok ten nigdy nie zostanie wykonany.

Przypisując do bloku Case zakresy również możesz korzystać z możliwości umieszczania w pojedynczym bloku kilku wyrażeń.

Powyższa funkcja cenaWynajmu zawiera przykładowo aż 6 bloków Case, ale tak naprawdę wykonują one na przemian 3 operacje - przypisanie do funkcji wartości 40 zł, 55 zł lub 70 zł. Nie ma więc przeszkód, aby połączyć ze sobą bloki wykonujące te same operacje:
1
2
3
4
5
6
7
8
9
10
Function cenaWynajmu(data As Date) As Integer
    Select Case data
        Case #2011-01-01# To #2011-04-28#, #2011-10-03# To #2011-12-29#
                cenaWynajmu = 40
        Case #2011-04-29# To #2011-05-03#, #2011-06-17# To #2011-08-28#
                cenaWynajmu = 70
        Case #2011-05-04# To #2011-06-16#, #2011-08-29# To #2011-10-02#
                cenaWynajmu = 55
    End Select
End Function

Zasada łączenia kilku zakresów w jednym bloku jest dokładnie taka sama jak w przypadku łączenia pojedynczych wartości - wystarczy po prostu wymienić po słowie kluczowym Case wszystkie obowiązujące dla tego bloku zakresy oddzielając je od siebie przecinkami.

Nie ma też żadnych przeszkód, aby w jednym bloku wymienić zarówno zakresy, jak i pojedyncze wartości. Jeżeli przykładowo do pierwszego bloku w powyższej funkcji chciałbyć dodać jeszcze datę 14 maja, wystarczyłoby dopisać tę datę do listy wartości zdefiniowanych dla tego bloku:
 
Case #2011-01-01# To #2011-04-28#, #2011-10-03# To #2011-12-29#, #2011-05-14#

Zagnieżdżanie instrukcji Select Case

Instrukcja Select Case, podobnie jak inne omówione dotychczas instrukcje, smoże być zagnieżdżana. Zagnieżdżanie instrukcji Select Case jest bardzo proste i wymaga po prostu wstawienia w bloku Case , należącym do jednej instrukcji, kolejnej instrukcji Select Case.

Przykład makra zawierającego zagnieżdżone instrukcje Select Case przedstawiono poniżej. Jest to rozbudowana wersja omawianej wcześniej funkcji cenaWynajmu, która oprócz daty wynajmu bierze pod uwagę wielkość wynajmowanego pokoju.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Function cenaWynajmu(data As Date, ileOsobWPokoju As Byte) As Integer
    Select Case data
        Case #2011-01-01# To #2011-04-28#, #2011-10-03# To #2011-12-29#
                Select Case ileOsobWPokoju
                        Case 2: cenaWynajmu = 46
                        Case 3: cenaWynajmu = 60
                        Case 4: cenaWynajmu = 72
                End Select
        Case #2011-04-29# To #2011-05-03#, #2011-06-17# To #2011-08-28#
                Select Case ileOsobWPokoju
                        Case 2: cenaWynajmu = 80
                        Case 3: cenaWynajmu = 99
                        Case 4: cenaWynajmu = 120
                End Select
        Case #2011-05-04# To #2011-06-16#, #2011-08-29# To #2011-10-02#
                Select Case ileOsobWPokoju
                        Case 2: cenaWynajmu = 60
                        Case 3: cenaWynajmu = 78
                        Case 4: cenaWynajmu = 92
                End Select
    End Select
End Function

Funkcja cenaWynajmu posiada teraz dwa argumenty wejściowe: data oraz ileOsobWPokoju.

Wewnątrz funkcji znajduje się instrukcja Select Case, która rozpatruje podaną przy wywoływaniu funkcji datę. W każdym z bloków Case znajduje się natomiast kolejna instrukcja Select Case, w której rozpatrywanym wyrażeniem jest wartość zmiennej ileOsobWPokoju. Dopiero w tej zagnieżdżonej instrukcji w blokach Case znajdują się konkretne operacje wpływające na wynik funkcji, czyli przypisanie do niej odpowiedniej ceny wynajmu.

Wyliczenia

Często zdarza się, że przy wywoływaniu funkcji lub procedury, oprócz argumentów wejściowych na podstawie których obliczane będą wyniki, potrzebny jest jeszcze jakiś dodatkowy parametr określający sposób wykonania tej funkcji lub procedury.

Przykładem takiego dodatkowego parametru jest np. dzień uważany za pierwszy dzień tygodnia podawany przy wywoływaniu funkcji Weekday, zwracającej numer dnia tygodnia, lub rodzaj ikony jaka ma się pojawić w oknie wyświetlanym przez funkcję MsgBox.

W takim przypadku konieczne jest określenie listy dostępnych wariantów danego parametru, dla których będą zdefiniowane odpowiadające im operacje w kodzie. Zdefiniować trzeba także spójny system zapisu tych wariantów, tak aby w każdym miejscu kodu były one tak samo oznaczane i by kompilator nie miał problemu z ich interpretacją (np. dla parametru oznaczającego dzień uważany za pierwszy dzień tygodnia nie można podawać w jednym miejscu słowa poniedziałek, a w innym Monday - w każdym miejscu kodu sposób zapisu musi być jednakowy).

Być może na razie brzmi to trochę zawile, ale wszystko powinno stać się dla Ciebie jaśniejsze po zapoznaniu się z poniższym przykładem.

Wróćmy do funkcji nazwaMiesiaca analizowanej na początku tej lekcji przy okazji omawiania instrukcji Select Case i załóżmy, że funkcja ta ma zostać zmodyfikowana w taki sposób, aby przy jej wywoływaniu, oprócz argumentu określającego numer miesiąca, dla którego ma być zwrócona nazwa, podawany był jeszcze drugi argument, określający język, w którym nazwa tego miesiąca ma być zwrócona. Funkcja będzie umożliwiała uzyskanie nazwy miesiąca w jednym z trzech języków (polskim, angielskim i hiszpańskim), konieczne będzie więc zastosowanie zagnieżdżonych instrukcji Select Case - jedna z tych instrukcji będzie sprawdzała numer miesiąca, a druga język, który podano przy wywoływaniu funkcji.

Jaki w ogóle typ powinien posiadać w tej funkcji argument jezyk? Na podstawie dotychczas zdobytej wiedzy, mógłbyś się zastanawiać nad dwiema możliwościami - typem String lub którymś z typów liczbowych - niestety, oba te rozwiązania obarczone są wadami, które szerzej opisano poniżej.

Jeżeli argument jezyk zostanie zdefiniowany jako argument typu String, pierwsza przeszkoda pojawia się już przy budowaniu instrukcji Select Case, określającej w jakim języku ma być zwrócona nazwa miesiąca.

Jaką wartość należy zdefiniować dla bloku Case, aby kompilator wiedział, że chodzi przykładowo o język polski? Załóżmy, że dla opisania poszczególnych języków użyłeś nazw polski, angielski i hiszpański, a cała funkcja wygląda tak (dla skrócenia kodu w każdym języku zapisano tylko 6 pierwszy miesięcy):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Function nazwaMiesiaca(numer As Integer, jezyk As String) As String
    Select Case jezyk
        Case "polski"
                Select Case numer
                    Case 1: nazwaMiesiaca = "styczeń"
                    Case 2: nazwaMiesiaca = "luty"
                    Case 3: nazwaMiesiaca = "marzec"
                    Case 4: nazwaMiesiaca = "kwiecień"
                    Case 5: nazwaMiesiaca = "maj"
                    Case 6: nazwaMiesiaca = "czerwiec"
                    'bloki dla kolejnych miesięcy
                End Select
        Case "angielski"
                Select Case numer
                    Case 1: nazwaMiesiaca = "January"
                    Case 2: nazwaMiesiaca = "February"
                    Case 3: nazwaMiesiaca = "March"
                    Case 4: nazwaMiesiaca = "April"
                    Case 5: nazwaMiesiaca = "May"
                    Case 6: nazwaMiesiaca = "June"
                    'bloki dla kolejnych miesięcy
                End Select
        Case "hiszpański"
                Select Case numer
                    Case 1: nazwaMiesiaca = "enero"
                    Case 2: nazwaMiesiaca = "febrero"
                    Case 3: nazwaMiesiaca = "marzo"
                    Case 4: nazwaMiesiaca = "abril"
                    Case 5: nazwaMiesiaca = "mayo"
                    Case 6: nazwaMiesiaca = "junio"
                    'bloki dla kolejnych miesięcy
                End Select
        End Select
End Function

Budowa oraz schemat działania tej funkcji są bardzo proste i, jeżeli zapoznałeś się z podrozdziałem dotyczącym zagnieżdżania instrukcji Select Case, ich zrozumienie nie powinno przysporzyć Ci żadnych kłopotów. Funkcja składa się z instrukcji Select Case, zawierającej trzy bloki - każdy opisujący jeden z języków. W każdym z tych bloków zagnieżdżona jest kolejna instrukcja Select Case, która zawiera z kolei bloki z poszczególnymi numerami miesięcy.

Kompilator sprawdza więc najpierw który język został podany przy wywołaniu funkcji, a następnie, wewnątrz bloku przeznaczonego dla tego języka, wyszukuje podanego w argumencie numeru miesiąca i przypisuje do wyniku funkcji odpowiednią nazwę miesiąca.

Zauważ, że aby kompilator rozpoznał, jaki język masz na myśli wywołując tę funkcję, musisz podać jego nazwę dokładnie w takiej postaci, w jakiej określiłeś ją w odpowiednim bloku Case . Jeżeli więc chcesz przykładowo wyświetlić nazwę któregoś miesiąca po hiszpańsku, jako argument jezyk musisz podać wyraz hiszpański, a nie hiszpanski, ESP czy jeszcze jakąś inną formę. Oczywiście bezpośrednio po utworzeniu tej funkcji będziesz pamiętał o tej zasadzie i nie będzie problemów z jej prawidłowym wywołaniem (aczkolwiek nawet wtedy może się przytrafić jakaś literówka, która spowoduje zwrócenie błędnej wartości).

Wyobraź sobie teraz jednak, że po kilku tygodniach w aplikacji korzystającej z tej funkcji trzeba coś dodać lub poprawić, więc po długiej przerwie ponownie musisz zajrzeć do kodu tego makra (lub, co gorsza, musi do niego zajrzeć ktoś inny, kto nigdy wcześniej nie widział go na oczy). Po kilku tygodniach prawdopodobnie nie będziesz już pamiętał czy do bloku Case , opisującego język polski, przypisałeś tekst polski czy też może było to pol lub PL. Być może nie pamiętałbyś już nawet, jakie języki są w ogóle dostępne w funkcji nazwaMiesiaca.

Pozostaje Ci więc albo odszukanie tej funkcji w kodzie i przeanalizowanie jej budowy, albo zgadywanie, jaka nazwa została zastosowana do zidentyfikowania bloku Case zawierającego polskie nazwy miesięcy. Jeżeli jednak użyjesz nieprawidłowej nazwy i wywołasz funkcję przykładowo w takiej postaci:
 
nazwaMiesiaca(3, "pl")
kompilator w żaden sposób nie domyśli się, że chcesz w ten sposób zwrócić polską nazwę miesiąca, ponieważ do żadnego bloku Case w funkcji nazwaMiesiaca nie przypisano wartości pl. Wynikiem tej funkcji przy takich argumentach wejściowych byłby więc pusty ciąg znaków.

Oczywiście, jak pamiętasz z podrozdziału poświęconego rozbudowanym blokom Case , do pojedynczego bloku można przypisać kilka wartości, zwiększając tym samym prawdopodobieństwo późniejszego trafienia właściwej nazwy, np.
 
Case "polski", "pol", "pl", "język polski", "Polish"

Nigdy nie ma jednak gwarancji, że wypiszesz wszystkie wartości, które przyjdą do głowy innym osobom korzystającym z tej funkcji lub Tobie samemu podczas jej późniejszego stosowania. Właściwie to jest niemal pewne, że prędzej czy później ktoś użyje wartości, której w tym bloku Case nie uwzględniono.

Jeszcze gorszym pomysłem wydaje się być zastosowanie liczbowego typu zmiennej do określania, w jakim języku ma być zwrócona nazwa miesiąca. W tym przypadku trzeba byłoby najpierw przypisać do każdego z dostępnych języków jakąś liczbę, a potem stosować tę numerację przy definiowaniu poszczególnych bloków w instrukcji Select Case oraz przy późniejszym wywoływaniu tej funkcji.

Takie rozwiązanie jest jednak bardzo niewygodne i podatne na błędy, gdyż na dłuższą metę nie sposób zapamiętać jaka liczba została przypisana do każdego z języków, tym bardziej, jeżeli w aplikacji znajdowałoby się więcej tego typu parametrów.

Wszystkich niedogodności związanych z korzystaniem w podobnych sytuacjach ze zmiennych tekstowych lub liczbowych pozbawione są natomiast wyliczenia.

Wyliczenie, zwane też enumeracją, jest zestawem dopuszczalnych wartości, jakie można przypisać do jakiegoś parametru.

W omawianym przykładzie takim parametrem jest język, a lista dostępnych wartości składa się z trzech pozycji: języka polskiego, angielskiego i hiszpańskiego.

Postać ogólna wyliczenia wygląda następująco:
 
 
 
 
 
 
Enum Nazwa
    wartosc1 [=liczba]
    wartosc2 [=liczba]
    '(...)
    wartoscN [=liczba]
End Enum

Nazwa powinna jak najdokładniej określać parametr opisywany przez to wyliczenie. Możesz ustalić sobie także jakiś skrót, który będzie poprzedzał wszystkie deklarowane w kodzie wyliczenia, tak aby potem w trakcie pisania makr móc szybko przeglądać ich listę (np. enumNazwaWyliczenia). Przykładowo, wszystkie wyliczenia fabrycznie zaimplementowane w VBA poprzedzone są literami Vb, dzięki czemu korzystając z autopodpowiedzi, po wpisaniu tych dwóch liter widzisz zgromadzoną w jednym miejscu listę wszystkich wbudowanych wyliczeń, tak jak to widać na poniższym rysunku (zwróć przy okazji uwagę na ikonę symbolizującą wyliczenie, pozwalającą z łatwością rozpoznać, które elementy znajdujące się na liście autopodpowiedzi są wyliczeniami).

Wszystkie fabryczne wyliczenia VBA zgromadzone w jednym miejscu, dzięki poprzedzeniu ich nazw literami 'Vb'

We wnętrzu wyliczenia powinny zostać wypisane wszystkie wartości dostępne dla opisywanego przez to wyliczenie parametru. W każdej linijce powinna zostać umieszczona tylko nazwa opisująca konkretny wariant parametru bez żadnych poprzedzających ją słów kluczowych. Do każdej wartości można ewentualnie przypisać jakąś wartość liczbową, co czasami może okazać się bardzo przydatne (przykłady takich sytuacji zostaną jednak przedstawione w dalszej części kursu).

Przy nadawaniu nazw wartościom wyliczenia obowiązują te same reguły, co przy nadawaniu nazw funkcjom i zmiennym. Dodatkowe zastrzeżenie jest takie, że w ramach jednego wyliczenia nie mogą znaleźć się dwie wartości o identycznych nazwach.

Blok definiujący wyliczenie musi być umieszczony u góry modułu, jeszcze przed rozpoczęciem jakiejkolwiek funkcji lub procedury.

Umieszczenie bloku z wyliczeniem za jakąś funkcją lub procedurą spowoduje, że przy próbie uruchomienia makra zostanie wyświetlony błąd: Compile error: Only comments may appear after End Sub, End Function or End Property.

Wyliczenie, które będzie użyte w analizowanej funkcji nazwaMiesiaca powinno wyglądać następująco:
1
2
3
4
5
Enum Jezyk
    polski
    angielski
    hiszpanski
End Enum
co oznacza, że opisuje ono języki i udostępnia trzy warianty tego parametru: język polski, angielski i hiszpański.

Wiesz już jak stworzyć w kodzie blok wyliczeniowy i zdefiniować dostępne dla niego wartości, najwyższy więc czas, abyś dowiedział się, jak można to wyliczenie wykorzystać podczas pisania programu.

Każde wyliczenie jest traktowane przez edytor VBA jak dodatkowy typ danych. Oznacza to, że od tego momentu możesz deklarować zmienne jako typ Jezyk. Zauważ, że typ Jezyk pojawia się też od teraz na rozwijanej liście dostępnych typów zmiennych (co pokazano na poniższym rysunku):

Typ wyliczeniowy Jezyk na liście autopodpowiedzi

Tak jak w przypadku każdego innego typu zmiennych, zakres danych przyjmowanych przez typ Jezyk nie jest dowolny i ma pewne ograniczenia. Jak się zapewne domyślasz, każdy wyliczeniowy typ danych może przyjmować tylko takie wartości, jakie zostały zdefiniowane w bloku opisującym to wyliczenie. W przypadku typu Jezyk zakres przyjmowanych przez zmienne wartości będzie więc ograniczony do trzech wartości: polski, angielski oraz hiszpanski.

Pamiętaj, że nazwy wyliczeniowe wypisywane są w kodzie bez cudzysłowów, ponieważ nie są one tekstami.

Teraz można zmodyfikować funkcję nazwaMiesiaca tak, aby zamiast zmiennej tekstowej wykorzystywała do określania języka zmienną typu wyliczeniowego Jezyk. Nowa postać funkcji przedstawia się następująco (zmienione fragmentu kodu zostały oznaczone na czerwono):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Function nazwaMiesiaca(numer As Integer, jezyk As Jezyk) As String
    Select Case jezyk
        Case polski
                Select Case numer
                    Case 1: nazwaMiesiaca = "styczeń"
                    Case 2: nazwaMiesiaca = "luty"
                    Case 3: nazwaMiesiaca = "marzec"
                    Case 4: nazwaMiesiaca = "kwiecień"
                    Case 5: nazwaMiesiaca = "maj"
                    Case 6: nazwaMiesiaca = "czerwiec"
                    Case 7: nazwaMiesiaca = "lipiec"
                    Case 8: nazwaMiesiaca = "sierpień"
                    Case 9: nazwaMiesiaca = "wrzesień"
                    Case 10: nazwaMiesiaca = "październik"
                    Case 11: nazwaMiesiaca = "listopad"
                    Case 12: nazwaMiesiaca = "grudzień"
                End Select
        Case angielski
                Select Case numer
                    Case 1: nazwaMiesiaca = "January"
                    Case 2: nazwaMiesiaca = "February"
                    Case 3: nazwaMiesiaca = "March"
                    Case 4: nazwaMiesiaca = "April"
                    Case 5: nazwaMiesiaca = "May"
                    Case 6: nazwaMiesiaca = "June"
                    Case 7: nazwaMiesiaca = "July"
                    Case 8: nazwaMiesiaca = "August"
                    Case 9: nazwaMiesiaca = "September"
                    Case 10: nazwaMiesiaca = "October"
                    Case 11: nazwaMiesiaca = "November"
                    Case 12: nazwaMiesiaca = "December"
                End Select
        Case hiszpanski
                Select Case numer
                    Case 1: nazwaMiesiaca = "enero"
                    Case 2: nazwaMiesiaca = "febrero"
                    Case 3: nazwaMiesiaca = "marzo"
                    Case 4: nazwaMiesiaca = "abril"
                    Case 5: nazwaMiesiaca = "mayo"
                    Case 6: nazwaMiesiaca = "junio"
                    Case 7: nazwaMiesiaca = "julio"
                    Case 8: nazwaMiesiaca = "agosto"
                    Case 9: nazwaMiesiaca = "septiembre"
                    Case 10: nazwaMiesiaca = "octubre"
                    Case 11: nazwaMiesiaca = "noviembre"
                    Case 12: nazwaMiesiaca = "diciembre"
                End Select
        End Select
End Function

W wierszu otwarcia funkcji zmienił się tylko typ danych dla argumentu jezyk.

Zmieniła się także wartość dla każdego z bloków Case w instrukcji Select Case rozpatrującej zmienną jezyk. Wcześniej rozpatrywana przez tę instrukcję zmienna jezyk była zmienną typu tekstowego, dlatego też do bloków Case przypisane była wartości tekstowe: polski, angielski oraz hiszpański. Jednak w obecnej postaci funkcji, zmienna jezyk posiada typ wyliczeniowy Jezyk, w związku z czym wartości przypisane do poszczególnych bloków Case muszą być zgodne z tym typem. Nie mogą więc to być dłużej zmienne tekstowe, ponieważ typ wyliczeniowy Jezyk (ani żaden inny typ wyliczeniowy) nie obsługuje zmiennych tekstowych. Jedyne wartości dostępne dla tego typu danych to polski, angielski oraz hiszpanski, dlatego takie właśnie wartości są teraz zdefiniowane dla bloków Case zamiast poprzednich wartości tekstowych.

Spróbuj teraz wywołać w oknie Immediate gotową już funkcję nazwaMiesiaca (a właściwie prawie gotową, bo w jednej z kolejnych lekcji, poświęconej tablicom, zostanie ona jeszcze bardziej udoskonalona).

Zwróć uwagę, że kiedy wpiszesz już numer miesiąca i przejdziesz do określania języka, edytor VBA wyświetli rozwijaną listę zawierającą wszystkie dostępne warianty tego parametru. Nie ma więc już miejsca na żadną pomyłkę, nie musisz się też dłużej zastanawiać, jakie języki dostępne są w tej funkcji, a dodatkowo nie musisz wpisywać całej nazwy definiującej parametr, ponieważ możesz po prostu wybrać go z listy.

Lista rozwijana ze wszystkimi dostępnymi wartościami dla danego typu wyliczeniowego

Typy wyliczeniowe były już kilkakrotnie stosowane we wcześniejszych lekcjach tego kursu. Należały do nich np. parametry określające pierwszy dzień tygodnia i tydzień uważany za pierwszy tydzień roku (wykorzystywane we wbudowanych funkcjach datowych) lub wszystkie parametry podawane dla argumentu Buttons w funkcji MsgBox.

Należy jeszcze w tym miejscu wrócić do opisanego w lekcji dziewiątej sposobu odczytywania przycisku wciśniętego przez użytkownika w oknie MsgBox.

Z racji tego, że nie znałeś jeszcze typów wyliczeniowych, w omawianym tam przykładzie wciśnięty przycisk był określany za pomocą zmiennej typu Integer. Teraz nie ma już przeszkód, aby wykorzystać do tego celu typ wyliczeniowy VbMsgBoxResult, który jest wbudowanym typem wyliczeniowym przeznaczonym właśnie do idetyfikacji przycisku wciśniętego przez użytkownika w oknie MsgBox.

Omawiana w tamtej lekcji procedura wyswietlanieDaty może więc przyjąć następującą postać (standardowo zmienione fragmenty kodu oznaczono na czerwono):
1
2
3
4
5
6
7
8
9
10
Sub wyswietlanieDaty()
    Dim przycisk As VbMsgBoxResult

    przycisk = MsgBox("Czy chcesz wyświetlić w arkuszu aktualną godzinę?", _
        vbYesNo + vbQuestion, "Potwierdzenie")

    If przycisk = VbYes Then
        Cells(1, 1) = Time
    End If
End Sub

Zmienna przycisk posiada teraz typ VbMsgBoxResult. Za każdym razem, gdy jest ona wykorzystywana w kodzie, edytor VBA wyświetla listę wszystkich dostępnych dla niej wartości, dzięki czemu bardzo łatwo wybrać odpowiednią wartość, a przytoczona w tamtej lekcji tabelka zawierająca liczbowe odpowiedniki poszczególnych przycisków staje się mało użyteczna.