środa, 18 grudnia 2013

Narzędzia programisty VBA

Oczywiście, że podstawowym środowiskiem pracy programisty VBA pozostaje Integrated Developer Editor (IDE) dostarczany w pakiecie Office. Nie mniej istnieje kilka narzędzi pomocniczych, które można zintegrować ze środowiskiem lub jako osobne aplikacje mogą stać się niezastąpioną pomocą w codziennej pracy z VBA. Oto kilka przykładowych programów wartych polecenia i uwagi.

1. EZ-Tool 
 Link do strony dostawcy: www

Narzędzie instalowane jako dodatek środowiska IDE. Wspiera programistę w szeregu czynności: tworzenie procedur, zarządzanie komentarzami, obsługą błędów. Dostarcza schematy dla ADO. W moim odczuciu narzędzie w stylu 'must have'.

2. RibbonX Visual Designer 2010
Link do strony dostawcy: www

Narzędzie wspomagające tworzenie elementów wstążki- zakładek, przycisków i innych kontrolek.

3. Excel VBA Code Cleaner
Link do strony dostawcy: www

Jak sama nazwa wskazuje narzędzie oczyszczające kod ze zbędnych elementów. Narzędzie polecane w przypadku dużych projektów, projektów tworzonych w dłuższych okresach czasu, itp.



piątek, 13 grudnia 2013

Unikalne wartości tablicy Array (2/2)

Kontynuując poprzedni wpis o tym samym tytule tym razem trzecia z technik odnajdywania unikalnych elementów kolekcji. Technika ta opiera się o wykorzystanie słownika Dictionary. Wszystkich czytelników, których rozwiązanie to w jakiś sposób zainteresuje odsyłam do tej strony, na której znaleźć można dodatkowe informacje.

Poniższy kod, korzystając z obiektu słownika, zwróci do arkusza unikalne wartości z tablicy. Wewnątrz kodu zawarte zostały dodatkowe informacje i komentarze wyjaśniające cały mechanizm.


Na koniec kluczowe pytanie, którą technikę użyć- kolekcję czy słownik. Osobiście nie udzielę jednoznacznej odpowiedzi, ale wskażę na kilka różnic i podobieństw boldem oznaczając lepsze rozwiązanie.

a) Kolekcja jest obiektem wbudowanym języka VBA, Słownik wymaga tworzenia referencji do osobnej biblioteki.

b) Kolekcja wymaga stosowania obsługi błędów czego nie ma w przypadku Słownika i co jest rozwiązaniem (pozornie?) bardziej profesjonalnym.

c) Dodawanie elementu do kolekcji ignoruje wielkość liter, słownik uwzględnia wielkość liter przez co wymaga stosowanie dodatkowych technik VBA.

d) Kolekcja posiada ograniczony zasób metod i właściwości podczas gdy słownik pozostaje bardziej elastyczny pod względem szerszych zastosowań.

poniedziałek, 9 grudnia 2013

Unikalne wartości tablicy Array (1/2)

 Jak skutecznie odnaleźć unikalne wartości z tablicy Array?
 powyższe pytanie usłyszałem podczas kursu VBA prowadzonego ostatnio w Warszawie.

Osobiście stosuję dwie (lub trzy) techniki odszukiwania unikatów:

Technika 1. wartości tablicy wrzucamy w pionowy, czysty obszar arkusza, następnie stosując polecenie usuwania duplikatów (RemoveDuplicates) pozbywamy się zbędnych elementów, a następnie wynik pobieramy do naszej tablicy w kodzie VBA. Przyznam jednak, że rozwiązanie to nie należy do najbardziej efektywnych.

Technika 2. o wiele łatwiej i skuteczniej będzie sięgnąć po obiekt kolekcji (Collection) w VBA. W tym celu wykorzystamy fakt, że dodanie elementu do kolekcji, nie jest możliwe jeżeli element taki już istnieje. Jeżeli podejmujemy taką próbę to kompilator zwraca błąd wykonania. W naszej procedurze zignorujemy taki błąd w wyniku czego zignorujemy jednocześnie dany element.


Więcej informacji na temat kolekcji znaleźć można pod tym linkiem.
Kolekcje nie są jedynym czysto programistycznym sposobem na uzyskanie unikatów z tablicy. O alternatywnej technice napiszę w osobnym poście. Zapraszam do lektury już wkrótce.

poniedziałek, 2 grudnia 2013

Co w rzeczywistości zawiera komórka w Excelu?

Z pytaniem umieszczonym w tytule niniejszego posta spotkałem się kilkukrotnie, zarówno przeglądając różne wpisy i pytania na forach internetowych, jak również prowadząc szkolenie z zakresu VBA dla Excela. Chodzi bowiem o sytuację, w której chcemy określić rodzaj informacji zawartej w komórce z uwzględnieniem rodzaju formatowania jaki został zastosowany w danym zakresie arkusza.

Gdzie jednak znajduje się problem? Z punktu widzenia VBA liczba, data, czas i procent- wszystkie te elementy są liczbami. Także wartość Prawda/Fałsz w praktyce jest liczbą odpowiadającą 1 lub 0. W naszej sytuacji określić rzeczywisty typ danych znajdujących się w komórce.

W celu rozwiązania tego problemu wystarczy skonstruować prostą funkcję, której pełną postać znajdziecie Państwo poniżej. Kluczowa w tej funkcji pozostaje kolejność sprawdzania poszczególnych typów. Prześledźmy to na przykładzie typu liczbowego, który sprawdzany jest jako ostatni. Musimy się najpierw upewnić, że podane wartości nie są żadnym z typów: datą, wartością czasu, procentem lub wartością Prawda/Fałsz. Każdy z tych typów będąc domyślnie numerycznym zostałby więc rozpoznany jako liczba. Tymczasem nasza funkcja wydaje się działać prawidłowo co prezentuje poniższy zrzut ekranu.



środa, 27 listopada 2013

Dynamiczne wymiary tablicy wielowymiarowej

Zgodnie z definicją dynamiczna tablica wielowymiarowa może zmienić swój rozmiar (ilość elementów) tylko w zakresie ostatniego jej wymiaru. Jeżeli więc zdefiniujemy naszą tablicę następująco:


to redefinicja wymiaru dotyczyć może tylko drugiego wymiaru, pierwszy wymiar zawsze pozostanie o stałej wielkości równej 1. Możliwe będą więc następujące dalsze procesy:


Możemy jednak stworzyć tablicę wielowymiarową jako złożenie dwóch tablic. W sytuacji tej każdy element tablicy głównej zawierał będzie tablicę podrzędną. Tak mógłby wyglądać zapis takiego rozwiązania:


Jak widać w powyższym przypadku wymiar tablicy głównej można swobodnie zmieniać jak również elementy tablicowe w jej wnętrzu mogą również posiadać różne wymiary. Rozwiązanie to posiada jednak swoje wady, z których części można zaradzić, o czym w dalszej części wpisu.

Wadą pierwszą jest to, że o ile w przypadku pierwszej tablicy dany element pobieramy korzystając z zapisu:


o tyle w drugim przypadku zapis ten jest następujący:


O ile z powyższym w większości przypadków radzimy sobie bez problemu, o tyle problem zasadniczy pojawia się w sytuacji, gdy próbujemy przenieść tablicę do komórek Excela. Dla powyższych przypadków (indeks wymiaru tablic to 2) zapisy te będą następujące:


Chcąc rozwiązać problem dot. tablicy myArrayB i skutecznie przenieść jej wartości do zakresu komórek arkusza wystarczy tablicę tą dwukrotnie transponować. Poniższa instrukcja umożliwia prawidłowe wykonanie tej operacji:

czwartek, 21 listopada 2013

Funkcja MID w roli operatora

Znana i powszechnie stosowana funkcja MID języka VBA jest nie tylko poleceniem zwracającym fragment tekstu, ale także operatorem, którego zadaniem jest podmiana fragmentu tekstu dla określonej zmiennej.

Wyobraźmy sobie taki tekst:"Uzupełnienie k..... przez f...... MID", w którym w miejsce kropek chcemy wstawić odpowiednie rozwinięcie wyrazów aby uzyskać tekst: "Uzupełnienie kropek przez funkcję MID".

Poniżej przedstawiam dwie proste procedury realizujące to zadanie, pierwsza z nich wykorzystuje funkcję MID w nowej roli. Druga z procedur da dokładnie ten sam efekt. Co jednak ciekawe- pierwsza procedura wykona się około 2-3 razy krócej niż druga.


Źródło powyższego działania funkcji MID zaczerpnąłem z tego pytania na StackOverflow.Com. Zainteresowane osoby zachęcam do zapoznania się z innym ciekawostkami zawartymi w odpowiedziach.  O niektórych z tych ciekawostek wspominałem już w swoich wpisach. O innych z pewnością kiedyś wspomnę w strefie wiedzy. Wiele z tych tematów poruszane podczas kursów VBA, które prowadzimy w Warszawie, Krakowie i Wrocławiu.

wtorek, 12 listopada 2013

TypeName i TypeOf- przydatne funkcje i operatory

Muszę przyznać, że prowadząc szkolenie z zakresu VBA zazwyczaj brakuje czasu aby zaprezentować i omówić dwie ważne instrukcje języka VBA: funkcję TypeName i operator TypeOf. Cel stosowania obu instrukcji jest podobny- określić rodzaj obiektu lub zmiennej. Sposób stosowania i wynik mogą być zgoła odmienne.

Spójrzmy na różnice i podobieństwa pomiędzy prezentowanymi instrukcjami przez pryzmat przykładów.

1. Sprawdzamy czy bieżące zaznaczenie odpowiada obiektowi określonego typu, tu: obiektowi Range
 


choć w obu wypadkach uzyskamy wyniku True proszę zwrócić uwagę, że wynikiem pracy funkcji TypeName jest ciąg tekstowy. TypeOf można użyć tylko w relacji do obiektu.


Oczywiście możemy dokonać podobnego sprawdzenia w relacji do zmiennej:

Nie mniej tylko instrukcja TypeName pozwala nam określić jakiego typu jest dana zmienna:

2. Jeżeli badana zmienna przyjmuje wartość Nothing operator TypeOf zwróci błąd a funkcja TypeName tekst Nothing:

3. Operator TypeOf zwraca wartości Prawda/Fałsz, funkcja TypeName zwraca ciąg tekstowy z nazwą typu zmiennej:

4. Operator TypeOf działa szybciej niż funkcja TypeName.

Dodatkowe informacje dot. w/w instrukcji można znaleźć tutaj:
TypeName
TypeOf

wtorek, 5 listopada 2013

Ograniczona ilość znaków w linii (MS Word)

Wyobraźmy sobie sytuację, że określonym dokumencie MS Word chcemy dokonać takiego podziału tekstu, aby w żadnej linii nie było więcej niż 30 znaków. Przykładowy paragraf, który pierwotnie wygląda następująco: 

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna. Nunc viverra imperdiet enim. Fusce est. 

chcemy automatycznie podzielić na następujące linie, z których żadna nie ma więcej niż 30 znaków: 

Lorem ipsum dolor sit amet,
consectetuer adipiscing elit.
Maecenas porttitor congue
massa. Fusce posuere, magna
sed pulvinar ultricies, purus
lectus malesuada libero, sit
amet commodo magna eros quis
urna. Nunc viverra imperdiet
enim. Fusce est.


Poniższe makro wykona za nas tą operację dla wszystkich paragrafów aktywnego dokumentu. Tym razem wszystkie uwagi, ciekawostki i założenia zostały omówione w postaci komentarzy wewnątrz kodu.


czwartek, 31 października 2013

Niemodalne okienko MsgBox

Standardowe okienko MsgBox występujące w języku VBA ma tą (niekiedy uciążliwą) cechę, że jest okienkiem modalnym. Modalnym czyli takim, które należy zamknąć aby móc powrócić do pracy z aplikacją znajdującą się w tle. Jak więc wywołać proste okienko z przyciskami Tak/Nie/Anuluj w trybie niemodalnym? Jest kilka możliwych wariantów spośród których skupię się szczegółowo tylko na jednym.

Wariant A. Należy stworzyć okno UserForm, a następnie wyświetlić go w trybie niemodalnym:


Wariant B. Wykorzystać okienko znajdujące się w zewnętrznej bibliotece Windows Script Hosting. Poniżej prezentuję kompletny kod, który wyświetla okienko z trzema przyciskami oraz wykonuje akcję zależnie od wybranego przycisku.


Rozwiązanie to posiada dodatkową opcję, która pozwala określić czas, przez jaki okienko będzie widoczne. Niestety, parametr ten nie działa stabilnie. W praktyce tylko ustawienie czasu oczekiwania na 1 sekundę gwarantuje ukrycie okna. W przypadku dłuższego czasu oczekiwania okienko i tak oczekuje na akcję za strony użytkownika. Być może błąd ten zależny jest od wersji oprogramowania nie mniej nie udało mi się odnaleźć informacji na temat rozwiązania tego problemu.

Dodatkowe informacje na temat okna Popup z biblioteki Windows Scripting znajdziecie Państwo pod tym linkiem.

wtorek, 22 października 2013

Losowy dokument w Wordzie

Tworząc szereg rozwiązań w VBA dla aplikacji MS Word często testuję określone techniki korzystając z przykładowego 'losowego' dokumentu. Dokument taki można stworzyć w bardzo prosty sposób wykorzystując ukryte polecenia aplikacji MS Word. W tym celu proszę utworzyć nowy pusty dokument w Word'zie i wykorzystać jedną z poniższych technik.

Wariant 1. Aby stworzyć losowy tekst 'Lorem ipsum' sięgamy po polecenie:

=Lorem()
które należy wpisać w pierwszym linii dokumentu i wcisnąć enter. W wyniku otrzymamy trzy paragrafy losowego tekstu.

Rozwiązanie to posiada możliwość parametryzowania:

=Lorem(10) 
utworzy 10 paragrafów z losową ilością zdań

=Lorem(10,3)
utworzy 10 paragrafów i po trzy zdania w każdym paragrafie.

Wariant 2. Jeżeli tekst, z których chcemy pracować ma być w języku polskim sięgamy wtedy po polecenie:

=Rand()
które działa podobnie jak prezentowane powyżej polecenie Lorem. Dostępne więc będą wiarianty:

=Rand(10)
=Rand(10,3)

które odpowiednio definiują ilość paragrafów i zdań w każdym paragrafie.

Technika ta przyda się w jednym z kolejnych wpisów, który wkrótce pojawi się w sekcji Strefa Wiedzy. Strefa Wiedzy prowadzona jest jako rozszerzenie dla kursów VBA organizowanych przez naszą firmę w Krakowie, Warszawie i Wrocławiu.

środa, 16 października 2013

Metoda Find w aplikacji MS Word- Przykład 1

Metoda .Find to jedno z bardziej przydatnych narzędzi nie tylko w Wordzie, ale także w Excelu (o czym wkrótce napiszę kilka postów). Ogólna zasada działania metody jest prosta- Znajdź >> Zaznacz. O wiele częściej sięgamy po to rozwiązanie w konfiguracji Znajdź >> Zamień. Tak czy inaczej w pracy z metodą .Find warto zawsze:

a) skorzystać z rejestratora makr
b) wykorzystać umiejętnie znaki specjalne (Wildcards) dostępne w pracy z tą metodą.

Od czasu do czasu przedstawię proste przykłady pracy z techniką Znajdź >> Zamień. Na początek następujące zadanie:

Dokument składa się z szeregu 12-to cyfrowych liczb umieszczonych w kolejnych paragrafach. Zadanie polega na wstawieniu pojedynczych cudzysłowów na początku i końcu każdego ciągu oraz na umieszczeniu wszystkich wpisów w jednej linii. Poniższy zrzut ekran pokazuje sytuację przed i po. Nieprzypadkowo na zrzucie ekranu dostrzec można również znaki podziału paragrafu, które będą kluczowe w rozwiązaniu problemu.


Podejście A. Teoretycznie możemy wykorzystać pętle i przeszukać cały dokument na okoliczność ciągów znaku uprzednio częściowo dokonawszy zamiany znaku paragrafu na przecinek. Rozwiązanie choć skuteczne to nieefektywne  i nie będzie tu prezentowane.

Podejście B. Wykorzystując dwukrotnie metodę .Find >> .Replace osiągniemy nasz cel. Proszę zwrócić uwagę na parametr .Wildcards ustawiony odmiennie w pierwszej i drugiej części kodu.


Podejście C. Kluczowe w tym wariancie jest zastąpienie symbolu znaku paragrafu. W miejsce '^p' wstawiamy inny odpowiednik nowego paragrafu wyrażony jako '^13', który nie powoduje kolizji z ustawionym naprzemiennie parametrem .Wildcards w powyższym przykładzie. Dzięki temu nasze ostateczne rozwiązanie staje się jeszcze bardziej kompaktowe.

Prowadząc szkolenie z VBA zawsze staram się prezentować rozwiązania optymalne, choć często wychodzę od rozwiązań naturalnych-intuicyjnych by następnie móc zaprezentować ewolucję rozwiązania podobnie jak ma to miejsce powyżej.

czwartek, 10 października 2013

Podręczna paleta kolorów wypełnienia

Wyobraźmy sobie sytuację, w której chcemy ułatwić użytkownikowi nanoszenie kolorów w określonym obszarze arkusza. Odpowiednie kolory mogą oznaczać status danego produktu, zamówienia, tworzyć kolorystyczną informacje o postępie zdarzenia, itp. Ot np. spójrzmy na poniższy układ prostej tabeli wraz z wyświetlonym szybkim menu podręcznym przyspieszającym kolorowanie.


Poniżej w kilku krokach prezentuję technikę tworzenia niniejszego rozwiązania.

Krok 1. Procedura tworząca i wyświetlająca menu podręczne z paletą kolorystyczną. Procedura ta wykorzystuje tradycyjne techniki tworzenia menu oparte o obiekty/kolekcje CommandBar/CommandBars.

Krok 2. Jak łatwo zauważyć w powyższej procedurze tworzone menu jest tymczasowe. Niezbędna jest więc procedura usuwania paska zaraz po tym gdy zostanie wykorzystany.

Krok 3. Wykorzystamy obsługę zdarzenia BeforeRightClick w celu wyświetlenia menu tylko w kolumnie C odpowiedniego arkusza. Co ważne, poniższe rozwiązanie wyświetli nasze nowe menu po kliknięciu prawym klawiszem myszy. Nadal pozostawimy użytkownikowi dostęp do standardowego menu podręcznego o ile wybierze na klawiaturze konfigurację Shift + F10.

I na koniec mała wskazówka- wykorzystana w kroku 1 metoda .ShowPopup posiada dodatkowe, opcjonalne parametry, które umożliwiają wskazanie miejsca, w którym ma zostać wyświetlone nasze menu podręczne.
Tworzenie interakcji i rozwiązań ułatwiających pracę z Excelem (i innymi aplikacjami Office) jest ważną umiejętnością każdego programisty co staram się często podkreślać prowadząc kurs z programowania w VBA.

piątek, 4 października 2013

Przechowywanie wartości zmiennych po zamknięciu pliku

Szukając odpowiedni na pytanie jak przechować zmienne po zamknięciu pliku przychodzi mi do głowy szereg pomysłów. Na początek wymienię kilka z nich- wykorzystując komórkę w arkuszu (tylko Excel), korzystając z właściwości dokumentu, korzystając z kolekcji Variables (tylko Word), korzystając z rejestru komputera (tylko lokalnie, nie przenoszone z plikiem).

Relatywnie ciekawą techniką jest jednak wykorzystanie instrukcji Put (dla zapisu) i Get (dla odczytu) z zapisem do pliku tekstowego. Rozwiązanie to mam kilka dodatkowych zalet:
1.  można szybko i skutecznie przechować wartości tablic, co nie jest możliwe w rozwiązaniach alternatywnych,
2. utworzony plik tekstowy sprawia wrażenie zaszyfrowanego, nie każdy będzie w stanie odczytać jego zawartość.

Skoro są zalety to i zazwyczaj istnieją wady rozwiązania. Otóż i owszem- ograniczona wielkość zmiennej- dla tablicy będzie to niewiele ponad 3000 rekordów.

Jak działa taki mechanizm? Jak wygląda kod zapisu i odczytu?

Wyobraźmy sobie taką oto sytuację, że obszar tabeli Excela A1:E7 chcemy zapisać w postaci zmiennej tablicowej i przesłać w postaci pliku tekstowego do innej osoby.


W celu zapisu posłużymy się takim poniższym przykładowym kodem. Dodatkowe parametry zostały opisane w postaci komentarzy wewnątrz kodu.


Jeżeli operacja powiodła się w domyślnej lokalizacji znajdziemy plik o nazwie "Array.To.File.VBA.bin". Próba otwarcia tego pliku w prostych edytorach tekstu da nam następujący lub podobny efekt:




Plik jest niewielki i jednocześnie sprawia wrażenie jakoby zawartość była zaszyfrowana. Teraz możemy przesłać plik do odbiorcy.

W jaki sposób możemy odczytać nasz plik. Wystarczy uruchomić poniższą procedurę aby wartość naszej tabeli została wprowadzona do arkusza Excel począwszy od aktywnie zaznaczonej komórki. (Dodatkowe uwagi dot. kodu w postaci komentarzy poniżej)


Przydatne linki referencyjne do strony Microsoft MSDN:

Put Statement
Get Statement

piątek, 27 września 2013

Sprawdzanie właściwości przed jej ustawieniem

Niniejszy temat został wywołany na forum StackOverflow.Com gdzie padło pytanie o sens niniejszego kodu (który tutaj został lekko zmodyfikowany dla celów prezentacyjnych):


Na pierwszy rzut oka w istocie- jaki jest sens sprawdzać czy dany wiersz jest ukryty skoro chcemy i tak ostatecznie odkryć wszystkie dziesięć tysięcy wierszy. Wystarczy przecież wykonać poniższą pętlę:


Otóż pierwszy zapis jest bardzo uzasadniony i ma swoją wyraźną przewagę nad pętlą drugą (choć efekt ostatecznie będzie na 100% identyczny). Porównawczo wygląda to następująco (dla identycznych parametrów środowiska, w którym wykonany został test dla każdego wariantu):

1. pętla pierwsza, wszystkie wiersze były uprzednio odkryte- czas wykonania- 0.2 sek
2. pętla druga, wszystkie wiersze były uprzednio odkryte- czas wykonania- 13.1 sek

3. pętla pierwsza, wszystkie wiersze były uprzednio ukryte- czas wykonania- 15.6 sek


Z czego wynikają różnice. Zasadniczo z prostego założenia, zgodnie z którym odczyt właściwości odbywa się szybciej niż jej ustawienie. Jeżeli istnieje uzasadnienie, że większość elementów (tu: wierszy) może nie wymagać ustawiania właściwości (tu: odkrywania) to warto uprzednio sprawdzić bieżący stan danej właściwości (tu: czy wiersz jest ukryty czy odkryty). Zasadę tę warto stosować do wszystkich właściwości o ile pracujemy na relatywnie dużej kolekcji.

piątek, 20 września 2013

Kolejność ma znaczenie czyli o porządkach w kolekcji

Chyba nie każdy (szczególnie początkujący) programista zdaje sobie sprawę z faktu, że elementy każdej z kolekcji ułożone są w określonym porządku. Często porządek ten nie ma znaczenia dla zadania jakie realizujemy, w innych sytuacjach wiedza ta może wpłynąć na prawidłowość wykonania operacji lub szybkość jej wykonania. Poniżej kilka przykładów, informacji i ciekawostek dot. tego zagadnienia w odniesieniu do aplikacji Excel i Word, gdzie każdy z przykładów zostanie przedstawiony w zapisie pętli For Each.

1. Skoroszyty:


pętla wykonywać się będzie w kolejności w jakiej skoroszyty były otwarte lub utworzone.

2. Arkusze


pętla wykona się w kolejności od lewego do prawego arkusza. Nie ma znaczenia czy arkusz jest ukryty czy nie.

3. Komórki Cells

pętla zostanie wykonana w porządku w jakim piszemy i czytamy, od lewej do prawej, od górnego wiersza w dół zaznaczenia.

4. Komentarze w arkuszu Excel

o kolejności decyduje adres komórki, w której znajduje się komentarz, a następnie kolejność tej komórki w obszarze arkusza zgodnie z zasadami opisanymi dla komórek Cells w punkcie 3 powyżej.

5. Komentarze w dokumencie Word

pętla zostanie wykonana w kolejności występowania komentarzy w dokumencie, od początku dokumentu (od pierwszego komentarza) do końca (do ostatniego komentarza).

6. Zakładki w dokumencie Word. W tym przypadku dostępne są dwa warianty:

zakładki zostaną ułożone w kolejności ... alfabetycznej, wg nazw zakładek.


w tym przypadku zakładki zostaną ułożone w kolejności występowania w dokumencie.

 7. Kształty Shape w Excelu


o kolejności decyduje parametr ZOrderPosition, a więc ułożenie względem osi Z arkusza. Pierwszym kształtem będzie ten `najbliżej obszaru komórek`, ostatni to ten `najbliżej nas.`

Oczywiście każda z kolekcji posiada swoją własną kolejność. Powyższe wybrane przykłady mają na celu zwrócenie uwagi na ten aspekt. Dla innych obiektów warto przeprowadzić swoje własne próby.

poniedziałek, 16 września 2013

Obszar Range dwuwymiarowy do tablicy Array jednowymiarowej


Jedną z typowych operacji wykonywanych w VBA jest pobieranie danych z kolejnych komórek arkusza i wykonywanie określonych działań na pobranych wartościach. Proste działanie w którym najczęściej wykorzystujemy prostą pętlę For...Next.

Co jednak ważne, w sytuacji gdy operacja dotyczy wielu komórek o wiele bardziej efektywnym pozostaje przeniesienie wartości komórek do tablicy Array i przeprowadzenie dalszych działań na elementach tablicy. W sytuacji tej należy jednak pamiętać, że utworzona tablica pozostaje tablicą dwu-wymiarową.

Sytuacja ta ma miejsce także wtedy gdy pobieramy dane z pojedynczej kolumny lub pojedynczego wiersza arkusza. Spójrzmy na poniższy przykład.



 utworzenie poniższych tablic:

sprawi, że obie tablice będą dwuwymiarowe. Pobranie z nich wartości C będzie wymagało utworzenia następujących zapytań:


Istnieje jednak możliwość utworzenia z w/w tablic tablic jednowymiarowych. Aby to uczynić należy transponować tablice. Prześledźmy to w kolejnych krokach dla obu tablic jednocześnie:


W wyniku powyższego działania tablica rowArr pozostanie dwuwymiarowa podczas gdy colArr stanie się tablicą jednowymiarową. Aby więc pobrać wartość C z tablic wywołamy następujące instrukcje:


Jeżeli jednak tablicę rowArr transponujemy ponownie także i ona stanie się jednowymiarową:


wtorek, 10 września 2013

Jak ukryć makro po stronie aplikacji Excel i zachować jego publiczny charakter

Wyobraźmy sobie sytuację, w której szereg współpracujących procedur Sub znajduje się w naszym projekcie VBA. Makra rozdzielone zostały na szereg modułów. Zależy nam jednak na tym, aby makra były dostępne z poziomu każdej inne procedury, a jednocześnie zależy nam na tym, aby szereg z tych makr nie był dostępny i widoczny z poziomu Excela...

Powyższe tytułem wstępu, a teraz uporządkujmy możliwe rozwiązania:

Wariant A- raczej oczywisty. Nadanie procedurze parametru Private:

    Private Sub MojaProcedura()

sprawi, że procedura dostępna będzie z poziomu tylko modułu w którym jest makro oraz okna Immediate z przy zachowaniu pełnej referencji: Module1.MojaProcedura. Makro nie będzie dostępne z poziomu aplikacji Excel.

Wariant B oczywisty. Dodanie u góry modułu wpisu:
  
    Option Private Module

co sprawi, że wszystkie makra danego modułu nie są widoczne po stronie Excela i jednocześnie zachowują swój publiczny charakter w zakresie pracy z nimi z poziomu innych modułów i okna Immediate.

Wariant C nie zawsze oczywisty. Konstrukcja publicznej procedury z parametrem:

    Public MojaProceduraParametr(boParametr as Boolean)

czyni z tej procedury procedurę o podobnej charakterystyce jak w wariancie B- nie jest widoczna po stronie aplikacji Excel, ale jest w pełni dostępna z poziomu okna Immediate i innych modułów. Oczywiście wywołując ją należy podać parametr nie mniej nie mamy żadnego obowiązku, aby ten parametr wykorzystać w dalszej części procedury.

Szersze wykorzystanie wariantu C dla aplikacji MS Word, gdzie ze względu na specyficzną sytuację związaną z makrami typu AutoExec rozwiązanie C jest szczególnie przydatne, opisane zostało pod poniższym tematem w serwisie StackOverflow:

Hide AutoExec() and AutoNew() macros from the macro list yet still have them run?

czwartek, 5 września 2013

Wyrażenia regularne RegExp raz jeszcze

Postanowiłem raz jeszcze wrócić do zagadnienia związanego z wyrażeniami regularnymi. Tym razem poszerzę temat o dwa obszary- pobieranie określonego fragmentu n-tego elementu spełniającego kryterium wyszukiwania oraz włączenie RegExp do własnej funkcji użytkownika (UDF).

Spójrzmy na początek na poniższy przykładowy tekst:

    Questionnaire results from company web.
    Name: John Smith
    Phone: 1234567
    Name: Jan Kowalski
    Phone: 9876545321
    Name: Johan Schmitt
    Phone: 00112233

Z pomocą RegExp i VBA spróbujemy przygotować rozwiązanie, które umożliwi pobranie np. drugiego imienia  i nazwiska (Jan Kowalski) czy też trzeciego numeru telefonu (00112233) z naszego przykładowego tekstu.

Trudność pierwsza- musimy odnaleźć fragment tekstu, który zaczyna się od Name. W tym celu nasz wzorzec będzie miał postać:

W wyniku czego jesteśmy w stanie otrzymać kolekcję składającą się z elementów:

    Name: John Smith
    Name: Jan Kowalski 
    Name: Johan Schmitt 

Trudność druga- jak pobrać samo imię i nazwisko i pominąć początkowy fragment z wyników wyszukiwania? W tym celu będziemy musieli sięgnąć głębiej w metodę .Execute. Samo wywołanie tej metody tworzy kolekcję zawierającą wszystkie wystąpienia spełniające kryterium .Pattern. Istnieje jednak możliwość pobrania określonego fragmentu n-tego elementu sięgając do kolekcji .SubMatches. Elementami należącymi do tej kolekcji będą wszystkie fragmenty, które zostały ujęte w nawiasach w naszym wzorcu .Pattern.
Ogólna składnia metody .Execute wyglądałaby następująco:


Proponuję zebrać w całość nasz kod. Na początek testowa procedura wywołująca z dodatkowymi komentarzami wewnątrz kodu:


A teraz funkcja właściwa uwzględniająca przedstawione powyżej istotne elementy metody .Execute z dodatkowym komentarzem:

Po wywołaniu procedury Pobieranie_nTego_elementu() uzyskamy w oknie immediate dokładnie to czego szukaliśmy, a więc odpowiednio:

    Jan Kowalski
    00112233

piątek, 30 sierpnia 2013

Przeglądarka zdjęć w Excelu

W czasie dwóch prowadzonych szkoleń z VBA dla Excel w Krakowie i Wrocławiu padło pytanie o możliwość stworzenia czegoś na wzór przeglądarki zdjęć w Excelu w oparciu o nazwy plików graficznych umieszczonych w kolumnie arkusza.

Oczywiste pytanie, które również zadałem brzmiało- po co i dlaczego mielibyśmy tworzyć takie rozwiązanie? Jak się okazało, i tu będę bazował na podobnych elementach uzyskanych odpowiedzi, chodziło o szybką i skuteczną weryfikację jak wygląda dany produkt w bazie produktów danej firmy.

Poniżej przedstawiam wariant uproszczony przygotowany w trzech krokach. Wszelkie modyfikacje i rozszerzenia są jak najbardziej możliwe i zalecane.

Krok 1. Tworzymy listę plików graficznych. 
W tym celu w komórce A1 dowolnego arkusza proszę wprowadzić ścieżkę do katalogu, w którym znajdują się nasze pliki graficzne, np: c:\Users\Imię Naziwsko\Documents\Moje obrazy.

Następnie proszę wywołać poniższe makro, którego celem będzie utworzenie listy wszystkich plików graficznych JPG począwszy od rzędu 11 w kolumnie A.

Krok 2.  Wstawianie i usuwanie grafiki.
Poniższe dwa makra będą odpowiedzialne za wyświetlenie oraz usunięcie pliku graficznego z naszego arkusza. Dodatkowe informacje na temat ich działania zawarte są w komentarzach wewnątrz kodu.


Krok 3. Automatyczne działanie z pomocą obsługi zdarzenia.
Na koniec połączymy nasze makra ze zdarzeniem. Chcemy bowiem aby wyświetlił się nam obraz tylko wtedy, gdy zaznaczymy pojedynczą komórkę w kolumnie zawierającej nazwę pliku, a w każdym z pozostałych przypadków chcemy usunąć zbędną grafikę. W tym celu w module naszego arkusza dodamy następujący kod obsługi zdarzenia Worksheet_SelectionChange:


poniedziałek, 26 sierpnia 2013

Nawiasy mają znaczenie!- procedura parametryzowana

Myślę, że wielu programistów nie zdaje sobie sprawy z faktu, że wywołując zewnętrzną procedurę parametryzowaną Sub ma znaczenie to, czy parametry zostaną podane w nawiasie czy też nie. Chodzi o różnicę, którą obrazują następujące składnie wywołania:


Zanim wyjaśnię  różnicę i przyczynę różnicy najlepiej będzie jeżeli spojrzymy na przykład:

Wywołanie procedury Foo sprawi, że najpierw otrzymamy w wyniku 'Hello', a następnie uzyskamy wartość zmiennej 'Hello World". Skąd ta różnica? Otóż ujęcie argumentów procedury w nawias (wywołanie 1 powyżej) zmienia typ parametru z ByRef na ByVal, to zaś sprawia, że zmienna przekazana do podprocedury `Boo` nie zmienia swojej wartości. Przekazanie argumentu bez nawiasu sprawia, że parametr jest typu ByRef a więc wszystkie operacje na nim wykonane w podprocedurze zostaną również przekazane do procedury nadrzędnej.

Podobną różnicę w zachowaniu i wyniku wywołania podprocedury znajdziemy korzystając z instrukcji Call. W tym przypadku dodatkowy nawias będzie zmieniał tryb przekazania parametru. Obrazują to następujące przykłady:

poniedziałek, 19 sierpnia 2013

MS Word- podmiana akcji przycisku na wstążce

Aplikacja MS Word udostępnia ciekawą i praktyczną opcję, która umożliwia zamianę standardowej akcji wywołanej dowolnym przyciskiem znajdującym się na wstążce na akcję własną użytkownika. Proces zamiany jest relatywnie łatwy do wykonania i zaprogramowania choć od razu muszę zaznaczyć rzecz ważną- podmiana ta dotyczyć będzie wskazanego szablonu lub dokumentu. Nie zmienia ona akcji danego przycisku dla wszystkich dokumentów. Tak czy inaczej rozwiązanie to znajduje szereg praktycznych i przydatnych zastosowań. Żałuję też jednocześnie, że podobnych rozwiązań nie da się zrealizować we wszystkich aplikacjach MS Office.

Aby osiągnąć cel opisany w powyższym wprowadzeniu musimy wykonać kilka kroków i operacji. Całość zaprezentuję na bazie aktualizacji działania przycisku Wersja Robocza w zakładce Widok aplikacji MS Word 2010 (przycisk zaznaczony na poniższym zrzucie ekranu).



Krok 1. Otwieramy dokument, w którym chcemy zapisać zindywidualizowane zachowanie przycisku.

Krok 2. Przechodzimy do zakładki i wywołujemy polecenie: Widok >> Makra >> Wyświetl makra.

Krok 3. W wyświetlonym oknie Makra, w jego środkowej części z listy rozwijalnej Makra w: wybieramy opcję: Polecenia programu Word.

Krok 4. Chyba najtrudniejszy z etapów- na otrzymanej liście makr musimy odnaleźć i zaznaczyć to makro, które (wydaje się nam) powiązane jest z danym przyciskiem. Czym się kierować w poszukiwaniach- intuicją i logiką opartą o nazwy zakładek, nazwę grupy poleceń czy wreszcie nazwę naszego przycisku, który modyfikujemy.

W naszym przykładzie od razu trafimy na pewną trudność- otóż przycisk Wersja robocza nie będzie reprezentowany przez makro ViewDraft  lecz makro ViewNormal. Aby się przekonać czy dokonaliśmy właściwego wyboru możliwe, że będziemy musieli wykonać i powtórzyć kilka kolejnych kroków.

Proszę pamiętać, aby zaznaczyć wybrane makro i nie zmieniać tego zaznaczenia!

Krok 5. W naszym oknie Makra na liście Makra w: dokonujemy ponownej zmiany- tym razem wskazujemy tam plik Worda, z którym chcemy powiązać indywidualne ustawienia przycisku.

Wskazówka! jeżeli w tym kroku zamiast pliku wskażemy odpowiedni  szablon, np. Normal.Dotm, to nasza zmiana obsługi przycisku powiązana zostanie z tym szablonem a w konsekwencji ze wszystkimi dokumentami, które na bazie szablonu powstaną.

Krok 6. Klikamy w przycisk Utwórz w wyniku czego zostaniemy przeniesieni do edytora VBA w obszar utworzonego makra (proszę zwrócić uwagę na lokalizację kodu- znajduje się on w module w naszym dokumencie). W tym konkretnym przypadku makro powinno mieć następującą postać:

Krok 7. W tym momencie każda modyfikacja, która zostanie dokonana w otrzymanym makrze zostanie jednocześnie powiązana z przyciskiem 'Wersja robocza'. Przekonajmy się o tym dodając do naszego kodu proste polecenie:


A następnie proszę przejść do aplikacji MS Word i wcisnąć przycisk, którego kod poddaliśmy modyfikacji.

Krok 8. Proszę pamiętać o zapisaniu naszego pliku w wariancie z obsługą makr, a więc rozszerzeniem DOCM.



poniedziałek, 12 sierpnia 2013

Zapisanie zakresu komórek arkusza w postaci pliku JPG

Niniejsze rozwiązanie znane jest wielu praktykom VBA, szczególnie tym osobom, które wysyłają  fragmenty arkusza w postaci grafiki wstawionej w wiadomości e-mail.

Jak wyglądać będzie makro, którego zadaniem będzie zapisanie utworzenie pliku graficznego JPG prezentującego fragment obszaru arkusza? Pełne rozwiązanie poniżej. W tym wypadku wszystkie dodatkowe komentarze zostały umieszczone poniżej.


I jeszcze dwa obrazy graficzne na koniec- zrzut ekranu z naszą tabelą, która została poddana procesowi exportu oraz plik, który powstał w wyniku działania powyższego makra.









poniedziałek, 5 sierpnia 2013

Testowanie zgodności ciągów tekstowych- wyrażenia regularne RegExp- 4/4

W ostatnim wpisie dot. wyrażeń regularnych RegExp chciałbym zwrócić uwagę metodę .Execute, którą wykorzystałem poprzednio w celu odnalezienia i pobrania adresu e-mail z podanego ciągu tekstowego.

Metoda .Execute zwraca kolekcję Matches Collection, która zawiera wszystkie wystąpienia ciągów tekstowych spełniających określone kryteria.  W tej sytuacji w relatywnie łatwy sposób możemy pobrać elementy odpowiadające naszemu wzorcowi.

Prześledźmy to na jednym tylko przykładzie, którego zadaniem będzie pobranie wszystkich adresów e-mail z podanego ciągu tekstowego. W zakresie dalszego zrozumienia zastosowania kolekcji Matches odsyłam do komentarzy w poniższym kodzie.


środa, 31 lipca 2013

Testowanie zgodności ciągów tekstowych- wyrażenia regularne RegExp- 3/4

Zanim uruchomimy pierwszą pełną procedurę z wykorzystaniem techniki wyrażeń regularnych (RegExp) przyjrzyjmy się dodatkowo właściwościom obiektu RegExp

.Pattern       pozwoli nam zdefiniować wzorzec porównawczy,
.Global        określa, czy wzorzec dotyczy pierwszego (= False), 
               czy wszystkich wystąpień w tekście (= True),
.IgnoreCase    określa sposób testowania wyrażenia ze względu na wielkość liter (domyślnie =False,
               wyszukiwanie binarne uwzględniające wielkość liter),

oraz metodom obiektu Regexp:
.Test       zwraca informację czy można dopasować wyrażenie do ciągu tekstowego,
.Replace    dokonuje zamiany fragmentu wzorcowego na nowy wskazany ,
.Execute    wykonuje operację zwracając obiekt Match z kolekcji MatchCollection.

W dużym skrócie podsumowując powyższe elementy obiektu  RegExp określę to w sposób następujący- pozwalają one na wskazanie wzorca a następnie dokonanie zamiany fragmentu tekstu na inny lub pobranie poszczególnych elementów w całości lub dla pierwszego wystąpienia.

Czas na zebranie całości zagadnienia w konkretne rozwiązania i prezentację przykładowych pełnych konstrukcji kodu VBA. Wykonując poniższe operacje w środowisku VBA proszę nie zapomnieć o Kroku 1, a więc ustanowieniu referencji do biblioteki RegExp.

Przykład 1. zamiana wyrazów w ciągu tekstowy, wyraz 'jest' na 'był'


Przykład 2. z podanego ciągu tekstowego pozostawić tylko cyfry
 

Przykład 3. z podanego ciągu tekstowego usunąć wszystkie liczby


Przykład 4. W przykładzie nr 2 dodatkowo pozostały podwójne spacje oraz zbędna spacja przed kropką i wykrzyknikiem. To wszystko także chcemy usunąć i wyprostować. W tej sytuacji utworzymy kaskadowy mechanizm RegExp:

Przykład 5. W powyższym przykładzie zastosowana została technika zamiany kolejności wyrazów. Jest to jedna z wielu dodatkowych korzyści pracy z RegExp. Poniżej prosty przykład, który zamienia kolejność 3 pierwszych wyrazów podanego tekstu:

Przykład 6. Z podanego tekstu chcemy pobrać tylko adres e-mai. W tym celu wykorzystamy jeden z wielu możliwych wzorców odpowiadających adresowi e-mail i stworzymy następujący kod:

Powyższy przykład zamiast metody .Replace wykorzystuje metodę .Execute. Ale o tym napiszę już w kolejnym wpisie za kilka dni...


Osobom, które są zainteresowane wyrażeniami regularnymi polecam poniższe linki, które pozwolą na poszerzenie i ugruntowanie informacji przedstawionych w ostatnich postach. Linki te poświęcone są ogólni RegExp i niekoniecznie wskazują na bezpośrednie wykorzystanie tej techniki w VBA.

Korzystanie z wyrażeń regularnych w programie Microsoft Visual Basic 6.0Regular-Expressions.Info
.NET Framework Regular Expressions
RegExp Help
Regular-Expressions.info
Introduction to Regular Expressions

poniedziałek, 22 lipca 2013

Testowanie zgodności ciągów tekstowych- wyrażenia regularne RegExp- 2/4

Kontynuując temat tworzenia wyrażeń regularnych RegExp omówię najważniejszą umiejętność związaną z tą techniką, a więc budowanie wzorców porównawczych.
 

Krok 2.
To najtrudniejsza część pracy z RegExp- budowa składni tworzącej kryteria. Spojrzymy na to zadanie najpierw przez pryzmat wybranych znaków (kodów) specjalnych, następnie przedstawię proste przykłady, a na koniec zaprezentuję kilka złożonych wzorców.

A) Znaki i symbole specjalne
\d    cyfra
\D    nie cyfra
\s    spacja
\S    nie spacja
\w    znak alfanumeryczny, litera
\W    nie znak alfanumeryczny
\b    granica słowa, pozycja między słowem a spacją
\n    znak nowej linii (vbCrLf, Chr(10), Chr(13), vbNewLine)
\r    znak nowej linii (Chr(13))
\t    znak tabulacji (vbTab)
.     (kropka) dowolny znak z wyjątkiem znaku nowej linii
*     powtórzenie symbolu dowolną ilość lub zero razy
?     powtórzenie symbolu zero lub jeden raz
+     powtórzenie poprzedniego wyrażenia co najmniej raz
\     używany jako znak wyjścia dla znaków specjalnych
^     na początku tekstu
      także symbol zaprzeczenia(patrz poniżej)
$     na końcu tekstu
[]    przedział znaków
      [A-Z]  dowolna wielka litera
      [a-z]  dowolna mała litera
      [0-9]  dowolna cyfra
      [^0-9] nie zawiera cyfry
{}    wskazuje ilość dopuszczalnych powtórzeń: 
      a{1}   jednokrotne powtórzenie litery a
      a{0,2} zero do dwóch powtórzeń litery a
()    grupowanie
|     alternatywa dla grupowania

Proszę jednocześnie pamiętać, że dla prostych wyrażeń bez udziału powtórzeń i znaków specjalnych zastosujemy technikę podobną do tej opisanej przy pracy z operatorem Like w jednym z wcześniejszych postów. RegExp jest także, co do zasady, wrażliwy na wielkość liter

Spójrzmy na przykłady:
RegExp Pattern   Dopasowanie    Wyjaśnienie (opcjonalne)
           (czerwonym-dopasowanie)
abc              abcabcabc
234              1234567
Jan              Jan
                 Janek
                 janek          wielkość liter ma znaczenie
ab*c             acde
                 abcde
                 abbbbcde
ab?c             acde
                 abcde
                 abbbcde        brak dopasowania
ab+c             acde           brak dopasowania
                 abcde
                 abbbcde
ab.d             abcd
                 ab1d
                 abc1d          brak dopasowania 
a.*d             ad
                 abcd
                 asfas13wbd
\d\d             10
                 99
                 100
                 9999           dwa podwójne układy cyfr
\w+              alfa           dowolny, każdy wyraz
                 Witaj Jan! 
\w+@\w+          a@a            uproszczony schemat dla e-maila
                 email@domena.pl
\w+@\w+.\w+      email@domena.pl
                 jan.kowalski@domena.pl
^ab.*c           abc
                 ab123fdcdef
                 babc           tekst nie zaczyna się od a
^ab.*c$          abc
                 ab123c
                 ab123cd        nie kończy się na c
                 ab123cdc       powtórzenie c nie ma znaczenia
                 bacdc          nie zaczyna się od a
^\d              1abc           zaczyna się od cyfry
\d$              abc1           kończy się cyfrą
[abc]            abc            znaki wskazane w nawiasie
                 abcdef
                 abcdefabc   
[^abc]           abcdefabc      nie a, nie b, nie c     

[A-Z]            abcDEF         wielkie litery
                 ABCDEF
                 abcdef
^[A-Z].+         Abc123d        zaczyna się od wielkiej litery
                 abc123d
[+*?.]           ..??**++       nawiasy likwidują znaczenie znaków specjalnych
^[1-9][0-9]*$    1              liczba całkowita większa od zera
                 1000
                 -1000
                 123.45         tylko liczba całkowita
(ab)+            ab
                 ababab
                 bababa
(ab|ba)+         ab
                 ba
                 ababab         rozpozna jako jeden ciąg
                 bacbacab       rozpozna trzy ciągi
                 abcdef
(abc)?           abc            ale także dla pustego ciągu
a(\d+)c          a123c          zawiera cyfry pomiędzy a i c
                 a1c
                 ac
                 abc  
a{2}             alfaabeta
a{1,2}           alfaabeta

Inne gotowe przykłady z tego źródła:
A) weryfikacja, czy podano prawidłowy e-mail:
^[A-Za-z0-9_\.-]+@[A-Za-z0-9_\.-]+[A-Za-z0-9_][A-Za-z0-9_]$

Wskazówka! w sieci znaleźć można wiele przykładów weryfikacji adresów e-mail, kolejny z nich podam w trzeciej lekcji poświęconej RegExp. Zainteresowanych tym zagadnieniem odsyłam do tej dyskusji.

B) weryfikacja zapisu daty w układzie: yyyy-MMM -dd, gdzie MMM to trzyliterowy skrót zapisany z wielkiej litery:
^\d\d\d\d-[A-Z][a-z][a-z]-\d\d$

C)  weryfikacja daty jak powyżej rozszerzona, sprawdzamy, czy miesiąc podany w języku polskim lub angielskim:
^20\d\d-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(0[1-9]|[1-2][0-9]|3[01])$ 

^20\d\d-(Sty|Lut|Mar|Kwi|Maj|Cze|Lip|Sie|Wrz|Paź|Lis|Gru)-(0[1-9]|[1-2][0-9]|3[01])$